Repository: Studio3T/robomongo Branch: master Commit: 4ad11c6686fb Files: 1999 Total size: 32.9 MB Directory structure: gitextract_2e5kkx9s/ ├── .gitattributes ├── .gitignore ├── .travis.yml ├── CHANGELOG ├── CMakeLists.txt ├── COPYRIGHT ├── DESCRIPTION ├── LICENSE ├── README.md ├── bin/ │ ├── README.md │ ├── build │ ├── build-and-run-tests │ ├── build-and-run-tests.bat │ ├── build.bat │ ├── clang-checks.txt │ ├── clang-tidy.xml │ ├── clean │ ├── clean.bat │ ├── common/ │ │ ├── setup │ │ └── setup.bat │ ├── configure │ ├── configure.bat │ ├── enable-visual-studio-clang-tidy.py │ ├── install │ ├── install-debug-dlls.bat │ ├── install.bat │ ├── pack │ ├── pack.bat │ ├── rebuild.bat │ ├── run │ ├── run-clang-tidy │ ├── run-clang-tidy.py │ ├── run-cppcheck.bat │ ├── run-scan-build │ ├── run-tests │ ├── run-tests.bat │ ├── run-vs-code-analysis.bat │ ├── set-mongo-warning-level-3.py │ └── tag ├── cmake/ │ ├── FindMongoDB.cmake │ ├── FindOpenSSL.cmake │ ├── FindThreading.cmake │ ├── RobomongoCMakeDefaults.cmake │ ├── RobomongoCommon.cmake │ ├── RobomongoConfigurationSummary.cmake │ ├── RobomongoDefaults.cmake │ ├── RobomongoInstall.cmake │ ├── RobomongoInstallQt.cmake │ ├── RobomongoPackage.cmake │ ├── RobomongoPrintUtils.cmake │ ├── RobomongoTargetArch.cmake │ ├── RobomongoTrashSymbols.cmake │ ├── mongodb/ │ │ ├── README.md │ │ ├── linux-debug.objects │ │ ├── linux-release.objects │ │ ├── macosx-debug.objects │ │ ├── macosx-release.objects │ │ ├── windows-debug.objects │ │ └── windows-release.objects │ └── readme.md ├── docs/ │ ├── BuildRobo3TOnMacAndLinux.md │ ├── BuildRobo3TOnWindows.md │ ├── BuildRobo3TShell.md │ ├── BuildingMongoDB.md │ ├── BuildingRobomongo.md │ ├── BuildingRobomongoOnWindows.md │ └── Debug.md ├── install/ │ ├── linux/ │ │ └── robomongo.sh │ ├── macosx/ │ │ ├── DMG_DS_Store │ │ ├── Info.plist.in │ │ ├── README.md │ │ ├── qt.conf │ │ └── robomongo.icns │ ├── qt.conf.in │ └── windows/ │ ├── README.md │ └── winres.rc.in ├── shortcuts.txt ├── src/ │ ├── robomongo/ │ │ ├── .clang-format │ │ ├── CMakeLists.txt │ │ ├── app/ │ │ │ ├── main.cpp │ │ │ ├── main_mongo.cpp │ │ │ └── main_test.cpp │ │ ├── core/ │ │ │ ├── AppRegistry.cpp │ │ │ ├── AppRegistry.h │ │ │ ├── Core.h │ │ │ ├── Enums.cpp │ │ │ ├── Enums.h │ │ │ ├── Event.cpp │ │ │ ├── Event.h │ │ │ ├── EventBus.cpp │ │ │ ├── EventBus.h │ │ │ ├── EventBusDispatcher.cpp │ │ │ ├── EventBusDispatcher.h │ │ │ ├── EventBusSubscriber.cpp │ │ │ ├── EventBusSubscriber.h │ │ │ ├── EventError.cpp │ │ │ ├── EventError.h │ │ │ ├── EventWrapper.cpp │ │ │ ├── EventWrapper.h │ │ │ ├── HexUtils.cpp │ │ │ ├── HexUtils.h │ │ │ ├── HexUtils_test.cpp │ │ │ ├── KeyboardManager.cpp │ │ │ ├── KeyboardManager.h │ │ │ ├── domain/ │ │ │ │ ├── App.cpp │ │ │ │ ├── App.h │ │ │ │ ├── CursorPosition.cpp │ │ │ │ ├── CursorPosition.h │ │ │ │ ├── MongoAggregateInfo.h │ │ │ │ ├── MongoCollection.cpp │ │ │ │ ├── MongoCollection.h │ │ │ │ ├── MongoCollectionInfo.cpp │ │ │ │ ├── MongoCollectionInfo.h │ │ │ │ ├── MongoDatabase.cpp │ │ │ │ ├── MongoDatabase.h │ │ │ │ ├── MongoDocument.cpp │ │ │ │ ├── MongoDocument.h │ │ │ │ ├── MongoFunction.cpp │ │ │ │ ├── MongoFunction.h │ │ │ │ ├── MongoNamespace.cpp │ │ │ │ ├── MongoNamespace.h │ │ │ │ ├── MongoQueryInfo.cpp │ │ │ │ ├── MongoQueryInfo.h │ │ │ │ ├── MongoServer.cpp │ │ │ │ ├── MongoServer.h │ │ │ │ ├── MongoShell.cpp │ │ │ │ ├── MongoShell.h │ │ │ │ ├── MongoShellResult.h │ │ │ │ ├── MongoUser.cpp │ │ │ │ ├── MongoUser.h │ │ │ │ ├── MongoUtils.cpp │ │ │ │ ├── MongoUtils.h │ │ │ │ ├── Notifier.cpp │ │ │ │ ├── Notifier.h │ │ │ │ ├── ScriptInfo.cpp │ │ │ │ └── ScriptInfo.h │ │ │ ├── engine/ │ │ │ │ ├── ScriptEngine.cpp │ │ │ │ └── ScriptEngine.h │ │ │ ├── events/ │ │ │ │ ├── MongoEvents.cpp │ │ │ │ ├── MongoEvents.h │ │ │ │ ├── MongoEventsInfo.cpp │ │ │ │ └── MongoEventsInfo.h │ │ │ ├── mongodb/ │ │ │ │ ├── MongoClient.cpp │ │ │ │ ├── MongoClient.h │ │ │ │ ├── MongoWorker.cpp │ │ │ │ ├── MongoWorker.h │ │ │ │ ├── ReplicaSet.cpp │ │ │ │ ├── ReplicaSet.h │ │ │ │ ├── SshTunnelWorker.cpp │ │ │ │ └── SshTunnelWorker.h │ │ │ ├── settings/ │ │ │ │ ├── ConnectionSettings.cpp │ │ │ │ ├── ConnectionSettings.h │ │ │ │ ├── CredentialSettings.cpp │ │ │ │ ├── CredentialSettings.h │ │ │ │ ├── ReplicaSetSettings.cpp │ │ │ │ ├── ReplicaSetSettings.h │ │ │ │ ├── SettingsManager.cpp │ │ │ │ ├── SettingsManager.h │ │ │ │ ├── SshSettings.cpp │ │ │ │ ├── SshSettings.h │ │ │ │ ├── SslSettings.cpp │ │ │ │ └── SslSettings.h │ │ │ └── utils/ │ │ │ ├── BsonUtils.cpp │ │ │ ├── BsonUtils.h │ │ │ ├── Logger.cpp │ │ │ ├── Logger.h │ │ │ ├── QtUtils.cpp │ │ │ ├── QtUtils.h │ │ │ ├── SingletonPattern.hpp │ │ │ ├── StdUtils.cpp │ │ │ └── StdUtils.h │ │ ├── gui/ │ │ │ ├── AppStyle.cpp │ │ │ ├── AppStyle.h │ │ │ ├── GuiRegistry.cpp │ │ │ ├── GuiRegistry.h │ │ │ ├── MainWindow.cpp │ │ │ ├── MainWindow.h │ │ │ ├── dialogs/ │ │ │ │ ├── AboutDialog.cpp │ │ │ │ ├── AboutDialog.h │ │ │ │ ├── ChangeShellTimeoutDialog.cpp │ │ │ │ ├── ChangeShellTimeoutDialog.h │ │ │ │ ├── ConnectionAdvancedTab.cpp │ │ │ │ ├── ConnectionAdvancedTab.h │ │ │ │ ├── ConnectionAuthTab.cpp │ │ │ │ ├── ConnectionAuthTab.h │ │ │ │ ├── ConnectionBasicTab.cpp │ │ │ │ ├── ConnectionBasicTab.h │ │ │ │ ├── ConnectionDiagnosticDialog.cpp │ │ │ │ ├── ConnectionDiagnosticDialog.h │ │ │ │ ├── ConnectionDialog.cpp │ │ │ │ ├── ConnectionDialog.h │ │ │ │ ├── ConnectionsDialog.cpp │ │ │ │ ├── ConnectionsDialog.h │ │ │ │ ├── CopyCollectionDialog.cpp │ │ │ │ ├── CopyCollectionDialog.h │ │ │ │ ├── CreateCollectionDialog.cpp │ │ │ │ ├── CreateCollectionDialog.h │ │ │ │ ├── CreateDatabaseDialog.cpp │ │ │ │ ├── CreateDatabaseDialog.h │ │ │ │ ├── CreateUserDialog.cpp │ │ │ │ ├── CreateUserDialog.h │ │ │ │ ├── DocumentTextEditor.cpp │ │ │ │ ├── DocumentTextEditor.h │ │ │ │ ├── EulaDialog.cpp │ │ │ │ ├── EulaDialog.h │ │ │ │ ├── ExportDialog.cpp │ │ │ │ ├── ExportDialog.h │ │ │ │ ├── FunctionTextEditor.cpp │ │ │ │ ├── FunctionTextEditor.h │ │ │ │ ├── PreferencesDialog.cpp │ │ │ │ ├── PreferencesDialog.h │ │ │ │ ├── SSHTunnelTab.cpp │ │ │ │ ├── SSHTunnelTab.h │ │ │ │ ├── SSLTab.cpp │ │ │ │ └── SSLTab.h │ │ │ ├── editors/ │ │ │ │ ├── FindFrame.cpp │ │ │ │ ├── FindFrame.h │ │ │ │ ├── JSLexer.cpp │ │ │ │ ├── JSLexer.h │ │ │ │ ├── PlainJavaScriptEditor.cpp │ │ │ │ └── PlainJavaScriptEditor.h │ │ │ ├── resources/ │ │ │ │ ├── gui.qrc │ │ │ │ ├── icons/ │ │ │ │ │ ├── new_app_icon/ │ │ │ │ │ │ └── Robomongo App Icon.idraw │ │ │ │ │ └── psd/ │ │ │ │ │ └── BsonNull_16x16.psd │ │ │ │ └── scripts/ │ │ │ │ ├── esprima.js │ │ │ │ └── uuidhelpers.js │ │ │ ├── utils/ │ │ │ │ ├── ComboBoxUtils.cpp │ │ │ │ ├── ComboBoxUtils.h │ │ │ │ ├── DialogUtils.cpp │ │ │ │ ├── DialogUtils.h │ │ │ │ └── GuiConstants.h │ │ │ └── widgets/ │ │ │ ├── LogWidget.cpp │ │ │ ├── LogWidget.h │ │ │ ├── explorer/ │ │ │ │ ├── AddEditIndexDialog.cpp │ │ │ │ ├── AddEditIndexDialog.h │ │ │ │ ├── ExplorerCollectionIndexItem.cpp │ │ │ │ ├── ExplorerCollectionIndexItem.h │ │ │ │ ├── ExplorerCollectionIndexesDir.cpp │ │ │ │ ├── ExplorerCollectionIndexesDir.h │ │ │ │ ├── ExplorerCollectionTreeItem.cpp │ │ │ │ ├── ExplorerCollectionTreeItem.h │ │ │ │ ├── ExplorerDatabaseCategoryTreeItem.cpp │ │ │ │ ├── ExplorerDatabaseCategoryTreeItem.h │ │ │ │ ├── ExplorerDatabaseTreeItem.cpp │ │ │ │ ├── ExplorerDatabaseTreeItem.h │ │ │ │ ├── ExplorerFunctionTreeItem.cpp │ │ │ │ ├── ExplorerFunctionTreeItem.h │ │ │ │ ├── ExplorerReplicaSetFolderItem.cpp │ │ │ │ ├── ExplorerReplicaSetFolderItem.h │ │ │ │ ├── ExplorerReplicaSetTreeItem.cpp │ │ │ │ ├── ExplorerReplicaSetTreeItem.h │ │ │ │ ├── ExplorerServerTreeItem.cpp │ │ │ │ ├── ExplorerServerTreeItem.h │ │ │ │ ├── ExplorerTreeItem.cpp │ │ │ │ ├── ExplorerTreeItem.h │ │ │ │ ├── ExplorerTreeWidget.cpp │ │ │ │ ├── ExplorerTreeWidget.h │ │ │ │ ├── ExplorerUserTreeItem.cpp │ │ │ │ ├── ExplorerUserTreeItem.h │ │ │ │ ├── ExplorerWidget.cpp │ │ │ │ └── ExplorerWidget.h │ │ │ └── workarea/ │ │ │ ├── BsonTableModel.cpp │ │ │ ├── BsonTableModel.h │ │ │ ├── BsonTableView.cpp │ │ │ ├── BsonTableView.h │ │ │ ├── BsonTreeItem.cpp │ │ │ ├── BsonTreeItem.h │ │ │ ├── BsonTreeModel.cpp │ │ │ ├── BsonTreeModel.h │ │ │ ├── BsonTreeView.cpp │ │ │ ├── BsonTreeView.h │ │ │ ├── CollectionStatsTreeItem.cpp │ │ │ ├── CollectionStatsTreeItem.h │ │ │ ├── CollectionStatsTreeWidget.cpp │ │ │ ├── CollectionStatsTreeWidget.h │ │ │ ├── IndicatorLabel.cpp │ │ │ ├── IndicatorLabel.h │ │ │ ├── JsonPrepareThread.cpp │ │ │ ├── JsonPrepareThread.h │ │ │ ├── OutputItemContentWidget.cpp │ │ │ ├── OutputItemContentWidget.h │ │ │ ├── OutputItemHeaderWidget.cpp │ │ │ ├── OutputItemHeaderWidget.h │ │ │ ├── OutputWidget.cpp │ │ │ ├── OutputWidget.h │ │ │ ├── PagingWidget.cpp │ │ │ ├── PagingWidget.h │ │ │ ├── ProgressBarPopup.cpp │ │ │ ├── ProgressBarPopup.h │ │ │ ├── QueryWidget.cpp │ │ │ ├── QueryWidget.h │ │ │ ├── ScriptWidget.cpp │ │ │ ├── ScriptWidget.h │ │ │ ├── WelcomeTab.cpp │ │ │ ├── WelcomeTab.h │ │ │ ├── WorkAreaTabBar.cpp │ │ │ ├── WorkAreaTabBar.h │ │ │ ├── WorkAreaTabWidget.cpp │ │ │ └── WorkAreaTabWidget.h │ │ ├── resources/ │ │ │ ├── gnu_gpl3_license.html │ │ │ └── robo.qrc │ │ ├── shell/ │ │ │ ├── bson/ │ │ │ │ ├── json.cpp │ │ │ │ └── json.h │ │ │ ├── db/ │ │ │ │ ├── ptimeutil.cpp │ │ │ │ └── ptimeutil.h │ │ │ └── shell/ │ │ │ └── dbshell.cpp │ │ ├── ssh/ │ │ │ ├── CMakeLists.txt │ │ │ ├── README.md │ │ │ ├── array.c │ │ │ ├── libssh2_config.h.in │ │ │ ├── log.c │ │ │ ├── private.h │ │ │ ├── server.c │ │ │ ├── ssh.c │ │ │ ├── ssh.h │ │ │ ├── temp/ │ │ │ │ ├── direct_tcpip.c │ │ │ │ ├── internal.h │ │ │ │ ├── temp_ssh.c │ │ │ │ ├── temp_ssh.h │ │ │ │ └── temp_ssh_log.c │ │ │ ├── test.c │ │ │ ├── unix.h │ │ │ ├── valgrind/ │ │ │ │ └── macosx-clang.supp │ │ │ ├── win.c │ │ │ └── win.h │ │ └── utils/ │ │ ├── RoboCrypt.cpp │ │ ├── RoboCrypt.h │ │ ├── RoboCrypt_test.cpp │ │ ├── SimpleCrypt.cpp │ │ ├── SimpleCrypt.h │ │ ├── StringOperations.cpp │ │ ├── StringOperations.h │ │ ├── StringOperations_test.cpp │ │ ├── common.cpp │ │ ├── common.h │ │ └── qzip/ │ │ ├── qconfig_p.h │ │ ├── qglobal_p.h │ │ ├── qtcore-config_p.h │ │ ├── qtgui-config_p.h │ │ ├── qtguiglobal_p.h │ │ └── qzipreader_p.h │ ├── robomongo-unit-tests/ │ │ ├── CMakeLists.txt │ │ └── README.md │ └── third-party/ │ ├── README.md │ ├── esprima-2.7.3/ │ │ └── README.md │ ├── googletest-1.8.1/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── BUILD.bazel │ │ ├── CMakeLists.txt │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── Makefile.am │ │ ├── README.md │ │ ├── WORKSPACE │ │ ├── appveyor.yml │ │ ├── ci/ │ │ │ ├── build-linux-autotools.sh │ │ │ ├── build-linux-bazel.sh │ │ │ ├── env-linux.sh │ │ │ ├── env-osx.sh │ │ │ ├── get-nprocessors.sh │ │ │ ├── install-linux.sh │ │ │ ├── install-osx.sh │ │ │ ├── log-config.sh │ │ │ └── travis.sh │ │ ├── configure.ac │ │ ├── googlemock/ │ │ │ ├── CHANGES │ │ │ ├── CMakeLists.txt │ │ │ ├── CONTRIBUTORS │ │ │ ├── LICENSE │ │ │ ├── Makefile.am │ │ │ ├── README.md │ │ │ ├── cmake/ │ │ │ │ ├── gmock.pc.in │ │ │ │ └── gmock_main.pc.in │ │ │ ├── configure.ac │ │ │ ├── docs/ │ │ │ │ ├── CheatSheet.md │ │ │ │ ├── CookBook.md │ │ │ │ ├── DesignDoc.md │ │ │ │ ├── Documentation.md │ │ │ │ ├── ForDummies.md │ │ │ │ ├── FrequentlyAskedQuestions.md │ │ │ │ └── KnownIssues.md │ │ │ ├── include/ │ │ │ │ └── gmock/ │ │ │ │ ├── gmock-actions.h │ │ │ │ ├── gmock-cardinalities.h │ │ │ │ ├── gmock-generated-actions.h │ │ │ │ ├── gmock-generated-actions.h.pump │ │ │ │ ├── gmock-generated-function-mockers.h │ │ │ │ ├── gmock-generated-function-mockers.h.pump │ │ │ │ ├── gmock-generated-matchers.h │ │ │ │ ├── gmock-generated-matchers.h.pump │ │ │ │ ├── gmock-generated-nice-strict.h │ │ │ │ ├── gmock-generated-nice-strict.h.pump │ │ │ │ ├── gmock-matchers.h │ │ │ │ ├── gmock-more-actions.h │ │ │ │ ├── gmock-more-matchers.h │ │ │ │ ├── gmock-spec-builders.h │ │ │ │ ├── gmock.h │ │ │ │ └── internal/ │ │ │ │ ├── custom/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── gmock-generated-actions.h │ │ │ │ │ ├── gmock-generated-actions.h.pump │ │ │ │ │ ├── gmock-matchers.h │ │ │ │ │ └── gmock-port.h │ │ │ │ ├── gmock-generated-internal-utils.h │ │ │ │ ├── gmock-generated-internal-utils.h.pump │ │ │ │ ├── gmock-internal-utils.h │ │ │ │ └── gmock-port.h │ │ │ ├── msvc/ │ │ │ │ ├── 2005/ │ │ │ │ │ ├── gmock.sln │ │ │ │ │ ├── gmock.vcproj │ │ │ │ │ ├── gmock_config.vsprops │ │ │ │ │ ├── gmock_main.vcproj │ │ │ │ │ └── gmock_test.vcproj │ │ │ │ ├── 2010/ │ │ │ │ │ ├── gmock.sln │ │ │ │ │ ├── gmock.vcxproj │ │ │ │ │ ├── gmock_config.props │ │ │ │ │ ├── gmock_main.vcxproj │ │ │ │ │ └── gmock_test.vcxproj │ │ │ │ └── 2015/ │ │ │ │ ├── gmock.sln │ │ │ │ ├── gmock.vcxproj │ │ │ │ ├── gmock_config.props │ │ │ │ ├── gmock_main.vcxproj │ │ │ │ └── gmock_test.vcxproj │ │ │ ├── scripts/ │ │ │ │ ├── fuse_gmock_files.py │ │ │ │ ├── generator/ │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README │ │ │ │ │ ├── README.cppclean │ │ │ │ │ ├── cpp/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── ast.py │ │ │ │ │ │ ├── gmock_class.py │ │ │ │ │ │ ├── gmock_class_test.py │ │ │ │ │ │ ├── keywords.py │ │ │ │ │ │ ├── tokenize.py │ │ │ │ │ │ └── utils.py │ │ │ │ │ └── gmock_gen.py │ │ │ │ ├── gmock-config.in │ │ │ │ ├── gmock_doctor.py │ │ │ │ ├── upload.py │ │ │ │ └── upload_gmock.py │ │ │ ├── src/ │ │ │ │ ├── gmock-all.cc │ │ │ │ ├── gmock-cardinalities.cc │ │ │ │ ├── gmock-internal-utils.cc │ │ │ │ ├── gmock-matchers.cc │ │ │ │ ├── gmock-spec-builders.cc │ │ │ │ ├── gmock.cc │ │ │ │ └── gmock_main.cc │ │ │ └── test/ │ │ │ ├── BUILD.bazel │ │ │ ├── gmock-actions_test.cc │ │ │ ├── gmock-cardinalities_test.cc │ │ │ ├── gmock-generated-actions_test.cc │ │ │ ├── gmock-generated-function-mockers_test.cc │ │ │ ├── gmock-generated-internal-utils_test.cc │ │ │ ├── gmock-generated-matchers_test.cc │ │ │ ├── gmock-internal-utils_test.cc │ │ │ ├── gmock-matchers_test.cc │ │ │ ├── gmock-more-actions_test.cc │ │ │ ├── gmock-nice-strict_test.cc │ │ │ ├── gmock-port_test.cc │ │ │ ├── gmock-spec-builders_test.cc │ │ │ ├── gmock_all_test.cc │ │ │ ├── gmock_ex_test.cc │ │ │ ├── gmock_leak_test.py │ │ │ ├── gmock_leak_test_.cc │ │ │ ├── gmock_link2_test.cc │ │ │ ├── gmock_link_test.cc │ │ │ ├── gmock_link_test.h │ │ │ ├── gmock_output_test.py │ │ │ ├── gmock_output_test_.cc │ │ │ ├── gmock_output_test_golden.txt │ │ │ ├── gmock_stress_test.cc │ │ │ ├── gmock_test.cc │ │ │ └── gmock_test_utils.py │ │ └── googletest/ │ │ ├── CHANGES │ │ ├── CMakeLists.txt │ │ ├── CONTRIBUTORS │ │ ├── LICENSE │ │ ├── Makefile.am │ │ ├── README.md │ │ ├── cmake/ │ │ │ ├── Config.cmake.in │ │ │ ├── gtest.pc.in │ │ │ ├── gtest_main.pc.in │ │ │ └── internal_utils.cmake │ │ ├── codegear/ │ │ │ ├── gtest.cbproj │ │ │ ├── gtest.groupproj │ │ │ ├── gtest_all.cc │ │ │ ├── gtest_link.cc │ │ │ ├── gtest_main.cbproj │ │ │ └── gtest_unittest.cbproj │ │ ├── configure.ac │ │ ├── docs/ │ │ │ ├── Pkgconfig.md │ │ │ ├── PumpManual.md │ │ │ ├── XcodeGuide.md │ │ │ ├── advanced.md │ │ │ ├── faq.md │ │ │ ├── primer.md │ │ │ └── samples.md │ │ ├── include/ │ │ │ └── gtest/ │ │ │ ├── gtest-death-test.h │ │ │ ├── gtest-message.h │ │ │ ├── gtest-param-test.h │ │ │ ├── gtest-param-test.h.pump │ │ │ ├── gtest-printers.h │ │ │ ├── gtest-spi.h │ │ │ ├── gtest-test-part.h │ │ │ ├── gtest-typed-test.h │ │ │ ├── gtest.h │ │ │ ├── gtest_pred_impl.h │ │ │ ├── gtest_prod.h │ │ │ └── internal/ │ │ │ ├── custom/ │ │ │ │ ├── README.md │ │ │ │ ├── gtest-port.h │ │ │ │ ├── gtest-printers.h │ │ │ │ └── gtest.h │ │ │ ├── gtest-death-test-internal.h │ │ │ ├── gtest-filepath.h │ │ │ ├── gtest-internal.h │ │ │ ├── gtest-linked_ptr.h │ │ │ ├── gtest-param-util-generated.h │ │ │ ├── gtest-param-util-generated.h.pump │ │ │ ├── gtest-param-util.h │ │ │ ├── gtest-port-arch.h │ │ │ ├── gtest-port.h │ │ │ ├── gtest-string.h │ │ │ ├── gtest-tuple.h │ │ │ ├── gtest-tuple.h.pump │ │ │ ├── gtest-type-util.h │ │ │ └── gtest-type-util.h.pump │ │ ├── m4/ │ │ │ ├── acx_pthread.m4 │ │ │ └── gtest.m4 │ │ ├── msvc/ │ │ │ └── 2010/ │ │ │ ├── gtest-md.sln │ │ │ ├── gtest-md.vcxproj │ │ │ ├── gtest-md.vcxproj.filters │ │ │ ├── gtest.sln │ │ │ ├── gtest.vcxproj │ │ │ ├── gtest.vcxproj.filters │ │ │ ├── gtest_main-md.vcxproj │ │ │ ├── gtest_main-md.vcxproj.filters │ │ │ ├── gtest_main.vcxproj │ │ │ ├── gtest_main.vcxproj.filters │ │ │ ├── gtest_prod_test-md.vcxproj │ │ │ ├── gtest_prod_test-md.vcxproj.filters │ │ │ ├── gtest_prod_test.vcxproj │ │ │ ├── gtest_prod_test.vcxproj.filters │ │ │ ├── gtest_unittest-md.vcxproj │ │ │ ├── gtest_unittest-md.vcxproj.filters │ │ │ ├── gtest_unittest.vcxproj │ │ │ └── gtest_unittest.vcxproj.filters │ │ ├── samples/ │ │ │ ├── prime_tables.h │ │ │ ├── sample1.cc │ │ │ ├── sample1.h │ │ │ ├── sample10_unittest.cc │ │ │ ├── sample1_unittest.cc │ │ │ ├── sample2.cc │ │ │ ├── sample2.h │ │ │ ├── sample2_unittest.cc │ │ │ ├── sample3-inl.h │ │ │ ├── sample3_unittest.cc │ │ │ ├── sample4.cc │ │ │ ├── sample4.h │ │ │ ├── sample4_unittest.cc │ │ │ ├── sample5_unittest.cc │ │ │ ├── sample6_unittest.cc │ │ │ ├── sample7_unittest.cc │ │ │ ├── sample8_unittest.cc │ │ │ └── sample9_unittest.cc │ │ ├── scripts/ │ │ │ ├── common.py │ │ │ ├── fuse_gtest_files.py │ │ │ ├── gen_gtest_pred_impl.py │ │ │ ├── gtest-config.in │ │ │ ├── pump.py │ │ │ ├── release_docs.py │ │ │ ├── upload.py │ │ │ └── upload_gtest.py │ │ ├── src/ │ │ │ ├── gtest-all.cc │ │ │ ├── gtest-death-test.cc │ │ │ ├── gtest-filepath.cc │ │ │ ├── gtest-internal-inl.h │ │ │ ├── gtest-port.cc │ │ │ ├── gtest-printers.cc │ │ │ ├── gtest-test-part.cc │ │ │ ├── gtest-typed-test.cc │ │ │ ├── gtest.cc │ │ │ └── gtest_main.cc │ │ ├── test/ │ │ │ ├── BUILD.bazel │ │ │ ├── googletest-break-on-failure-unittest.py │ │ │ ├── googletest-break-on-failure-unittest_.cc │ │ │ ├── googletest-catch-exceptions-test.py │ │ │ ├── googletest-catch-exceptions-test_.cc │ │ │ ├── googletest-color-test.py │ │ │ ├── googletest-color-test_.cc │ │ │ ├── googletest-death-test-test.cc │ │ │ ├── googletest-death-test_ex_test.cc │ │ │ ├── googletest-env-var-test.py │ │ │ ├── googletest-env-var-test_.cc │ │ │ ├── googletest-filepath-test.cc │ │ │ ├── googletest-filter-unittest.py │ │ │ ├── googletest-filter-unittest_.cc │ │ │ ├── googletest-json-outfiles-test.py │ │ │ ├── googletest-json-output-unittest.py │ │ │ ├── googletest-linked-ptr-test.cc │ │ │ ├── googletest-list-tests-unittest.py │ │ │ ├── googletest-list-tests-unittest_.cc │ │ │ ├── googletest-listener-test.cc │ │ │ ├── googletest-message-test.cc │ │ │ ├── googletest-options-test.cc │ │ │ ├── googletest-output-test-golden-lin.txt │ │ │ ├── googletest-output-test.py │ │ │ ├── googletest-output-test_.cc │ │ │ ├── googletest-param-test-invalid-name1-test.py │ │ │ ├── googletest-param-test-invalid-name1-test_.cc │ │ │ ├── googletest-param-test-invalid-name2-test.py │ │ │ ├── googletest-param-test-invalid-name2-test_.cc │ │ │ ├── googletest-param-test-test.cc │ │ │ ├── googletest-param-test-test.h │ │ │ ├── googletest-param-test2-test.cc │ │ │ ├── googletest-port-test.cc │ │ │ ├── googletest-printers-test.cc │ │ │ ├── googletest-shuffle-test.py │ │ │ ├── googletest-shuffle-test_.cc │ │ │ ├── googletest-test-part-test.cc │ │ │ ├── googletest-test2_test.cc │ │ │ ├── googletest-throw-on-failure-test.py │ │ │ ├── googletest-throw-on-failure-test_.cc │ │ │ ├── googletest-tuple-test.cc │ │ │ ├── googletest-uninitialized-test.py │ │ │ ├── googletest-uninitialized-test_.cc │ │ │ ├── gtest-typed-test2_test.cc │ │ │ ├── gtest-typed-test_test.cc │ │ │ ├── gtest-typed-test_test.h │ │ │ ├── gtest-unittest-api_test.cc │ │ │ ├── gtest_all_test.cc │ │ │ ├── gtest_assert_by_exception_test.cc │ │ │ ├── gtest_environment_test.cc │ │ │ ├── gtest_help_test.py │ │ │ ├── gtest_help_test_.cc │ │ │ ├── gtest_json_test_utils.py │ │ │ ├── gtest_list_output_unittest.py │ │ │ ├── gtest_list_output_unittest_.cc │ │ │ ├── gtest_main_unittest.cc │ │ │ ├── gtest_no_test_unittest.cc │ │ │ ├── gtest_pred_impl_unittest.cc │ │ │ ├── gtest_premature_exit_test.cc │ │ │ ├── gtest_prod_test.cc │ │ │ ├── gtest_repeat_test.cc │ │ │ ├── gtest_sole_header_test.cc │ │ │ ├── gtest_stress_test.cc │ │ │ ├── gtest_test_macro_stack_footprint_test.cc │ │ │ ├── gtest_test_utils.py │ │ │ ├── gtest_testbridge_test.py │ │ │ ├── gtest_testbridge_test_.cc │ │ │ ├── gtest_throw_on_failure_ex_test.cc │ │ │ ├── gtest_unittest.cc │ │ │ ├── gtest_xml_outfile1_test_.cc │ │ │ ├── gtest_xml_outfile2_test_.cc │ │ │ ├── gtest_xml_outfiles_test.py │ │ │ ├── gtest_xml_output_unittest.py │ │ │ ├── gtest_xml_output_unittest_.cc │ │ │ ├── gtest_xml_test_utils.py │ │ │ ├── production.cc │ │ │ └── production.h │ │ └── xcode/ │ │ ├── Config/ │ │ │ ├── DebugProject.xcconfig │ │ │ ├── FrameworkTarget.xcconfig │ │ │ ├── General.xcconfig │ │ │ ├── ReleaseProject.xcconfig │ │ │ ├── StaticLibraryTarget.xcconfig │ │ │ └── TestTarget.xcconfig │ │ ├── Resources/ │ │ │ └── Info.plist │ │ ├── Samples/ │ │ │ └── FrameworkSample/ │ │ │ ├── Info.plist │ │ │ ├── WidgetFramework.xcodeproj/ │ │ │ │ └── project.pbxproj │ │ │ ├── runtests.sh │ │ │ ├── widget.cc │ │ │ ├── widget.h │ │ │ └── widget_test.cc │ │ ├── Scripts/ │ │ │ ├── runtests.sh │ │ │ └── versiongenerate.py │ │ └── gtest.xcodeproj/ │ │ └── project.pbxproj │ ├── libssh2-1.7.0/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ └── sources/ │ │ ├── .gitattribute │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CMakeLists.txt │ │ ├── COPYING │ │ ├── Makefile.OpenSSL.inc │ │ ├── Makefile.WinCNG.inc │ │ ├── Makefile.am │ │ ├── Makefile.inc │ │ ├── Makefile.libgcrypt.inc │ │ ├── Makefile.os400qc3.inc │ │ ├── NEWS │ │ ├── NMakefile │ │ ├── README │ │ ├── RELEASE-NOTES │ │ ├── acinclude.m4 │ │ ├── appveyor.yml │ │ ├── buildconf │ │ ├── cmake/ │ │ │ ├── CheckFunctionExistsMayNeedLibrary.cmake │ │ │ ├── CheckNonblockingSocketSupport.cmake │ │ │ ├── CopyRuntimeDependencies.cmake │ │ │ ├── FindLibgcrypt.cmake │ │ │ ├── SocketLibraries.cmake │ │ │ ├── Toolchain-Linux-32.cmake │ │ │ └── max_warnings.cmake │ │ ├── config.rpath │ │ ├── configure.ac │ │ ├── docs/ │ │ │ ├── .gitignore │ │ │ ├── AUTHORS │ │ │ ├── BINDINGS │ │ │ ├── CMakeLists.txt │ │ │ ├── HACKING │ │ │ ├── HACKING.CRYPTO │ │ │ ├── INSTALL_AUTOTOOLS │ │ │ ├── INSTALL_CMAKE │ │ │ ├── Makefile.am │ │ │ ├── TODO │ │ │ ├── libssh2_agent_connect.3 │ │ │ ├── libssh2_agent_disconnect.3 │ │ │ ├── libssh2_agent_free.3 │ │ │ ├── libssh2_agent_get_identity.3 │ │ │ ├── libssh2_agent_init.3 │ │ │ ├── libssh2_agent_list_identities.3 │ │ │ ├── libssh2_agent_userauth.3 │ │ │ ├── libssh2_banner_set.3 │ │ │ ├── libssh2_base64_decode.3 │ │ │ ├── libssh2_channel_close.3 │ │ │ ├── libssh2_channel_direct_tcpip.3 │ │ │ ├── libssh2_channel_direct_tcpip_ex.3 │ │ │ ├── libssh2_channel_eof.3 │ │ │ ├── libssh2_channel_exec.3 │ │ │ ├── libssh2_channel_flush.3 │ │ │ ├── libssh2_channel_flush_ex.3 │ │ │ ├── libssh2_channel_flush_stderr.3 │ │ │ ├── libssh2_channel_forward_accept.3 │ │ │ ├── libssh2_channel_forward_cancel.3 │ │ │ ├── libssh2_channel_forward_listen.3 │ │ │ ├── libssh2_channel_forward_listen_ex.3 │ │ │ ├── libssh2_channel_free.3 │ │ │ ├── libssh2_channel_get_exit_signal.3 │ │ │ ├── libssh2_channel_get_exit_status.3 │ │ │ ├── libssh2_channel_handle_extended_data.3 │ │ │ ├── libssh2_channel_handle_extended_data2.3 │ │ │ ├── libssh2_channel_ignore_extended_data.3 │ │ │ ├── libssh2_channel_open_ex.3 │ │ │ ├── libssh2_channel_open_session.3 │ │ │ ├── libssh2_channel_process_startup.3 │ │ │ ├── libssh2_channel_read.3 │ │ │ ├── libssh2_channel_read_ex.3 │ │ │ ├── libssh2_channel_read_stderr.3 │ │ │ ├── libssh2_channel_receive_window_adjust.3 │ │ │ ├── libssh2_channel_receive_window_adjust2.3 │ │ │ ├── libssh2_channel_request_pty.3 │ │ │ ├── libssh2_channel_request_pty_ex.3 │ │ │ ├── libssh2_channel_request_pty_size.3 │ │ │ ├── libssh2_channel_request_pty_size_ex.3 │ │ │ ├── libssh2_channel_send_eof.3 │ │ │ ├── libssh2_channel_set_blocking.3 │ │ │ ├── libssh2_channel_setenv.3 │ │ │ ├── libssh2_channel_setenv_ex.3 │ │ │ ├── libssh2_channel_shell.3 │ │ │ ├── libssh2_channel_subsystem.3 │ │ │ ├── libssh2_channel_wait_closed.3 │ │ │ ├── libssh2_channel_wait_eof.3 │ │ │ ├── libssh2_channel_window_read.3 │ │ │ ├── libssh2_channel_window_read_ex.3 │ │ │ ├── libssh2_channel_window_write.3 │ │ │ ├── libssh2_channel_window_write_ex.3 │ │ │ ├── libssh2_channel_write.3 │ │ │ ├── libssh2_channel_write_ex.3 │ │ │ ├── libssh2_channel_write_stderr.3 │ │ │ ├── libssh2_channel_x11_req.3 │ │ │ ├── libssh2_channel_x11_req_ex.3 │ │ │ ├── libssh2_exit.3 │ │ │ ├── libssh2_free.3 │ │ │ ├── libssh2_hostkey_hash.3 │ │ │ ├── libssh2_init.3 │ │ │ ├── libssh2_keepalive_config.3 │ │ │ ├── libssh2_keepalive_send.3 │ │ │ ├── libssh2_knownhost_add.3 │ │ │ ├── libssh2_knownhost_addc.3 │ │ │ ├── libssh2_knownhost_check.3 │ │ │ ├── libssh2_knownhost_checkp.3 │ │ │ ├── libssh2_knownhost_del.3 │ │ │ ├── libssh2_knownhost_free.3 │ │ │ ├── libssh2_knownhost_get.3 │ │ │ ├── libssh2_knownhost_init.3 │ │ │ ├── libssh2_knownhost_readfile.3 │ │ │ ├── libssh2_knownhost_readline.3 │ │ │ ├── libssh2_knownhost_writefile.3 │ │ │ ├── libssh2_knownhost_writeline.3 │ │ │ ├── libssh2_poll.3 │ │ │ ├── libssh2_poll_channel_read.3 │ │ │ ├── libssh2_publickey_add.3 │ │ │ ├── libssh2_publickey_add_ex.3 │ │ │ ├── libssh2_publickey_init.3 │ │ │ ├── libssh2_publickey_list_fetch.3 │ │ │ ├── libssh2_publickey_list_free.3 │ │ │ ├── libssh2_publickey_remove.3 │ │ │ ├── libssh2_publickey_remove_ex.3 │ │ │ ├── libssh2_publickey_shutdown.3 │ │ │ ├── libssh2_scp_recv.3 │ │ │ ├── libssh2_scp_recv2.3 │ │ │ ├── libssh2_scp_send.3 │ │ │ ├── libssh2_scp_send64.3 │ │ │ ├── libssh2_scp_send_ex.3 │ │ │ ├── libssh2_session_abstract.3 │ │ │ ├── libssh2_session_banner_get.3 │ │ │ ├── libssh2_session_banner_set.3 │ │ │ ├── libssh2_session_block_directions.3 │ │ │ ├── libssh2_session_callback_set.3 │ │ │ ├── libssh2_session_disconnect.3 │ │ │ ├── libssh2_session_disconnect_ex.3 │ │ │ ├── libssh2_session_flag.3 │ │ │ ├── libssh2_session_free.3 │ │ │ ├── libssh2_session_get_blocking.3 │ │ │ ├── libssh2_session_get_timeout.3 │ │ │ ├── libssh2_session_handshake.3 │ │ │ ├── libssh2_session_hostkey.3 │ │ │ ├── libssh2_session_init.3 │ │ │ ├── libssh2_session_init_ex.3 │ │ │ ├── libssh2_session_last_errno.3 │ │ │ ├── libssh2_session_last_error.3 │ │ │ ├── libssh2_session_method_pref.3 │ │ │ ├── libssh2_session_methods.3 │ │ │ ├── libssh2_session_set_blocking.3 │ │ │ ├── libssh2_session_set_last_error.3 │ │ │ ├── libssh2_session_set_timeout.3 │ │ │ ├── libssh2_session_startup.3 │ │ │ ├── libssh2_session_supported_algs.3 │ │ │ ├── libssh2_sftp_close.3 │ │ │ ├── libssh2_sftp_close_handle.3 │ │ │ ├── libssh2_sftp_closedir.3 │ │ │ ├── libssh2_sftp_fsetstat.3 │ │ │ ├── libssh2_sftp_fstat.3 │ │ │ ├── libssh2_sftp_fstat_ex.3 │ │ │ ├── libssh2_sftp_fstatvfs.3 │ │ │ ├── libssh2_sftp_fsync.3 │ │ │ ├── libssh2_sftp_get_channel.3 │ │ │ ├── libssh2_sftp_init.3 │ │ │ ├── libssh2_sftp_last_error.3 │ │ │ ├── libssh2_sftp_lstat.3 │ │ │ ├── libssh2_sftp_mkdir.3 │ │ │ ├── libssh2_sftp_mkdir_ex.3 │ │ │ ├── libssh2_sftp_open.3 │ │ │ ├── libssh2_sftp_open_ex.3 │ │ │ ├── libssh2_sftp_opendir.3 │ │ │ ├── libssh2_sftp_read.3 │ │ │ ├── libssh2_sftp_readdir.3 │ │ │ ├── libssh2_sftp_readdir_ex.3 │ │ │ ├── libssh2_sftp_readlink.3 │ │ │ ├── libssh2_sftp_realpath.3 │ │ │ ├── libssh2_sftp_rename.3 │ │ │ ├── libssh2_sftp_rename_ex.3 │ │ │ ├── libssh2_sftp_rewind.3 │ │ │ ├── libssh2_sftp_rmdir.3 │ │ │ ├── libssh2_sftp_rmdir_ex.3 │ │ │ ├── libssh2_sftp_seek.3 │ │ │ ├── libssh2_sftp_seek64.3 │ │ │ ├── libssh2_sftp_setstat.3 │ │ │ ├── libssh2_sftp_shutdown.3 │ │ │ ├── libssh2_sftp_stat.3 │ │ │ ├── libssh2_sftp_stat_ex.3 │ │ │ ├── libssh2_sftp_statvfs.3 │ │ │ ├── libssh2_sftp_symlink.3 │ │ │ ├── libssh2_sftp_symlink_ex.3 │ │ │ ├── libssh2_sftp_tell.3 │ │ │ ├── libssh2_sftp_tell64.3 │ │ │ ├── libssh2_sftp_unlink.3 │ │ │ ├── libssh2_sftp_unlink_ex.3 │ │ │ ├── libssh2_sftp_write.3 │ │ │ ├── libssh2_trace.3 │ │ │ ├── libssh2_trace_sethandler.3 │ │ │ ├── libssh2_userauth_authenticated.3 │ │ │ ├── libssh2_userauth_hostbased_fromfile.3 │ │ │ ├── libssh2_userauth_hostbased_fromfile_ex.3 │ │ │ ├── libssh2_userauth_keyboard_interactive.3 │ │ │ ├── libssh2_userauth_keyboard_interactive_ex.3 │ │ │ ├── libssh2_userauth_list.3 │ │ │ ├── libssh2_userauth_password.3 │ │ │ ├── libssh2_userauth_password_ex.3 │ │ │ ├── libssh2_userauth_publickey.3 │ │ │ ├── libssh2_userauth_publickey_fromfile.3 │ │ │ ├── libssh2_userauth_publickey_fromfile_ex.3 │ │ │ ├── libssh2_userauth_publickey_frommemory.3 │ │ │ ├── libssh2_version.3 │ │ │ └── template.3 │ │ ├── example/ │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── Makefile.am │ │ │ ├── direct_tcpip.c │ │ │ ├── libssh2_config_cmake.h.in │ │ │ ├── scp.c │ │ │ ├── scp_nonblock.c │ │ │ ├── scp_write.c │ │ │ ├── scp_write_nonblock.c │ │ │ ├── sftp.c │ │ │ ├── sftp_RW_nonblock.c │ │ │ ├── sftp_append.c │ │ │ ├── sftp_mkdir.c │ │ │ ├── sftp_mkdir_nonblock.c │ │ │ ├── sftp_nonblock.c │ │ │ ├── sftp_write.c │ │ │ ├── sftp_write_nonblock.c │ │ │ ├── sftp_write_sliding.c │ │ │ ├── sftpdir.c │ │ │ ├── sftpdir_nonblock.c │ │ │ ├── ssh2.c │ │ │ ├── ssh2_agent.c │ │ │ ├── ssh2_echo.c │ │ │ ├── ssh2_exec.c │ │ │ ├── subsystem_netconf.c │ │ │ ├── tcpip-forward.c │ │ │ └── x11.c │ │ ├── get_ver.awk │ │ ├── git2news.pl │ │ ├── include/ │ │ │ ├── libssh2.h │ │ │ ├── libssh2_publickey.h │ │ │ └── libssh2_sftp.h │ │ ├── libssh2-style.el │ │ ├── libssh2.pc.in │ │ ├── m4/ │ │ │ ├── .gitignore │ │ │ ├── autobuild.m4 │ │ │ ├── lib-ld.m4 │ │ │ ├── lib-link.m4 │ │ │ └── lib-prefix.m4 │ │ ├── maketgz │ │ ├── nw/ │ │ │ ├── GNUmakefile │ │ │ ├── keepscreen.c │ │ │ ├── nwlib.c │ │ │ └── test/ │ │ │ └── GNUmakefile │ │ ├── os400/ │ │ │ ├── README400 │ │ │ ├── ccsid.c │ │ │ ├── include/ │ │ │ │ ├── alloca.h │ │ │ │ ├── stdio.h │ │ │ │ └── sys/ │ │ │ │ └── socket.h │ │ │ ├── initscript.sh │ │ │ ├── libssh2_ccsid.h │ │ │ ├── libssh2_config.h │ │ │ ├── libssh2rpg/ │ │ │ │ ├── libssh2.rpgle.in │ │ │ │ ├── libssh2_ccsid.rpgle.in │ │ │ │ ├── libssh2_publickey.rpgle │ │ │ │ └── libssh2_sftp.rpgle │ │ │ ├── macros.h │ │ │ ├── make-include.sh │ │ │ ├── make-rpg.sh │ │ │ ├── make-src.sh │ │ │ ├── make.sh │ │ │ └── os400sys.c │ │ ├── src/ │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── Makefile.am │ │ │ ├── NMakefile │ │ │ ├── agent.c │ │ │ ├── channel.c │ │ │ ├── channel.h │ │ │ ├── comp.c │ │ │ ├── comp.h │ │ │ ├── crypt.c │ │ │ ├── crypto.h │ │ │ ├── global.c │ │ │ ├── hostkey.c │ │ │ ├── keepalive.c │ │ │ ├── kex.c │ │ │ ├── knownhost.c │ │ │ ├── libgcrypt.c │ │ │ ├── libgcrypt.h │ │ │ ├── libssh2.pc.in │ │ │ ├── libssh2_config_cmake.h.in │ │ │ ├── libssh2_priv.h │ │ │ ├── mac.c │ │ │ ├── mac.h │ │ │ ├── misc.c │ │ │ ├── misc.h │ │ │ ├── openssl.c │ │ │ ├── openssl.h │ │ │ ├── os400qc3.c │ │ │ ├── os400qc3.h │ │ │ ├── packet.c │ │ │ ├── packet.h │ │ │ ├── pem.c │ │ │ ├── publickey.c │ │ │ ├── scp.c │ │ │ ├── session.c │ │ │ ├── session.h │ │ │ ├── sftp.c │ │ │ ├── sftp.h │ │ │ ├── transport.c │ │ │ ├── transport.h │ │ │ ├── userauth.c │ │ │ ├── userauth.h │ │ │ ├── version.c │ │ │ ├── wincng.c │ │ │ └── wincng.h │ │ ├── tests/ │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── Makefile.am │ │ │ ├── etc/ │ │ │ │ ├── host │ │ │ │ ├── host.pub │ │ │ │ ├── sshd_config │ │ │ │ ├── user │ │ │ │ └── user.pub │ │ │ ├── libssh2_config_cmake.h.in │ │ │ ├── mansyntax.sh │ │ │ ├── simple.c │ │ │ ├── ssh2.c │ │ │ ├── ssh2.sh │ │ │ ├── sshd_fixture.sh.in │ │ │ └── sshdwrap │ │ ├── vms/ │ │ │ ├── libssh2_config.h │ │ │ ├── libssh2_make_example.dcl │ │ │ ├── libssh2_make_help.dcl │ │ │ ├── libssh2_make_kit.dcl │ │ │ ├── libssh2_make_lib.dcl │ │ │ ├── man2help.c │ │ │ └── readme.vms │ │ └── win32/ │ │ ├── .gitignore │ │ ├── GNUmakefile │ │ ├── Makefile.Watcom │ │ ├── config.mk │ │ ├── libssh2.dsw │ │ ├── libssh2.rc │ │ ├── libssh2_config.h │ │ ├── msvcproj.foot │ │ ├── msvcproj.head │ │ ├── rules.mk │ │ ├── test/ │ │ │ └── GNUmakefile │ │ └── tests.dsp │ ├── libssh2-1.9.0/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ └── sources/ │ │ ├── .editorconfig │ │ ├── .gitattribute │ │ ├── .github/ │ │ │ ├── ISSUE_TEMPLATE/ │ │ │ │ └── bug_report.md │ │ │ └── stale.yml │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CMakeLists.txt │ │ ├── COPYING │ │ ├── Makefile.OpenSSL.inc │ │ ├── Makefile.WinCNG.inc │ │ ├── Makefile.am │ │ ├── Makefile.inc │ │ ├── Makefile.libgcrypt.inc │ │ ├── Makefile.mbedTLS.inc │ │ ├── Makefile.os400qc3.inc │ │ ├── NEWS │ │ ├── NMakefile │ │ ├── README │ │ ├── README.md │ │ ├── RELEASE-NOTES │ │ ├── acinclude.m4 │ │ ├── appveyor.yml │ │ ├── buildconf │ │ ├── cmake/ │ │ │ ├── CheckFunctionExistsMayNeedLibrary.cmake │ │ │ ├── CheckNonblockingSocketSupport.cmake │ │ │ ├── CopyRuntimeDependencies.cmake │ │ │ ├── FindLibgcrypt.cmake │ │ │ ├── FindmbedTLS.cmake │ │ │ ├── SocketLibraries.cmake │ │ │ ├── Toolchain-Linux-32.cmake │ │ │ └── max_warnings.cmake │ │ ├── config.rpath │ │ ├── configure.ac │ │ ├── docs/ │ │ │ ├── .gitignore │ │ │ ├── AUTHORS │ │ │ ├── BINDINGS │ │ │ ├── CMakeLists.txt │ │ │ ├── HACKING │ │ │ ├── HACKING.CRYPTO │ │ │ ├── INSTALL_AUTOTOOLS │ │ │ ├── INSTALL_CMAKE │ │ │ ├── Makefile.am │ │ │ ├── SECURITY.md │ │ │ ├── TODO │ │ │ ├── libssh2_agent_connect.3 │ │ │ ├── libssh2_agent_disconnect.3 │ │ │ ├── libssh2_agent_free.3 │ │ │ ├── libssh2_agent_get_identity.3 │ │ │ ├── libssh2_agent_get_identity_path.3 │ │ │ ├── libssh2_agent_init.3 │ │ │ ├── libssh2_agent_list_identities.3 │ │ │ ├── libssh2_agent_set_identity_path.3 │ │ │ ├── libssh2_agent_userauth.3 │ │ │ ├── libssh2_banner_set.3 │ │ │ ├── libssh2_base64_decode.3 │ │ │ ├── libssh2_channel_close.3 │ │ │ ├── libssh2_channel_direct_tcpip.3 │ │ │ ├── libssh2_channel_direct_tcpip_ex.3 │ │ │ ├── libssh2_channel_eof.3 │ │ │ ├── libssh2_channel_exec.3 │ │ │ ├── libssh2_channel_flush.3 │ │ │ ├── libssh2_channel_flush_ex.3 │ │ │ ├── libssh2_channel_flush_stderr.3 │ │ │ ├── libssh2_channel_forward_accept.3 │ │ │ ├── libssh2_channel_forward_cancel.3 │ │ │ ├── libssh2_channel_forward_listen.3 │ │ │ ├── libssh2_channel_forward_listen_ex.3 │ │ │ ├── libssh2_channel_free.3 │ │ │ ├── libssh2_channel_get_exit_signal.3 │ │ │ ├── libssh2_channel_get_exit_status.3 │ │ │ ├── libssh2_channel_handle_extended_data.3 │ │ │ ├── libssh2_channel_handle_extended_data2.3 │ │ │ ├── libssh2_channel_ignore_extended_data.3 │ │ │ ├── libssh2_channel_open_ex.3 │ │ │ ├── libssh2_channel_open_session.3 │ │ │ ├── libssh2_channel_process_startup.3 │ │ │ ├── libssh2_channel_read.3 │ │ │ ├── libssh2_channel_read_ex.3 │ │ │ ├── libssh2_channel_read_stderr.3 │ │ │ ├── libssh2_channel_receive_window_adjust.3 │ │ │ ├── libssh2_channel_receive_window_adjust2.3 │ │ │ ├── libssh2_channel_request_pty.3 │ │ │ ├── libssh2_channel_request_pty_ex.3 │ │ │ ├── libssh2_channel_request_pty_size.3 │ │ │ ├── libssh2_channel_request_pty_size_ex.3 │ │ │ ├── libssh2_channel_send_eof.3 │ │ │ ├── libssh2_channel_set_blocking.3 │ │ │ ├── libssh2_channel_setenv.3 │ │ │ ├── libssh2_channel_setenv_ex.3 │ │ │ ├── libssh2_channel_shell.3 │ │ │ ├── libssh2_channel_subsystem.3 │ │ │ ├── libssh2_channel_wait_closed.3 │ │ │ ├── libssh2_channel_wait_eof.3 │ │ │ ├── libssh2_channel_window_read.3 │ │ │ ├── libssh2_channel_window_read_ex.3 │ │ │ ├── libssh2_channel_window_write.3 │ │ │ ├── libssh2_channel_window_write_ex.3 │ │ │ ├── libssh2_channel_write.3 │ │ │ ├── libssh2_channel_write_ex.3 │ │ │ ├── libssh2_channel_write_stderr.3 │ │ │ ├── libssh2_channel_x11_req.3 │ │ │ ├── libssh2_channel_x11_req_ex.3 │ │ │ ├── libssh2_exit.3 │ │ │ ├── libssh2_free.3 │ │ │ ├── libssh2_hostkey_hash.3 │ │ │ ├── libssh2_init.3 │ │ │ ├── libssh2_keepalive_config.3 │ │ │ ├── libssh2_keepalive_send.3 │ │ │ ├── libssh2_knownhost_add.3 │ │ │ ├── libssh2_knownhost_addc.3 │ │ │ ├── libssh2_knownhost_check.3 │ │ │ ├── libssh2_knownhost_checkp.3 │ │ │ ├── libssh2_knownhost_del.3 │ │ │ ├── libssh2_knownhost_free.3 │ │ │ ├── libssh2_knownhost_get.3 │ │ │ ├── libssh2_knownhost_init.3 │ │ │ ├── libssh2_knownhost_readfile.3 │ │ │ ├── libssh2_knownhost_readline.3 │ │ │ ├── libssh2_knownhost_writefile.3 │ │ │ ├── libssh2_knownhost_writeline.3 │ │ │ ├── libssh2_poll.3 │ │ │ ├── libssh2_poll_channel_read.3 │ │ │ ├── libssh2_publickey_add.3 │ │ │ ├── libssh2_publickey_add_ex.3 │ │ │ ├── libssh2_publickey_init.3 │ │ │ ├── libssh2_publickey_list_fetch.3 │ │ │ ├── libssh2_publickey_list_free.3 │ │ │ ├── libssh2_publickey_remove.3 │ │ │ ├── libssh2_publickey_remove_ex.3 │ │ │ ├── libssh2_publickey_shutdown.3 │ │ │ ├── libssh2_scp_recv.3 │ │ │ ├── libssh2_scp_recv2.3 │ │ │ ├── libssh2_scp_send.3 │ │ │ ├── libssh2_scp_send64.3 │ │ │ ├── libssh2_scp_send_ex.3 │ │ │ ├── libssh2_session_abstract.3 │ │ │ ├── libssh2_session_banner_get.3 │ │ │ ├── libssh2_session_banner_set.3 │ │ │ ├── libssh2_session_block_directions.3 │ │ │ ├── libssh2_session_callback_set.3 │ │ │ ├── libssh2_session_disconnect.3 │ │ │ ├── libssh2_session_disconnect_ex.3 │ │ │ ├── libssh2_session_flag.3 │ │ │ ├── libssh2_session_free.3 │ │ │ ├── libssh2_session_get_blocking.3 │ │ │ ├── libssh2_session_get_timeout.3 │ │ │ ├── libssh2_session_handshake.3 │ │ │ ├── libssh2_session_hostkey.3 │ │ │ ├── libssh2_session_init.3 │ │ │ ├── libssh2_session_init_ex.3 │ │ │ ├── libssh2_session_last_errno.3 │ │ │ ├── libssh2_session_last_error.3 │ │ │ ├── libssh2_session_method_pref.3 │ │ │ ├── libssh2_session_methods.3 │ │ │ ├── libssh2_session_set_blocking.3 │ │ │ ├── libssh2_session_set_last_error.3 │ │ │ ├── libssh2_session_set_timeout.3 │ │ │ ├── libssh2_session_startup.3 │ │ │ ├── libssh2_session_supported_algs.3 │ │ │ ├── libssh2_sftp_close.3 │ │ │ ├── libssh2_sftp_close_handle.3 │ │ │ ├── libssh2_sftp_closedir.3 │ │ │ ├── libssh2_sftp_fsetstat.3 │ │ │ ├── libssh2_sftp_fstat.3 │ │ │ ├── libssh2_sftp_fstat_ex.3 │ │ │ ├── libssh2_sftp_fstatvfs.3 │ │ │ ├── libssh2_sftp_fsync.3 │ │ │ ├── libssh2_sftp_get_channel.3 │ │ │ ├── libssh2_sftp_init.3 │ │ │ ├── libssh2_sftp_last_error.3 │ │ │ ├── libssh2_sftp_lstat.3 │ │ │ ├── libssh2_sftp_mkdir.3 │ │ │ ├── libssh2_sftp_mkdir_ex.3 │ │ │ ├── libssh2_sftp_open.3 │ │ │ ├── libssh2_sftp_open_ex.3 │ │ │ ├── libssh2_sftp_opendir.3 │ │ │ ├── libssh2_sftp_read.3 │ │ │ ├── libssh2_sftp_readdir.3 │ │ │ ├── libssh2_sftp_readdir_ex.3 │ │ │ ├── libssh2_sftp_readlink.3 │ │ │ ├── libssh2_sftp_realpath.3 │ │ │ ├── libssh2_sftp_rename.3 │ │ │ ├── libssh2_sftp_rename_ex.3 │ │ │ ├── libssh2_sftp_rewind.3 │ │ │ ├── libssh2_sftp_rmdir.3 │ │ │ ├── libssh2_sftp_rmdir_ex.3 │ │ │ ├── libssh2_sftp_seek.3 │ │ │ ├── libssh2_sftp_seek64.3 │ │ │ ├── libssh2_sftp_setstat.3 │ │ │ ├── libssh2_sftp_shutdown.3 │ │ │ ├── libssh2_sftp_stat.3 │ │ │ ├── libssh2_sftp_stat_ex.3 │ │ │ ├── libssh2_sftp_statvfs.3 │ │ │ ├── libssh2_sftp_symlink.3 │ │ │ ├── libssh2_sftp_symlink_ex.3 │ │ │ ├── libssh2_sftp_tell.3 │ │ │ ├── libssh2_sftp_tell64.3 │ │ │ ├── libssh2_sftp_unlink.3 │ │ │ ├── libssh2_sftp_unlink_ex.3 │ │ │ ├── libssh2_sftp_write.3 │ │ │ ├── libssh2_trace.3 │ │ │ ├── libssh2_trace_sethandler.3 │ │ │ ├── libssh2_userauth_authenticated.3 │ │ │ ├── libssh2_userauth_hostbased_fromfile.3 │ │ │ ├── libssh2_userauth_hostbased_fromfile_ex.3 │ │ │ ├── libssh2_userauth_keyboard_interactive.3 │ │ │ ├── libssh2_userauth_keyboard_interactive_ex.3 │ │ │ ├── libssh2_userauth_list.3 │ │ │ ├── libssh2_userauth_password.3 │ │ │ ├── libssh2_userauth_password_ex.3 │ │ │ ├── libssh2_userauth_publickey.3 │ │ │ ├── libssh2_userauth_publickey_fromfile.3 │ │ │ ├── libssh2_userauth_publickey_fromfile_ex.3 │ │ │ ├── libssh2_userauth_publickey_frommemory.3 │ │ │ ├── libssh2_version.3 │ │ │ └── template.3 │ │ ├── example/ │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── Makefile.am │ │ │ ├── direct_tcpip.c │ │ │ ├── libssh2_config_cmake.h.in │ │ │ ├── scp.c │ │ │ ├── scp_nonblock.c │ │ │ ├── scp_write.c │ │ │ ├── scp_write_nonblock.c │ │ │ ├── sftp.c │ │ │ ├── sftp_RW_nonblock.c │ │ │ ├── sftp_append.c │ │ │ ├── sftp_mkdir.c │ │ │ ├── sftp_mkdir_nonblock.c │ │ │ ├── sftp_nonblock.c │ │ │ ├── sftp_write.c │ │ │ ├── sftp_write_nonblock.c │ │ │ ├── sftp_write_sliding.c │ │ │ ├── sftpdir.c │ │ │ ├── sftpdir_nonblock.c │ │ │ ├── ssh2.c │ │ │ ├── ssh2_agent.c │ │ │ ├── ssh2_echo.c │ │ │ ├── ssh2_exec.c │ │ │ ├── subsystem_netconf.c │ │ │ ├── tcpip-forward.c │ │ │ └── x11.c │ │ ├── get_ver.awk │ │ ├── git2news.pl │ │ ├── include/ │ │ │ ├── libssh2.h │ │ │ ├── libssh2_publickey.h │ │ │ └── libssh2_sftp.h │ │ ├── libssh2-style.el │ │ ├── libssh2.pc.in │ │ ├── m4/ │ │ │ ├── .gitignore │ │ │ ├── autobuild.m4 │ │ │ ├── lib-ld.m4 │ │ │ ├── lib-link.m4 │ │ │ └── lib-prefix.m4 │ │ ├── maketgz │ │ ├── nw/ │ │ │ ├── GNUmakefile │ │ │ ├── keepscreen.c │ │ │ ├── nwlib.c │ │ │ └── test/ │ │ │ └── GNUmakefile │ │ ├── os400/ │ │ │ ├── README400 │ │ │ ├── ccsid.c │ │ │ ├── include/ │ │ │ │ ├── alloca.h │ │ │ │ ├── stdio.h │ │ │ │ └── sys/ │ │ │ │ └── socket.h │ │ │ ├── initscript.sh │ │ │ ├── libssh2_ccsid.h │ │ │ ├── libssh2_config.h │ │ │ ├── libssh2rpg/ │ │ │ │ ├── libssh2.rpgle.in │ │ │ │ ├── libssh2_ccsid.rpgle.in │ │ │ │ ├── libssh2_publickey.rpgle │ │ │ │ └── libssh2_sftp.rpgle │ │ │ ├── macros.h │ │ │ ├── make-include.sh │ │ │ ├── make-rpg.sh │ │ │ ├── make-src.sh │ │ │ ├── make.sh │ │ │ └── os400sys.c │ │ ├── src/ │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── Makefile.am │ │ │ ├── NMakefile │ │ │ ├── agent.c │ │ │ ├── bcrypt_pbkdf.c │ │ │ ├── blf.h │ │ │ ├── blowfish.c │ │ │ ├── channel.c │ │ │ ├── channel.h │ │ │ ├── checksrc.pl │ │ │ ├── comp.c │ │ │ ├── comp.h │ │ │ ├── crypt.c │ │ │ ├── crypto.h │ │ │ ├── global.c │ │ │ ├── hostkey.c │ │ │ ├── keepalive.c │ │ │ ├── kex.c │ │ │ ├── knownhost.c │ │ │ ├── libgcrypt.c │ │ │ ├── libgcrypt.h │ │ │ ├── libssh2.pc.in │ │ │ ├── libssh2_config_cmake.h.in │ │ │ ├── libssh2_priv.h │ │ │ ├── mac.c │ │ │ ├── mac.h │ │ │ ├── mbedtls.c │ │ │ ├── mbedtls.h │ │ │ ├── misc.c │ │ │ ├── misc.h │ │ │ ├── openssl.c │ │ │ ├── openssl.h │ │ │ ├── os400qc3.c │ │ │ ├── os400qc3.h │ │ │ ├── packet.c │ │ │ ├── packet.h │ │ │ ├── pem.c │ │ │ ├── publickey.c │ │ │ ├── scp.c │ │ │ ├── session.c │ │ │ ├── session.h │ │ │ ├── sftp.c │ │ │ ├── sftp.h │ │ │ ├── transport.c │ │ │ ├── transport.h │ │ │ ├── userauth.c │ │ │ ├── userauth.h │ │ │ ├── version.c │ │ │ ├── wincng.c │ │ │ └── wincng.h │ │ ├── tests/ │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── Makefile.am │ │ │ ├── etc/ │ │ │ │ ├── host │ │ │ │ ├── host.pub │ │ │ │ ├── sshd_config │ │ │ │ ├── user │ │ │ │ └── user.pub │ │ │ ├── key_dsa │ │ │ ├── key_dsa.pub │ │ │ ├── key_dsa_wrong │ │ │ ├── key_dsa_wrong.pub │ │ │ ├── key_ed25519 │ │ │ ├── key_ed25519.pub │ │ │ ├── key_ed25519_encrypted │ │ │ ├── key_ed25519_encrypted.pub │ │ │ ├── key_rsa │ │ │ ├── key_rsa.pub │ │ │ ├── key_rsa_encrypted │ │ │ ├── key_rsa_encrypted.pub │ │ │ ├── key_rsa_openssh │ │ │ ├── key_rsa_openssh.pub │ │ │ ├── libssh2_config_cmake.h.in │ │ │ ├── mansyntax.sh │ │ │ ├── openssh_fixture.c │ │ │ ├── openssh_fixture.h │ │ │ ├── openssh_server/ │ │ │ │ ├── Dockerfile │ │ │ │ ├── authorized_keys │ │ │ │ ├── ssh_host_ecdsa_key │ │ │ │ ├── ssh_host_ed25519_key │ │ │ │ └── ssh_host_rsa_key │ │ │ ├── runner.c │ │ │ ├── session_fixture.c │ │ │ ├── session_fixture.h │ │ │ ├── simple.c │ │ │ ├── ssh2.c │ │ │ ├── ssh2.sh │ │ │ ├── sshd_fixture.sh.in │ │ │ ├── sshdwrap │ │ │ ├── test_hostkey.c │ │ │ ├── test_hostkey_hash.c │ │ │ ├── test_keyboard_interactive_auth_fails_with_wrong_response.c │ │ │ ├── test_keyboard_interactive_auth_succeeds_with_correct_response.c │ │ │ ├── test_password_auth_fails_with_wrong_password.c │ │ │ ├── test_password_auth_fails_with_wrong_username.c │ │ │ ├── test_password_auth_succeeds_with_correct_credentials.c │ │ │ ├── test_public_key_auth_fails_with_wrong_key.c │ │ │ ├── test_public_key_auth_succeeds_with_correct_dsa_key.c │ │ │ ├── test_public_key_auth_succeeds_with_correct_ed25519_key.c │ │ │ ├── test_public_key_auth_succeeds_with_correct_ed25519_key_from_mem.c │ │ │ ├── test_public_key_auth_succeeds_with_correct_encrypted_ed25519_key.c │ │ │ ├── test_public_key_auth_succeeds_with_correct_encrypted_rsa_key.c │ │ │ ├── test_public_key_auth_succeeds_with_correct_rsa_key.c │ │ │ └── test_public_key_auth_succeeds_with_correct_rsa_openssh_key.c │ │ ├── vms/ │ │ │ ├── libssh2_config.h │ │ │ ├── libssh2_make_example.dcl │ │ │ ├── libssh2_make_help.dcl │ │ │ ├── libssh2_make_kit.dcl │ │ │ ├── libssh2_make_lib.dcl │ │ │ ├── man2help.c │ │ │ └── readme.vms │ │ └── win32/ │ │ ├── .gitignore │ │ ├── GNUmakefile │ │ ├── Makefile.Watcom │ │ ├── config.mk │ │ ├── libssh2.dsw │ │ ├── libssh2.rc │ │ ├── libssh2_config.h │ │ ├── msvcproj.foot │ │ ├── msvcproj.head │ │ ├── rules.mk │ │ ├── test/ │ │ │ └── GNUmakefile │ │ └── tests.dsp │ ├── qjson-0.8.1/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ └── sources/ │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── ChangeLog │ │ ├── QJSONConfig.cmake.in │ │ ├── QJSONConfigVersion.cmake.in │ │ ├── QJson.pc.in │ │ ├── README.license │ │ ├── README.md │ │ ├── cmake_uninstall.cmake.in │ │ ├── doc/ │ │ │ ├── Doxyfile │ │ │ ├── footer.html │ │ │ ├── header.html │ │ │ └── qjson.dox │ │ ├── include/ │ │ │ └── QJson/ │ │ │ ├── Parser │ │ │ ├── QObjectHelper │ │ │ └── Serializer │ │ ├── src/ │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── FlexLexer.h │ │ │ ├── json_parser.cc │ │ │ ├── json_parser.hh │ │ │ ├── json_parser.yy │ │ │ ├── json_scanner.cc │ │ │ ├── json_scanner.cpp │ │ │ ├── json_scanner.h │ │ │ ├── json_scanner.yy │ │ │ ├── location.hh │ │ │ ├── parser.cpp │ │ │ ├── parser.h │ │ │ ├── parser_p.h │ │ │ ├── parserrunnable.cpp │ │ │ ├── parserrunnable.h │ │ │ ├── position.hh │ │ │ ├── qjson_debug.h │ │ │ ├── qjson_export.h │ │ │ ├── qobjecthelper.cpp │ │ │ ├── qobjecthelper.h │ │ │ ├── serializer.cpp │ │ │ ├── serializer.h │ │ │ ├── serializerrunnable.cpp │ │ │ ├── serializerrunnable.h │ │ │ └── stack.hh │ │ └── tests/ │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── benchmarks/ │ │ │ ├── CMakeLists.txt │ │ │ ├── parsingbenchmark.cpp │ │ │ └── qlocalevsstrtod_l.cpp │ │ └── scanner/ │ │ ├── CMakeLists.txt │ │ └── testscanner.cpp │ └── qscintilla-2.8.4/ │ ├── CMakeLists.txt │ ├── QScintilla-2.9.3-xcode8.patch │ ├── README.md │ └── sources/ │ ├── GPL_EXCEPTION.TXT │ ├── GPL_EXCEPTION_ADDENDUM.TXT │ ├── LICENSE.GPL2 │ ├── LICENSE.GPL3 │ ├── NEWS │ ├── OPENSOURCE-NOTICE.TXT │ ├── Python/ │ │ ├── configure-old.py │ │ ├── configure.py │ │ └── sip/ │ │ ├── qsciabstractapis.sip │ │ ├── qsciapis.sip │ │ ├── qscicommand.sip │ │ ├── qscicommandset.sip │ │ ├── qscidocument.sip │ │ ├── qscilexer.sip │ │ ├── qscilexeravs.sip │ │ ├── qscilexerbash.sip │ │ ├── qscilexerbatch.sip │ │ ├── qscilexercmake.sip │ │ ├── qscilexercoffeescript.sip │ │ ├── qscilexercpp.sip │ │ ├── qscilexercsharp.sip │ │ ├── qscilexercss.sip │ │ ├── qscilexercustom.sip │ │ ├── qscilexerd.sip │ │ ├── qscilexerdiff.sip │ │ ├── qscilexerfortran.sip │ │ ├── qscilexerfortran77.sip │ │ ├── qscilexerhtml.sip │ │ ├── qscilexeridl.sip │ │ ├── qscilexerjava.sip │ │ ├── qscilexerjavascript.sip │ │ ├── qscilexerlua.sip │ │ ├── qscilexermakefile.sip │ │ ├── qscilexermatlab.sip │ │ ├── qscilexeroctave.sip │ │ ├── qscilexerpascal.sip │ │ ├── qscilexerperl.sip │ │ ├── qscilexerpo.sip │ │ ├── qscilexerpostscript.sip │ │ ├── qscilexerpov.sip │ │ ├── qscilexerproperties.sip │ │ ├── qscilexerpython.sip │ │ ├── qscilexerruby.sip │ │ ├── qscilexerspice.sip │ │ ├── qscilexersql.sip │ │ ├── qscilexertcl.sip │ │ ├── qscilexertex.sip │ │ ├── qscilexerverilog.sip │ │ ├── qscilexervhdl.sip │ │ ├── qscilexerxml.sip │ │ ├── qscilexeryaml.sip │ │ ├── qscimacro.sip │ │ ├── qscimod3.sip │ │ ├── qscimod4.sip │ │ ├── qscimod5.sip │ │ ├── qscimodcommon.sip │ │ ├── qsciprinter.sip │ │ ├── qsciscintilla.sip │ │ ├── qsciscintillabase3.sip │ │ ├── qsciscintillabase4.sip │ │ ├── qscistyle.sip │ │ └── qscistyledtext.sip │ ├── Qt3/ │ │ ├── InputMethod.cpp │ │ ├── ListBoxQt.cpp │ │ ├── ListBoxQt.h │ │ ├── MacPasteboardMime.cpp │ │ ├── PlatQt.cpp │ │ ├── Qsci/ │ │ │ ├── qsciabstractapis.h │ │ │ ├── qsciapis.h │ │ │ ├── qscicommand.h │ │ │ ├── qscicommandset.h │ │ │ ├── qscidocument.h │ │ │ ├── qsciglobal.h │ │ │ ├── qscilexer.h │ │ │ ├── qscilexeravs.h │ │ │ ├── qscilexerbash.h │ │ │ ├── qscilexerbatch.h │ │ │ ├── qscilexercmake.h │ │ │ ├── qscilexercoffeescript.h │ │ │ ├── qscilexercpp.h │ │ │ ├── qscilexercsharp.h │ │ │ ├── qscilexercss.h │ │ │ ├── qscilexercustom.h │ │ │ ├── qscilexerd.h │ │ │ ├── qscilexerdiff.h │ │ │ ├── qscilexerfortran.h │ │ │ ├── qscilexerfortran77.h │ │ │ ├── qscilexerhtml.h │ │ │ ├── qscilexeridl.h │ │ │ ├── qscilexerjava.h │ │ │ ├── qscilexerjavascript.h │ │ │ ├── qscilexerlua.h │ │ │ ├── qscilexermakefile.h │ │ │ ├── qscilexermatlab.h │ │ │ ├── qscilexeroctave.h │ │ │ ├── qscilexerpascal.h │ │ │ ├── qscilexerperl.h │ │ │ ├── qscilexerpo.h │ │ │ ├── qscilexerpostscript.h │ │ │ ├── qscilexerpov.h │ │ │ ├── qscilexerproperties.h │ │ │ ├── qscilexerpython.h │ │ │ ├── qscilexerruby.h │ │ │ ├── qscilexerspice.h │ │ │ ├── qscilexersql.h │ │ │ ├── qscilexertcl.h │ │ │ ├── qscilexertex.h │ │ │ ├── qscilexerverilog.h │ │ │ ├── qscilexervhdl.h │ │ │ ├── qscilexerxml.h │ │ │ ├── qscilexeryaml.h │ │ │ ├── qscimacro.h │ │ │ ├── qsciprinter.h │ │ │ ├── qsciscintilla.h │ │ │ ├── qsciscintillabase.h │ │ │ ├── qscistyle.h │ │ │ └── qscistyledtext.h │ │ ├── SciClasses.cpp │ │ ├── SciClasses.h │ │ ├── SciNamespace.h │ │ ├── ScintillaQt.cpp │ │ ├── ScintillaQt.h │ │ ├── qsciabstractapis.cpp │ │ ├── qsciapis.cpp │ │ ├── qscicommand.cpp │ │ ├── qscicommandset.cpp │ │ ├── qscidocument.cpp │ │ ├── qscilexer.cpp │ │ ├── qscilexeravs.cpp │ │ ├── qscilexerbash.cpp │ │ ├── qscilexerbatch.cpp │ │ ├── qscilexercmake.cpp │ │ ├── qscilexercoffeescript.cpp │ │ ├── qscilexercpp.cpp │ │ ├── qscilexercsharp.cpp │ │ ├── qscilexercss.cpp │ │ ├── qscilexercustom.cpp │ │ ├── qscilexerd.cpp │ │ ├── qscilexerdiff.cpp │ │ ├── qscilexerfortran.cpp │ │ ├── qscilexerfortran77.cpp │ │ ├── qscilexerhtml.cpp │ │ ├── qscilexeridl.cpp │ │ ├── qscilexerjava.cpp │ │ ├── qscilexerjavascript.cpp │ │ ├── qscilexerlua.cpp │ │ ├── qscilexermakefile.cpp │ │ ├── qscilexermatlab.cpp │ │ ├── qscilexeroctave.cpp │ │ ├── qscilexerpascal.cpp │ │ ├── qscilexerperl.cpp │ │ ├── qscilexerpo.cpp │ │ ├── qscilexerpostscript.cpp │ │ ├── qscilexerpov.cpp │ │ ├── qscilexerproperties.cpp │ │ ├── qscilexerpython.cpp │ │ ├── qscilexerruby.cpp │ │ ├── qscilexerspice.cpp │ │ ├── qscilexersql.cpp │ │ ├── qscilexertcl.cpp │ │ ├── qscilexertex.cpp │ │ ├── qscilexerverilog.cpp │ │ ├── qscilexervhdl.cpp │ │ ├── qscilexerxml.cpp │ │ ├── qscilexeryaml.cpp │ │ ├── qscimacro.cpp │ │ ├── qscintilla.pro │ │ ├── qscintilla_cs.qm │ │ ├── qscintilla_cs.ts │ │ ├── qscintilla_de.qm │ │ ├── qscintilla_de.ts │ │ ├── qscintilla_es.qm │ │ ├── qscintilla_es.ts │ │ ├── qscintilla_fr.qm │ │ ├── qscintilla_fr.ts │ │ ├── qscintilla_pt_br.qm │ │ ├── qscintilla_pt_br.ts │ │ ├── qsciprinter.cpp │ │ ├── qsciscintilla.cpp │ │ ├── qsciscintillabase.cpp │ │ ├── qscistyle.cpp │ │ └── qscistyledtext.cpp │ ├── Qt4Qt5/ │ │ ├── InputMethod.cpp │ │ ├── ListBoxQt.cpp │ │ ├── ListBoxQt.h │ │ ├── MacPasteboardMime.cpp │ │ ├── PlatQt.cpp │ │ ├── Qsci/ │ │ │ ├── qsciabstractapis.h │ │ │ ├── qsciapis.h │ │ │ ├── qscicommand.h │ │ │ ├── qscicommandset.h │ │ │ ├── qscidocument.h │ │ │ ├── qsciglobal.h │ │ │ ├── qscilexer.h │ │ │ ├── qscilexeravs.h │ │ │ ├── qscilexerbash.h │ │ │ ├── qscilexerbatch.h │ │ │ ├── qscilexercmake.h │ │ │ ├── qscilexercoffeescript.h │ │ │ ├── qscilexercpp.h │ │ │ ├── qscilexercsharp.h │ │ │ ├── qscilexercss.h │ │ │ ├── qscilexercustom.h │ │ │ ├── qscilexerd.h │ │ │ ├── qscilexerdiff.h │ │ │ ├── qscilexerfortran.h │ │ │ ├── qscilexerfortran77.h │ │ │ ├── qscilexerhtml.h │ │ │ ├── qscilexeridl.h │ │ │ ├── qscilexerjava.h │ │ │ ├── qscilexerjavascript.h │ │ │ ├── qscilexerlua.h │ │ │ ├── qscilexermakefile.h │ │ │ ├── qscilexermatlab.h │ │ │ ├── qscilexeroctave.h │ │ │ ├── qscilexerpascal.h │ │ │ ├── qscilexerperl.h │ │ │ ├── qscilexerpo.h │ │ │ ├── qscilexerpostscript.h │ │ │ ├── qscilexerpov.h │ │ │ ├── qscilexerproperties.h │ │ │ ├── qscilexerpython.h │ │ │ ├── qscilexerruby.h │ │ │ ├── qscilexerspice.h │ │ │ ├── qscilexersql.h │ │ │ ├── qscilexertcl.h │ │ │ ├── qscilexertex.h │ │ │ ├── qscilexerverilog.h │ │ │ ├── qscilexervhdl.h │ │ │ ├── qscilexerxml.h │ │ │ ├── qscilexeryaml.h │ │ │ ├── qscimacro.h │ │ │ ├── qsciprinter.h │ │ │ ├── qsciscintilla.h │ │ │ ├── qsciscintillabase.h │ │ │ ├── qscistyle.h │ │ │ └── qscistyledtext.h │ │ ├── SciClasses.cpp │ │ ├── SciClasses.h │ │ ├── SciNamespace.h │ │ ├── ScintillaQt.cpp │ │ ├── ScintillaQt.h │ │ ├── features/ │ │ │ └── qscintilla2.prf │ │ ├── qsciabstractapis.cpp │ │ ├── qsciapis.cpp │ │ ├── qscicommand.cpp │ │ ├── qscicommandset.cpp │ │ ├── qscidocument.cpp │ │ ├── qscilexer.cpp │ │ ├── qscilexeravs.cpp │ │ ├── qscilexerbash.cpp │ │ ├── qscilexerbatch.cpp │ │ ├── qscilexercmake.cpp │ │ ├── qscilexercoffeescript.cpp │ │ ├── qscilexercpp.cpp │ │ ├── qscilexercsharp.cpp │ │ ├── qscilexercss.cpp │ │ ├── qscilexercustom.cpp │ │ ├── qscilexerd.cpp │ │ ├── qscilexerdiff.cpp │ │ ├── qscilexerfortran.cpp │ │ ├── qscilexerfortran77.cpp │ │ ├── qscilexerhtml.cpp │ │ ├── qscilexeridl.cpp │ │ ├── qscilexerjava.cpp │ │ ├── qscilexerjavascript.cpp │ │ ├── qscilexerlua.cpp │ │ ├── qscilexermakefile.cpp │ │ ├── qscilexermatlab.cpp │ │ ├── qscilexeroctave.cpp │ │ ├── qscilexerpascal.cpp │ │ ├── qscilexerperl.cpp │ │ ├── qscilexerpo.cpp │ │ ├── qscilexerpostscript.cpp │ │ ├── qscilexerpov.cpp │ │ ├── qscilexerproperties.cpp │ │ ├── qscilexerpython.cpp │ │ ├── qscilexerruby.cpp │ │ ├── qscilexerspice.cpp │ │ ├── qscilexersql.cpp │ │ ├── qscilexertcl.cpp │ │ ├── qscilexertex.cpp │ │ ├── qscilexerverilog.cpp │ │ ├── qscilexervhdl.cpp │ │ ├── qscilexerxml.cpp │ │ ├── qscilexeryaml.cpp │ │ ├── qscimacro.cpp │ │ ├── qscintilla.pro │ │ ├── qscintilla_cs.qm │ │ ├── qscintilla_cs.ts │ │ ├── qscintilla_de.qm │ │ ├── qscintilla_de.ts │ │ ├── qscintilla_es.qm │ │ ├── qscintilla_es.ts │ │ ├── qscintilla_fr.qm │ │ ├── qscintilla_fr.ts │ │ ├── qscintilla_pt_br.qm │ │ ├── qscintilla_pt_br.ts │ │ ├── qsciprinter.cpp │ │ ├── qsciscintilla.cpp │ │ ├── qsciscintillabase.cpp │ │ ├── qscistyle.cpp │ │ └── qscistyledtext.cpp │ ├── README │ ├── designer-Qt3/ │ │ ├── designer.pro │ │ └── qscintillaplugin.cpp │ ├── designer-Qt4Qt5/ │ │ ├── designer.pro │ │ ├── qscintillaplugin.cpp │ │ └── qscintillaplugin.h │ ├── example-Qt3/ │ │ ├── application.cpp │ │ ├── application.h │ │ ├── application.pro │ │ ├── fileopen.xpm │ │ ├── fileprint.xpm │ │ ├── filesave.xpm │ │ └── main.cpp │ ├── example-Qt4Qt5/ │ │ ├── application.pro │ │ ├── application.qrc │ │ ├── main.cpp │ │ ├── mainwindow.cpp │ │ └── mainwindow.h │ ├── include/ │ │ ├── ILexer.h │ │ ├── License.txt │ │ ├── Platform.h │ │ ├── SciLexer.h │ │ ├── Scintilla.h │ │ ├── Scintilla.iface │ │ └── ScintillaWidget.h │ ├── lexers/ │ │ ├── LexA68k.cpp │ │ ├── LexAPDL.cpp │ │ ├── LexASY.cpp │ │ ├── LexAU3.cpp │ │ ├── LexAVE.cpp │ │ ├── LexAVS.cpp │ │ ├── LexAbaqus.cpp │ │ ├── LexAda.cpp │ │ ├── LexAsm.cpp │ │ ├── LexAsn1.cpp │ │ ├── LexBaan.cpp │ │ ├── LexBash.cpp │ │ ├── LexBasic.cpp │ │ ├── LexBullant.cpp │ │ ├── LexCLW.cpp │ │ ├── LexCOBOL.cpp │ │ ├── LexCPP.cpp │ │ ├── LexCSS.cpp │ │ ├── LexCaml.cpp │ │ ├── LexCmake.cpp │ │ ├── LexCoffeeScript.cpp │ │ ├── LexConf.cpp │ │ ├── LexCrontab.cpp │ │ ├── LexCsound.cpp │ │ ├── LexD.cpp │ │ ├── LexECL.cpp │ │ ├── LexEScript.cpp │ │ ├── LexEiffel.cpp │ │ ├── LexErlang.cpp │ │ ├── LexFlagship.cpp │ │ ├── LexForth.cpp │ │ ├── LexFortran.cpp │ │ ├── LexGAP.cpp │ │ ├── LexGui4Cli.cpp │ │ ├── LexHTML.cpp │ │ ├── LexHaskell.cpp │ │ ├── LexInno.cpp │ │ ├── LexKVIrc.cpp │ │ ├── LexKix.cpp │ │ ├── LexLaTeX.cpp │ │ ├── LexLisp.cpp │ │ ├── LexLout.cpp │ │ ├── LexLua.cpp │ │ ├── LexMMIXAL.cpp │ │ ├── LexMPT.cpp │ │ ├── LexMSSQL.cpp │ │ ├── LexMagik.cpp │ │ ├── LexMarkdown.cpp │ │ ├── LexMatlab.cpp │ │ ├── LexMetapost.cpp │ │ ├── LexModula.cpp │ │ ├── LexMySQL.cpp │ │ ├── LexNimrod.cpp │ │ ├── LexNsis.cpp │ │ ├── LexOScript.cpp │ │ ├── LexOpal.cpp │ │ ├── LexOthers.cpp │ │ ├── LexPB.cpp │ │ ├── LexPLM.cpp │ │ ├── LexPO.cpp │ │ ├── LexPOV.cpp │ │ ├── LexPS.cpp │ │ ├── LexPascal.cpp │ │ ├── LexPerl.cpp │ │ ├── LexPowerPro.cpp │ │ ├── LexPowerShell.cpp │ │ ├── LexProgress.cpp │ │ ├── LexPython.cpp │ │ ├── LexR.cpp │ │ ├── LexRebol.cpp │ │ ├── LexRuby.cpp │ │ ├── LexRust.cpp │ │ ├── LexSML.cpp │ │ ├── LexSQL.cpp │ │ ├── LexSTTXT.cpp │ │ ├── LexScriptol.cpp │ │ ├── LexSmalltalk.cpp │ │ ├── LexSorcus.cpp │ │ ├── LexSpecman.cpp │ │ ├── LexSpice.cpp │ │ ├── LexTACL.cpp │ │ ├── LexTADS3.cpp │ │ ├── LexTAL.cpp │ │ ├── LexTCL.cpp │ │ ├── LexTCMD.cpp │ │ ├── LexTeX.cpp │ │ ├── LexTxt2tags.cpp │ │ ├── LexVB.cpp │ │ ├── LexVHDL.cpp │ │ ├── LexVerilog.cpp │ │ ├── LexVisualProlog.cpp │ │ ├── LexYAML.cpp │ │ └── License.txt │ ├── lexlib/ │ │ ├── Accessor.cpp │ │ ├── Accessor.h │ │ ├── CharacterCategory.cpp │ │ ├── CharacterCategory.h │ │ ├── CharacterSet.cpp │ │ ├── CharacterSet.h │ │ ├── LexAccessor.h │ │ ├── LexerBase.cpp │ │ ├── LexerBase.h │ │ ├── LexerModule.cpp │ │ ├── LexerModule.h │ │ ├── LexerNoExceptions.cpp │ │ ├── LexerNoExceptions.h │ │ ├── LexerSimple.cpp │ │ ├── LexerSimple.h │ │ ├── License.txt │ │ ├── OptionSet.h │ │ ├── PropSetSimple.cpp │ │ ├── PropSetSimple.h │ │ ├── SparseState.h │ │ ├── StyleContext.cpp │ │ ├── StyleContext.h │ │ ├── SubStyles.h │ │ ├── WordList.cpp │ │ └── WordList.h │ ├── qsci/ │ │ └── api/ │ │ └── python/ │ │ ├── Python-2.4.api │ │ ├── Python-2.5.api │ │ ├── Python-2.6.api │ │ ├── Python-2.7.api │ │ ├── Python-3.1.api │ │ ├── Python-3.2.api │ │ ├── Python-3.3.api │ │ └── Python-3.4.api │ └── src/ │ ├── AutoComplete.cpp │ ├── AutoComplete.h │ ├── CallTip.cpp │ ├── CallTip.h │ ├── CaseConvert.cpp │ ├── CaseConvert.h │ ├── CaseFolder.cpp │ ├── CaseFolder.h │ ├── Catalogue.cpp │ ├── Catalogue.h │ ├── CellBuffer.cpp │ ├── CellBuffer.h │ ├── CharClassify.cpp │ ├── CharClassify.h │ ├── ContractionState.cpp │ ├── ContractionState.h │ ├── Decoration.cpp │ ├── Decoration.h │ ├── Document.cpp │ ├── Document.h │ ├── Editor.cpp │ ├── Editor.h │ ├── ExternalLexer.cpp │ ├── ExternalLexer.h │ ├── FontQuality.h │ ├── Indicator.cpp │ ├── Indicator.h │ ├── KeyMap.cpp │ ├── KeyMap.h │ ├── License.txt │ ├── LineMarker.cpp │ ├── LineMarker.h │ ├── Partitioning.h │ ├── PerLine.cpp │ ├── PerLine.h │ ├── PositionCache.cpp │ ├── PositionCache.h │ ├── RESearch.cpp │ ├── RESearch.h │ ├── RunStyles.cpp │ ├── RunStyles.h │ ├── SciTE.properties │ ├── ScintillaBase.cpp │ ├── ScintillaBase.h │ ├── Selection.cpp │ ├── Selection.h │ ├── SplitVector.h │ ├── Style.cpp │ ├── Style.h │ ├── UniConversion.cpp │ ├── UniConversion.h │ ├── UnicodeFromUTF8.h │ ├── ViewStyle.cpp │ ├── ViewStyle.h │ ├── XPM.cpp │ └── XPM.h └── static_analysis/ ├── clang-tidy_debug_Aug2020.txt ├── clang-tidy_debug_Aug2020_robo.txt ├── cppcheck_output_Apr2019.txt ├── scan-build_debug_Aug2020.txt ├── scan-build_debug_Aug2020_robo.txt ├── vs_run-code-analysis_all_rules_rls.txt ├── vs_run-code-analysis_nat-rec_dbg.txt ├── vs_run-code-analysis_nat-rec_rls-reduced.txt ├── vs_run-code-analysis_nat-rec_rls.txt └── vs_run-code-analysis_no-rule_rls.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto # Explicitly declaring text files we want to always be normalized and converted # to native line endings on checkout. *.cs text *.java text *.html text *.css text *.js text *.sql text *.cpp text *.h text *.rm text *.pro text *.pri text *.txt text *.md text # Declare files that will always have CRLF line endings (Windows) on checkout. *.bat text eol=crlf # Declare files that will always have LF line endings (Unix) on checkout. *.sh text eol=lf # # Binary files # *.png binary *.so binary *.jpg binary *.gif binary *.dll binary *.exe binary *.doc binary *.docx binary *.lib binary ================================================ FILE: .gitignore ================================================ # Robomongo-specific CMakeLists.txt.user* .sconsign.dblite .sconf_temp .scons *.kate-swp *.swp target/ target-debug/ target-release/ libs/ /build /build2 /src/robomongo/robomongo.pro.user.1.3 .idea/ # Created by http://www.gitignore.io ### C++ ### # Compiled Object files *.slo *.lo *.o *.obj # Compiled Dynamic libraries *.so *.so.* *.dylib *.dll # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app ### Linux ### *~ # KDE directory preferences .directory ### OSX ### .DS_Store .AppleDouble .LSOverride ### Qt ### # C++ objects and libs *.slo *.lo *.o *.a *.la *.lai *.so *.dll *.dylib # Qt-es /.qmake.cache /.qmake.stash *.pro.user *.pro.user.* *.moc # QtCreator *.autosave *.creator.user ### Windows ### # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ .vscode/settings.json .vs/ ================================================ FILE: .travis.yml ================================================ os: - linux # - osx # - windows dist: xenial sudo: require language: cpp script: - bash bin/clean - bash bin/configure # - bash bin/build # - bash bin/run-unit-tests # end of file ================================================ FILE: CHANGELOG ================================================ Download latest version: https://robomongo.org/download Robo 3T 1.4.4 (Aug/2021) New Features: - Support for Standard Connection String Format (Mongo URI) Fixes: - Fix Welcome Tab not loading the new content issue by disable/bypass HTTP cache Robo 3T 1.4.3 (Jan/2021) New Features: - macOS Big Sur support - New Welcome Tab - embeds Chromium using QtWebEngine (Windows, macOS only) - Database explorer section has smaller default width (#1556) - Remember size of database explorer section - Import keys from old version: autoExpand, lineNumbers, debugMode and shellTimeoutSec Fixes: - Disable unsupported SSH tab for replica set connections (#1285 #1340) - Fix crash when new shell tab executed in server unreachable case - Fix crash when paging used in tabbed result window (#1661) - Fix broken F2, F3, F4 shortcuts for tabbed result view - One time re-order limit per new connections window to prevent data loss (macOS, #1790) - Fix previously broken IPv6 support from command line: robo3t --ipv6 Robo 3T 1.4.2 (Oct/2020) Fixes: - Fix broken paging in DocumentDB (#1694) Robo 3T 1.4.1 (Sep/2020) Fixes: - Force light mode on macOS (#1761, #1768, #1771) - Fix broken macOS native theme/look (#1766) - Fix broken Windows native theme/look Robo 3T 1.4.0 (Aug/2020) blog.robomongo.org/robo-3t-1-4/ New Features: - Mongo shell 4.2 upgrade - Support for Ubuntu 20.04 and macOS 10.15 (Catalina) - SSH: ECDSA and Ed25519 keys support on Windows & macOS (#1719, #1530, #1590) - Manually specify visible databases (#1696, #1368, #389) Improvements: - Qt Upgrade (v5.12.8 - Apr/2020, Windows & macOS only) - OpenSSL upgrade (v1.1.1f - Mar/2020, Windows & macOS only) - libssh2 upgrade (v1.9.0 - Jun/2019, Windows & macOS only) Fixes: - Authentication database option isn't used properly (#1696) - Add/Edit index ops fixed (re-written) (#1692) - Crash when expanding admin users (#1728) - Unable to run query after shell timeout reached (#1529) Robo 3T 1.3.1 (Apr, 2019) http://blog.robomongo.org/robo-3t-1-3 New Features: - Biggest change in this version is Mongo Shell version upgrade from 3.4 to 4.0.5. - Support for importing from DNS SRV based MongoDB connection strings - Query results windows now supports tabbed output #1591 #1403 - Adding support for SCRAM-SHA-256 auth mechanism #1569 - Support for Ubuntu 18.04 and Mac 10.14. - Support for creating version 4 UUID #1554 Improvements: - Encrypted passwords are used in config. file instead of clear text passwords - Showing much better error reason string from Mongo drivers for connection failures Fixes: - Broken create/edit/view user features fixed and updated #638 #1041 - Attempt to fix #1547: Crash when right click on existing results plus a new long running query - Fix for application crash when adding index with invalid JSON - 'Repair Database' in Robo3T needs a confirm dialog box #1568 - Robo 3T the input space causes connection failure #1551 - Crash when loading results + right click #1547 - Attempt to fix major connection problems seen with the second edit/add/remove document operation after MongoDB 4.0 upgrade #1603 - Attempt to fix #1581: For CRUD ops showing progress bar and disabling context menu while waiting for edit op to finish - Fixing UI issue where Functions folder freezing with "Functions..." when fails to refresh - Fix for #1529: Pagination does not work when the aggregation queries have dotted fields Robo 3T 1.3.0 Beta (16 Feb, 2019) Details: https://github.com/Studio3T/robomongo/releases/tag/v1.3.0-beta Robo 3T 1.2.1 (19 Feb, 2018) http://blog.robomongo.org/robo-3t-1-2 Fixes: - Aggregate query results are not pagaeble - Big double numbers are showing incorrectly #1447 - Editing a document from a projection causes data loss (override) (#881) - Error: Resources temporarily unavailable. Error when starting up SSH session: -8 #1426 - robo3t 1.1 failed to start on Ubuntu 17.04 #1385 - Robo 3T is not opening on MacOS High Sierra (#1412) - Cannot load list of indexes #1454 - Robo 3T is not buildable on latest MacOS High Sierra Improvements - Qt version upgraded from Qt 5.7.0 to Qt 5.9.3 - Auto switch to HTTP from HTTPS if the program crashes due to HTTPS related operations - Improvements for Robo 3T is crashing constantly on High Sierra #1412 - PR: Improved query time formatting (#1358) #1455 Robo 3T 1.2.0 Beta (18 Dec, 2017) https://github.com/Studio3T/robomongo/releases/tag/v1.2.0-beta Improvements & Fixes: - Qt version upgraded from Qt 5.7.0 to Qt 5.9.3 - Fix for "Cannot load list of indexes #1454" - Improvements for Robo 3T is crashing constantly on High Sierra #1412 - PR: Improved query time formatting (#1358) #1455 - Fix for "Robo 3T is not buildable on latest MacOS vesion High Sierra" - Fix attempt for "Error: Resources temporarily unavailable. Error when starting up SSH session: -8 #1426" - Fix for "robo3t 1.1 failed to start on Ubuntu 17.04 #1385" Robo 3T 1.1 (13 June, 2017) http://blog.robomongo.org/robomongo-1-1/ New Features: - MongoDB 3.4 support - New NumberDecimal (Decimal128) data type support - ECMAScript 2015 aka ES6 Support (Modernized JavaScript Implementation) - Re-naming from Robomongo to Robo 3T Improvements & Fixes: - Stability Improvements: Fix included to prevent a crash on MACOS Sierra - Shell timeout is configurable on UI now 'Options->Change Shell Timeout' - Fix for Robomongo Shell Timeout Issue (silently, prematurely finishing long lasting scripts) - Security Improvements: OpenSSL version upgrade to openssl-1.0.1u (22-Sep-2016) - Tool chain upgrades: Modern C++14 features are enabled and usable for developers Robomongo 1.1 Beta (12 May, 2017) https://github.com/Studio3T/robomongo/releases/tag/v1.1.0-beta New Features: - MongoDB 3.4 Support - New NumberDecimal (Decimal128) data type support - ECMAScript 2015 (Modernized JavaScript Implementation aka ES6) Support Improvements: - Security Improvements: OpenSSL version upgrade to openssl-1.0.1u (22-Sep-2016) - Tool Chain Improvements: Modern C++14 features are enabled Robomongo 1.0 (21 Apr, 2017) http://blog.robomongo.org/robomongo-1-0 New Features: - Update Notifications Bar - Welcome Tab - Welcome Wizard Improvements: - Replica set name configurable on UI - Configuration file system location change - "About Robomongo" dialog changes Fixes: - Fix to avoid the same set name problem (https://github.com/Studio3T/robomongo/issues/1302) - Timestamp value is represented incorrectly (https://github.com/Studio3T/robomongo/issues/1159) - Local Timezone shows wrong date (https://github.com/Studio3T/robomongo/issues/501) - Time zone shown as "000:00" when UTC +00:00 is used as local timezone(https://github.com/Studio3T/robomongo/issues/1309) Robomongo 1.0-RC1 (1 Feb, 2017) http://blog.robomongo.org/robomongo-1-rc1/ New Features: - Replica set support. - New 'Attributes' column in connections list window showing connection properties such as 'Replica Set', 'SSL' and 'SSH'. - New operation logging for almost all operations. Improvements: - Improved error handling (no double error windows) for all fail and success conditions for the following operations: . Create, Drop Database . Create, Edit User . Create, Drop, Duplicate, Rename Collection . Insert Document . Remove document, Delete document, Remove all documents (triggered both from explorer and from result widget) . Refresh Users . Refresh Functions . Refresh Collections . Connection establish & refresh server fail and success cases. - Windows size is saved and restored for Edit, View and Insert Document dialogs. - Connections are automatically imported from the latest Robomongo version. - Better error reporting/handling and logging have been added for the operations below: . Create, Drop Function . Drop User - More informative and adjusted log level prefix strings haven added for log window. (Error, Warn, Info etc..). - After a new connection is created, now the focus goes to the newly created connection (end of list) instead of the beginning of connections list. And when Robomongo is opened, the focus goes to the end of the connection list instead of beginning. - Error messages added and error handling for 'remove document', 'remove all documents' operations triggered explorer and single or multi 'delete document' from results window have been re-implemented and improved. - Relevant errors are shown for the following cases now: . Drop attempt of non-existing collection. . Duplicating a collection with a collection name which already exists finishes silently. Fixes: - Fix for the problem where the connections list comes empty if the Robomongo config file or it's directory do not exist. - Fix for the problem where 'in-progress bar' widget was not centered on main window for "Update Documents/Remove Documents" operations. https://github.com/paralect/robomongo/issues/1256 - Fix for two problems where the operations 'insert or update documents' in one tab was causing auto reloading of other unrelated tabs and also multiple error windows to popup. https://github.com/paralect/robomongo/issues/1236 - Fix for the problem where the operations 'remove document', 'remove all documents' triggered explorer and single or multi 'delete document' from results window were causing auto reloading of other unrelated tabs. Robomongo 0.9: Major Features: - Support for MongoDB 3.x and WiredTiger engine. Robomongo now embeds MongoDB 3.2 shell. (Ability to use the latest shell functionality) - SSH tunnel support for connections - SSL support - MongoDB Atlas (Cloud MongoDB) support - Both MONGODB-CR and SCRAM-SHA-1 authentication mechanisms are supported. - Allow direct connections to Replica Set members and support IPv6 protocol. - Loading of huge list of collections is now faster. - Create Collection dialog is now expandable to support all advanced options. (Supporting all major mongoDB versions 3.2, 3.0, 2.6 and all storage engine types (WiredTiger and MMAPV1) is added.) - Windows High DPI support. - Full size editor with dockable output window. Fixed: - Incorrect representation of floating-point numbers (fixed since RC3). This bug was a reason of a big number of issues: #910, #748, #697, #699, #760, #815, #870, #892, #894, #905, #945, #974, #978, #990, #831, #842 - SSH connection failure to servers on Ubuntu and OS X with the following error: "Resource temporarily unavailable. Error when starting up SSH session: -8" (#1090) What's new in 0.9.0 Final (30 Sep, 2016) http://blog.robomongo.org/robomongo-0-9-0-final/ New Features: - Full size editor with dockable output window. - New JSON object context menu items: "Copy Name" and "Copy Path" are added. - New database context menu items: "Current Operations" and "Kill Operation" are added. - Minimize to Tray Icon for Windows. Other Changes - Cloud platforms Compose, mLab and Amazon EC2 were tested and added as supported platforms. - Maximize/Restore result button behavior and icon have been improved. - For better code quality and code reviews, Robomongo Code Quality and Robomongo Code Review pages have been created. https://github.com/paralect/robomongo/wiki/Robomongo-Code-Review https://github.com/paralect/robomongo/wiki/Robomongo-Code-Quality What's new in RC10 (Aug 8, 2016) http://blog.robomongo.org/robomongo-rc10/ New Features: - SSL connections with advanced options are now supported. - MongoDB Atlas (Cloud MongoDB) is supported. - Cross Platform High DPI enhancements. Enhancements: - For better security, OpenSSL has been upgraded to OpenSSL 1.0.1p. - Qt has been updated from Qt 5.5 to latest Qt 5.7. With this update there are many enhancements including cross platform High DPI support. What's new in RC9 (May 30, 2016) http://blog.robomongo.org/robomongo-rc9/ New Features: - Create Collection dialog with advanced options. - New feature has been added to save/restore window sizes. (#1102) - Connection settings from previous versions of Robomongo are now automatically imported starting from Robomongo 0.9.0 RC9. (#1086) - Support for querying documents by UUIDs is added, both in legacy and new encoding. (#432) Fixed: - SSH connection failure to servers on Ubuntu and OS X with the following error: "Resource temporarily unavailable. Error when starting up SSH session: -8" (#1090) - Correct handling of create/drop of databases and collections. (#1083) What's new in RC8 (April 14, 2016) http://blog.robomongo.org/robomongo-rc8/ What's new in RC7 (March 4, 2016) http://blog.robomongo.org/robomongo-rc7 What's new in RC6 (February 13, 2016) http://blog.robomongo.org/ipv6-protocol What's new in RC5 (February 8, 2016) http://blog.robomongo.org/campaign-is-over-robomongo-is-not/ What's new in RC4 (February 1, 2016) http://blog.robomongo.org/robomongo-rc4 What's new in RC3 (January 29, 2016) http://blog.robomongo.org/robomongo-0-9-0-rc3-released What's new in RC2 (January 26, 2016) http://blog.robomongo.org/robomongo-rc2-for-windows-mac-os-x-and-linux What's new in RC1 (January 17, 2016) http://blog.robomongo.org/robomongo-rc1-with-full-support-for-mongodb-3 0.8.5 / March 10, 2015 Release 0.8.5 is now deprecated because of a critical bug, that is described here: http://blog.robomongo.org/robomongo-0-9-0-rc3-released We recommend to use Robomongo 0.9.0 or newer. * [Stephen Steneker] - OS X build fixes for XCode 5 / libc++ (#524) - OS X specific keyboard shortcut tips (#505) - View documents fail if collection name is "group" (#414) - Problem with slash in collection names (#404) - Add shortcut to open JS file (#408) - Fullscreen mode not working on OS X (#519) - Usability request - add initial curly braces to command bar (#506) - TTL index should allow expiry of 0 seconds (#500) - Add line numbers to JSON editing windows on OS X (#449) - Add user preference to have line numbers displayed by default (#564) - Space after document causes validation error (#544) - Expand current server as soon as you connect to it (#405) - Switching tabs using standard keys on OS X (#87) - Cannot make the Edit Document Window smaller (#548) - Consistent numbering for tree/table/text view items (#421) - Reload Query with keyboard shortcut doesn't work (#551) - Rename "Reload" to "Re-execute query" (#447) - Add "Copy Timestamp from ObjectId" to context menu for ObjectId fields (#559) - Bug: Type of Double changed to Int32 on save document (#622) - "Duplicate Query In New Tab": add to Window menu with keyboard shortcut (#631) - No errors logged if settings directory cannot be created or settings file cannot be written to (#636) - Show execution time for operations that don't return results (#455) - Disable broken "Copy Collection to Database" misfeature (#398) * [Vladimir Belozyorov] - View mode reset to default after no results (#509) - Comment / uncomment selected code in the shell (#467) - Passphrase and password should be hidden in authentication tab (#433) - Cursor initialization - place between curly brackets for default find({}) query (#592) - Include full date/time for log entries (#596) - Option to disable selected code autoexecution when opening a new tab (#514) - Tooltip fixes to match current keyboard shortcuts (#598) - Expand/collapse multiple selections (#591) - Changes in toolbars visibility are saved in config file (#457) - Include connection name under shell tabs (#47) * [Sergey Gavruk] - SettingsManager typo (#558) - Ability to disable autocompletion (#512) * [Kanstantsin Kamkou] - Increase display width for limit & offset input fields (#458) * [volans-] - CreateUserDialog.cpp: fixed typo in warning message * [Andres Kievsky] - Added new app icon in a variety of png and iDraw formats (#693) * [Michael Steinacher] - Retina text display support (#724) 0.8.4 / November 27, 2013 Read about updates in this version here: http://robomongo.org/whats-new-in/robomongo-0.8.4.html RC1: * SSH support (#117) * SSL support (first steps) (#100) * Create or edit index problem (#341) * Regexp error (with non-latin symbols) (#351) * Support for latin characters in edit document console (#347) * Robomongo fails to execute shell "comments" if the comment contains a non-latin character! (#334) * Crash when add ISODate field (#350) * Crash at executing several disconnections in a row (#290) * Deleting multiple documents prompts for each document (#386) * Connection context menu appears independently of position of Robomongo instance. * Crash at pressing "Ctrl+T" without any connection. RC2: * SSH now works on Mac OS X Mavericks (#401) * Fixed crash when parsing date (#356) Final: * Selected view mode of result panels now preserved between executions. Implemented by Vladimir Belozyorov (github.com/VBelozyorov) (#402) 0.8.3 / October 1, 2013 Read about updates in this version here: http://robomongo.org/whats-new-in/robomongo-0.8.3.html * Enable query logging (#108) * Inserting multiple documents from UI (#173) * Removing multiple documents from UI (#174) * Retain current view mode when running search (#93, #79) * Left panel collections/functions/users navigation doesn't work when authenticating against the DB (#282) * A way to disable Alt + number shortcut (#192) * Support mongodb 2.4.x style users (#139) * Add "Copy JSON" menu item to context menu (#298) * JSON list validation (#246) * Crash creating new Database (#291) * Incorrect handling of TTL Indexes (#313) * Add the document _id in the tree view (#142) * BSON Undefined type doesn't rendered to string correctly (#306) * Entering incorrect symbols as database name isn't handled properly (#262) * Ability to select UI theme * We now show number of fields (inside objects) and number of items (inside arrays) in Tree View. * Support for Mac OS X 10.6 (#287) * Support for Mac OS X 10.9 (#254) 0.8.2 / September 13, 2013 Read about updates in this version here: http://robomongo.org/whats-new-in/robomongo-0.8.2.html 0.8.1 / September 6, 2013 Read about updates in this version here: http://robomongo.org/whats-new-in/robomongo-0.8.1.html * Table Mode (in this version, only for the first-level fields in documents) (#72) * Paging Widget tweaks (#247, #203, #244, #243) * ~/.robomongorc.js file is loaded automatically when new shell is opened (c:\Users\Name\.robomongorc.js for Windows) * "Load .mongorc.js" menu item in "Options" top-level now allow to enable/disable loading of ~/.mongorc.js file. By default it will not load this file. * Batch Size now configurable (in this version only manually via ~/.config/robomongo.json) * JSON parser doesn't correctly parse numbers when locale isn't en_EN (#246, #253) * "Display Dates In" options doesn't saved/loaded correctly (#261) * [Linux] Support for CentOS * [Linux] Linux packages now takes 2x less space (10mb instead of 20mb) * [Windows] Desktop icon isn't created despite corresponding checkbox was checked at installation. (#260) * [Windows] Invalid symbols in installer for Windows if display language of system isn't English (#259) 0.8.0 / August 23, 2013 Read about updates in this version here: http://robomongo.org/whats-new-in/robomongo-0.8.0.html * Ability to save and load files for currently opened shell (#96) * MongoDB Index management (#74) * Expand all children recursively (#106) * Ability to toggle line numbers (press F11) for any editor in Robomongo (#112) * View Mode settings is persisted now (thanks, Luke Thompson!) (#114) * Search capability for any editor in Robomongo - press (Ctrl + F) (#32) * Timezone support: UTC or Local (#224) * Auto-complete on "Tab" (#95) * Fixed incorrect handling of Dates (#111) * Fixed incorrect handling of collections with leading underscore (_) symbol (#109) * Fixed incorrect rendering of float numbers (#115) * Fixed incorrect handling of Int64 numbers (#157) * Fixed incorrect parsing of DBRef sub-objects (#113) * New build system, based on CMake. Greatly simplified build and package process. * Qt updated to 5.1 version. * QScintilla updated to 2.7.2 version. 0.7.1 / April 13, 2013 * Fixed crash when right-clicking on non-primitive elements in Tree View (#85) * Alt + Cmd + Arrow for switching tabs on Mac (#82) * Several minor bugs fixed (including #82) 0.7.0 / April 4, 2013 * Support for UUIDs (read more: http://robomongo.org/articles/uuids.html) 0.6.9 / April 3, 2013 * Password is hidden by default (#65) * Fixed incorrect rendering of large values for the db.stats() results (#70) * Collection names with invalid (for JS var name) characters now rendered correctly (#71) 0.6.8 / April 1, 2013 * Support for '~/.mongorc.js' * Various Mac OS X UI fixes * Fixed crash when pressing Ctrl + W (#60) * "Copy Value" context menu item for documents in Tree View 0.6.7 / March 26, 2013 * Fixed creation of new tabs based on the current one. Before this fix initial database for new tab wasn't identical to the "parent". (#56) * Support for collections that starts from digits (#54) * [Sergey Gavruk] Duplicate Collection feature (Collection -> Duplicate Collection * Collection -> Reindex will not run immediately, as before * Fixed incorrect disposing of tabs resources (connections etc.) (#42) * [Stephen Steneker] Fixed KeepAlive invalid commands, when not authorised (#41) * [Sergey Gavruk] Various Mac OS X UI fixes. 0.6.6 / March 23, 2013 * Fixed possible crash when viewing results in text mode (#34) * Fixed possible crash when testing connection (Diagnostic window) * Memory leak in explorer's database tree fixed. 0.6.5 / March 17, 2013 * Fixed "Dropped connection" issue (#28). * Windows Installer now be used to destribute Robomongo. 0.6.4 / March 10, 2013 * Support for JS functions. Create/Edit/Remove/View. * Loading indicators (three dots for now) for Server, Collections, Functions and Users. * Count indicators for Server (number of databases), Collections, Functions and Users. * No need to type "functionName.toString()". You can output source of function by simply typing its name (like in the shell). * Support for RegEx, Code and CodeWScope bson elements. But you still cannot edit documents which contains Code or CodeWScope bson elements. * About dialog added. 0.6.3 / March 5, 2013 * Script execution can be stopped (this works only for JS code in SpiderMonkey VM) (#23) * Toolbar buttons shown only if needed (#21, #20) * MongoDB Connections window opens when application starts. * Some changes in menu items locations (#25, #26) * View Mode buttons moved to Options menu. * Full Screen and Logs functionality hidden right now (postponed for versions after Robomongo 1.0) 0.6.2 / March 4, 2013 * "Users" folder now shows list of users. * User can be added (Database -> Add User) * User can be edited (User -> Edit User) * User can be removed (User -> Remove Users) * Users can be refreshed (Users (folder) -> Refresh Users) * List of users can be viewed in the shell (Users (folder) -> View Users) 0.6.1 / March 2, 2013 * Minor script editor and viewer tweaks 0.6.0 / Feb 28, 2013 * Database can be created from UI (Server -> Create Database) * Database can be dropped from UI (Database -> Drop Database) * Collection can be created from UI (Database -> Create Collection) * Collection can be dropped from UI (Collection -> Drop Collection) * Collection can be renamed from UI (Collection -> Rename Collection) * All documents can be removed from UI (Collection -> Remove All Documents) * Minor tweaks in naming of context menu items. 0.5.7 / Feb 27, 2013 * Support for Custom View Modes. First custom view implemented for "db.printCollectionStats()" query. You still can view multiple output results, even if one of them presented in custom mode. * "Show Log" context menu item for servers in Explorer. 0.5.6 / Feb 26, 2013 * Support for one-line MongoDB commands ("show dbs", "show collection", "use somedb" etc.). This commands can be repearted (this will result in multiple output results) or they can be used together with ordinary JavaScript. The only rule: this command should be the only text on the line, starting from the very beginning of the line. * Refresh of databases and collections via "Refresh" context menu item. * This version of Robomongo is the first version compiled in release mode. It works much more faster and uses lower amount of memory. 0.5.5 / Feb 22, 2013 * New document can be added in a more user friendly way. There are two ways to do it: 1) Right-click on collection in Explorer and select "Insert Document" 2) Right-click at any place of Tree View and select "Insert Document" * Better formatting of JSON with correct indention. * Support for ISODate(...) factory function in JSON parser and builder. Date in JSON mode now always displayed as ISODate(...), like in the shell. Document Editor accepts dates in two forms: as "new Date()" and "ISODate()" (this behaviour identical to MongoDB shell behaviour) * Document editor implemented. Right-click on document in Tree View and select "Edit". * Document viwer implemented. Right-click on document in Tree View and select "View". * You can delete selected document in Tree View. (Right-click and select "Delete") * Validation of JSON in Document Editor/Viewer with error report, highlighting of errors and cursor positioning just where error is. * Fixed problem with "signed" dates (dates before Unix Epoch) (#5) * Fixed problem with UTF-8 (#17) * Fixed problem with loading indicator in Explorer (#18) 0.5.2 / Jan 26, 2013 In this version autocompletion in Robomongo becomes mostly usable. * AutoCompletion box now placed just near text you typed (in correct line and column) * AutoCompletion now works in the middle of the text (not only at the end of line, as before) * Ctrl + Space will trigger autocompletion for symbols under cursor. * If you select function in autocompletion box, their open parenthesis will overwrite existing in the text, if such available. 0.5.1 / Jan 24, 2013 * Several autocompletion tweaks. 0.5.0 / Jan 24, 2013 * AutoCompletion. First version. * Progress indicator for shells. * Shell result panels now have equal size, if this is possible. * Code cleanuped a bit. 0.4.6 / Jan 11, 2013 * Tooltip for collections with count and size. 0.4.5 / Jan 8, 2013 * WARNING: configuration file schema changed. Some properties will be empty in Connection Settings Dialog. Please fill them again. * Connection test (diagnostic) implemented. * Indicators in the shell: current server, database and collection (if available) This indicators are "real" - they respect your actual query. * Execution time measurement. Displayed for each individual output result. * Paging for each output result with respect to filtering, ordering, fields, limit, skip etc. of actual text query (pilot version). * Connection cloning support in Connections Dialog. * Databases and collections now displayed in sorted order. * Batch size extended to 50 documents. * New shape of Shells. * New shape of Connection Settings Dialog * New shape of Connections Dialog. 0.4.0 / Dec 26, 2012 * No more "type 'it' for more results" when we have more than 20 objects in collection. * Two global modes added - text and tree modes. Switch between them using F3 and F4. * Two local modes added - text and tree modes. Switch between them using small icons on Output panels for each resultset. * Improved performance of json creation (from list of documents). * Tooltips for tool buttons added. * Maximization of output panel implemented (via small icon at the top right of output panel) Click second time to restore back. * Fix for not working Ctrl-O key shortcut (that opens Connections menu). 0.3.5 / Dec 25, 2012 * Context menu for server, database and collection. * Tab management (Ctrl+Tab, Ctrl+Shift+Tab, Ctrl+T, Ctrl+F4) * Context menu for tab * Submenu for Connect button with connections. * Hotkeys for first 9 connections (Alt+1, ..., Alt+9) * Connections reordering via drag'n'drop in Connections dialog. * Correct focus placement when opening new connection and switching tabs. * Bonus: F12 - opens connections menu from any place. * Memory leaks fix. ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.8.0) project(Robomongo) # Where to look first for CMake modules, before "${CMAKE_ROOT}/Modules" is checked list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") # Project version set(PROJECT_VERSION_MAJOR "1") set(PROJECT_VERSION_MINOR "4") set(PROJECT_VERSION_PATCH "4") set(PROJECT_VERSION_BUILD "") set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${PROJECT_VERSION_BUILD}") # Enable C++17 features set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Modules include(RobomongoPrintUtils) include(RobomongoCMakeDefaults) include(RobomongoDefaults) include(RobomongoCommon) include(RobomongoTrashSymbols) include(RobomongoTargetArch) include(RobomongoInstallQt) include(RobomongoPackage) # Search for dependencies find_package(Threading REQUIRED) # Wrapper arround CMake "Threads" module find_package(Qt5Core REQUIRED) find_package(Qt5Gui REQUIRED) find_package(Qt5Widgets REQUIRED) find_package(Qt5PrintSupport REQUIRED) find_package(Qt5Network REQUIRED) find_package(Qt5Xml REQUIRED) if(NOT SYSTEM_LINUX) find_package(Qt5WebEngineWidgets REQUIRED) endif() find_package(MongoDB REQUIRED) find_package(OpenSSL REQUIRED) if (SYSTEM_MACOSX) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.14) find_package(Qt5MacExtras REQUIRED) endif() # Projects set(LIBSSH2_VERSION 1.9.0) if(SYSTEM_LINUX) set(LIBSSH2_VERSION 1.7.0) endif() set(LIBSSH2_DIR src/third-party/libssh2-${LIBSSH2_VERSION}) set(QJSON_VERSION 0.8.1) set(QJSON_DIR src/third-party/qjson-${QJSON_VERSION}) set(QSCINTILLA_VERSION 2.8.4) set(QSCINTILLA_DIR src/third-party/qscintilla-${QSCINTILLA_VERSION}) set(ESPRIMA_VERSION 2.7.3) set(ESPRIMA_DIR src/third-party/esprima-${ESPRIMA_VERSION}) # todo: Use equivalent vars from googletest cmake files. Currently they are somehow not working. set(GOOGLE_TEST_VERSION 1.8.1) set(GOOGLE_TEST_DIR src/third-party/googletest-${GOOGLE_TEST_VERSION}) add_subdirectory(${LIBSSH2_DIR}) add_subdirectory(src/robomongo/ssh) add_subdirectory(${QJSON_DIR}) add_subdirectory(${QSCINTILLA_DIR}) add_subdirectory(${GOOGLE_TEST_DIR}) add_subdirectory(src/robomongo) add_subdirectory(src/robomongo-unit-tests) # Show configuration summary include(RobomongoConfigurationSummary) ================================================ FILE: COPYRIGHT ================================================ Robo 3T, shell-centric cross-platform MongoDB management tool. Copyright 2014-2017 3T Software Labs Ltd. The Robo 3T is released under the terms of the GNU General Public License, version 3. The Robo 3T Project includes files written by third parties and used with permission or subject to their respective license agreements. ================================================ FILE: DESCRIPTION ================================================ Robo 3T is a shell-centric cross-platform open source MongoDB management tool (i.e. Admin GUI). Robomongo embeds the same JavaScript engine that powers MongoDB's mongo shell. ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================ ## Important If you are using Studio 3T, please file any feedback on the [Studio 3T Feedback](https://studio3t.com/feedback/) page. If you are using Studio 3T Free, there is the [3T Community](https://community.studio3t.com/) for discussions with a [dedicated Free section](https://community.studio3t.com/c/studio-3t-free/13). Studio 3T does not monitor this repository for Studio 3T issues. # End of Robo 3T Development Robo 3T is no longer being developed by Studio 3T. Studio 3T recommends users looking for a MongoDB GUI client try [Studio 3T Free](https://studio3t.com/free), a free-forever edition of the Studio 3T tools. Read more about the changes on the [Robo 3T Blog](https://blog.robomongo.org/studio3t-free/). The last release of Robo 3T is version 1.4.4, downloadable from the following links: * [Robo 3T Windows .zip](https://download.studio3t.com/robomongo/windows/robo3t-1.4.4-windows-x86_64-e6ac9ec5.zip) * [Robo 3T Windows .exe](https://download.studio3t.com/robomongo/windows/robo3t-1.4.4-windows-x86_64-e6ac9ec5.exe) * [Robo 3T Mac](https://download.studio3t.com/robomongo/mac/robo3t-1.4.4-darwin-x86_64-e6ac9ec.dmg) * [Robo 3T Linux](https://download.studio3t.com/robomongo/linux/robo3t-1.4.4-linux-x86_64-e6ac9ec.tar.gz) And the source code from [Robo 3T repository](https://github.com/Studio3T/robomongo/latest). Studio 3T would like to thank the Robo 3T community who used and supported the application, since it was acquired in 2017. This repository, the website and blog will be left online and available to maintain a record of what was one of the most influential MongoDB clients of its time. About Robo 3T =============== [Robo 3T](http://www.robomongo.org) (formerly Robomongo *) is a shell-centric cross-platform MongoDB management tool. Unlike most other MongoDB admin UI tools, Robo 3T embeds the actual `mongo` shell in a tabbed interface with access to a shell command line as well as GUI interaction. The latest stable release **Robo 3T 1.4** embeds **MongoDB 4.2** shell. Blog: http://blog.robomongo.org/robo-3t-1-4/ Download: https://robomongo.org/download All Releases: https://github.com/Studio3T/robomongo/releases Watch: [Robo 3T Youtube channel](https://www.youtube.com/channel/UCM_7WAseRWeeiBikExppstA) Follow: https://twitter.com/Robomongo **Embedded MongoDB shell history:** Robo 3T 1.4 -> MongoDB 4.2 Robo 3T 1.3 -> MongoDB 4.0 Robo 3T 1.1 -> MongoDB 3.4 Robo 3T 0.9 -> MongoDB 3.2 Robo 3T 0.8.x -> MongoDB 2.4.0 \* [Robomongo has been acquired by 3T](https://studio3t.com/press/3t-software-labs-acquires-robomongo-the-most-widely-used-mongodb-tool/) What's new in latest Robo 3T 1.4? ==================================== New Features: - Mongo shell 4.2 upgrade - Support for Ubuntu 20.04, macOS Big Sur and macOS 10.15 (Catalina) - SSH: ECDSA and Ed25519 keys support on Windows & macOS (issues #1719, #1530, #1590) - Manually specify visible databases (issues #1696, #1368, #389) - New Welcome Tab - embeds Chromium using QtWebEngine (Windows, macOS only) - Import keys from old version: autoExpand, lineNumbers, debugMode and shellTimeoutSec Improvements: - Qt Upgrade (v5.12.8 - Apr/2020, Windows & macOS only) - OpenSSL upgrade (v1.1.1f - Mar/2020, Windows & macOS only) - libssh2 upgrade (v1.9.0 - Jun/2019, Windows & macOS only) - Database explorer section has smaller default width (#1556) - Remember database explorer section size Fixes: - Fix previously broken IPv6 support from command line: robo3t --ipv6 - Fix crash when paging used in tabbed result window (#1661) - Fix broken paging in DocumentDB (#1694) - Authentication database option isn't used properly (#1696) - Add/Edit index ops fixed (re-written) (#1692) - Crash when expanding admin users (#1728) - Unable to run query after shell timeout reached (#1529) - Fix broken F2, F3, F4 shortcuts for tabbed result view - One time re-order limit per new connections window to prevent data loss (macOS, #1790) - Fix crash when new shell tab executed in server unreachable case Supported Platforms =============== Note: This sections is for Robo 3T and it directly depends on what MongoDB suppports (See: https://docs.mongodb.com/manual/administration/production-notes/#prod-notes-supported-platforms) | MongoDB Versions | MongoDB Cloud Platforms | | :-------------------- | :-------------------- | | 4.2 | Mongo Atlas | | 4.0 | | 3.6 | | Windows | Mac | Linux | |:---------------------- | :--------------------------------| :---------------------------| | Windows 64-bit 10 | Mac OS X 11 (Big Sur) | Linux Ubuntu 20.04 64-bit | Windows 64-bit 8.1 | Mac OS X 10.15 (Catalina) | Linux Ubuntu 18.04 64-bit | | Windows 64-bit 7 | Mac OS X 10.14 (Mojave) | | Contribute! =========== ### Code Contributions See all docs here: https://github.com/Studio3T/robomongo/wiki **Some important docs:** - [Build Diagram](https://github.com/Studio3T/robomongo/wiki/Robo-3T-Schematics:-Build,-Class-and-UI-Diagrams#1-build-diagram) - [Static Code Analysis](https://github.com/Studio3T/robomongo/wiki/Static-Code-Analysis) - [Robo 3T Feature Specisification](https://github.com/Studio3T/robomongo/wiki/Feature-Spec) - [Debugging](https://github.com/Studio3T/robomongo/blob/master/docs/Debug.md) - [Schematics](https://github.com/Studio3T/robomongo/tree/master/schematics) Code contributions are always welcome! Just try to follow our pre-commit checks and coding style: - [Robo 3T Code Quality](https://github.com/paralect/robomongo/wiki/Robomongo-Code-Quality) - [Robo 3T C++11/14 Transition Guide](https://github.com/Studio3T/robomongo/wiki/Robomongo-Cplusplus-11,-14-Transition-Guide) - [Robo 3T Coding Style](https://github.com/paralect/robomongo/wiki/Robomongo-Coding-Style) If you plan to contribute, please create a Github issue (or comment on the relevant existing issue) so we can help coordinate with upcoming release plans. Pull requests (PRs) should generally be for discrete issues (i.e. one issue per PR please) and be clean to merge against the current master branch. It would also be helpful if you can confirm what testing has been done (specific O/S targets and MongoDB versions if applicable). A usual naming approach for feature branches is `issue-###`. Include the issue number in your commit message / pull request description to link the PR to the original issue. For example: ```#248: updated QScintilla to 2.4.8 for retina display support".``` ### Testing - [Unit-Tests](https://github.com/Studio3T/robomongo/wiki/Unit-Tests) - [Manual Tests](wiki/Tests) - See all docs here: https://github.com/Studio3T/robomongo/wiki ### Suggest Features New feature suggestions or UI improvements are always welcome. [Create a new feature request on github](https://github.com/paralect/robomongo/issues/new) This project is powered by open source volunteers, so we have a limited amount of development resource to address all requests. We will certainly make best efforts to progress (particularly for those with strong community upvotes). Download ======== You can download tested installer packages for macOS, Windows, and Linux from our site: [www.robomongo.org](http://www.robomongo.org). Support ======= Robo 3T is an open source project driven by volunteers. We'll try to get to your questions as soon as we can, but please be patient :). You can: - [Create a new issue in the Github issue queue](https://github.com/paralect/robomongo/issues) - [Join developer discussion on Gitter](https://gitter.im/paralect/robomongo) License ======= Copyright 2014-2021 [3T Software Labs Ltd](https://studio3t.com/). All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ================================================ FILE: bin/README.md ================================================ Bash fronted for CMake ====================== If you want to use this scripts, you need to set single environment variable: E.g.: export ROBOMONGO_CMAKE_PREFIX_PATH="/path/to/qt-5/5.5/gcc_64;/path/to/robomongo-shell" Build Robomongo: $ bin/configure $ bin/build Install Robomongo: $ bin/install Run Robomongo: $ bin/run Pack Robomongo: $ bin/pack More: - [Build Robo 3T - Mac OS X and Linux](https://github.com/paralect/robomongo/blob/master/docs/BuildRobo3TOnMacAndLinux.md) - [Build Robo 3T - Windows](https://github.com/paralect/robomongo/blob/master/docs/BuildRobo3TOnWindows.md) - [Unit Tests](wiki/Unit-Tests) - [Static Code Analysis](wiki/Static-Code-Analysis) ================================================ FILE: bin/build ================================================ #!/usr/bin/env bash BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project source $BIN_DIR/common/setup $1 # $1: release or debug cd $BUILD_DIR cmake --build . "${@:2}" -- -j8 ================================================ FILE: bin/build-and-run-tests ================================================ #!/usr/bin/env bash BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project source $BIN_DIR/build || exit $? source $BIN_DIR/run-tests $1 ================================================ FILE: bin/build-and-run-tests.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion rem Path to bin and project folder set BIN_DIR_WITH_BACKSLASH=%~dp0% set BIN_DIR=%BIN_DIR_WITH_BACKSLASH:~0,-1% set PROJECT_DIR=%BIN_DIR%\.. rem Run build call "%BIN_DIR%\build.bat" %* if %ERRORLEVEL% neq 0 (exit /b 1) rem Run tests call "%BIN_DIR%\run-tests.bat" %* if %ERRORLEVEL% neq 0 (exit /b 1) ================================================ FILE: bin/build.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion rem Path to bin and project folder set BIN_DIR_WITH_BACKSLASH=%~dp0% set BIN_DIR=%BIN_DIR_WITH_BACKSLASH:~0,-1% set PROJECT_DIR=%BIN_DIR%\.. rem Run common setup code call "%BIN_DIR%\common\setup.bat" %* if %ERRORLEVEL% neq 0 (exit /b 1) rem Run build cd "%BUILD_DIR%" cmake --build . --config %BUILD_TYPE% rem Install debug *.dll files if "%BUILD_TYPE%" == "Debug" ( call "%BIN_DIR%\install-debug-dlls.bat" %* ) ================================================ FILE: bin/clang-checks.txt ================================================ bugprone-* cert-* clang-analyzer-* cppcoreguidelines-* concurrency-* hicpp-* llvm-* misc-* performance-* readability-* ================================================ FILE: bin/clang-tidy.xml ================================================ true ================================================ FILE: bin/clean ================================================ #!/usr/bin/env bash BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project source $BIN_DIR/common/setup $1 cd $VARIANT_DIR && rm -rf $BUILD_DIR_NAME ================================================ FILE: bin/clean.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion rem Path to bin and project folder set BIN_DIR_WITH_BACKSLASH=%~dp0% set BIN_DIR=%BIN_DIR_WITH_BACKSLASH:~0,-1% set PROJECT_DIR=%BIN_DIR%\.. rem Run common setup code call "%BIN_DIR%\common\setup.bat" %* if %ERRORLEVEL% neq 0 (exit /b 1) rem Clear build directory cd "%VARIANT_DIR%" rmdir /s /q "%BUILD_DIR_NAME%" ================================================ FILE: bin/common/setup ================================================ #!/usr/bin/env bash if [ "$1" == "debug" ]; then BUILD_TYPE=Debug else BUILD_TYPE=Release # Prepend "release" in front of $@ set -- release "$@" fi # Build dir name is a lowercase of BUILD_TYPE # i.e. debug BUILD_DIR_NAME="$(echo $BUILD_TYPE | tr '[A-Z]' '[a-z]')" # i.e. /path/to/robomongo/build VARIANT_DIR=$PROJECT_DIR/build # i.e. /path/to/robomongo/build/debug BUILD_DIR=$VARIANT_DIR/$BUILD_DIR_NAME # i.e. /path/to/robomongo/build/debug/install INSTALL_PREFIX=$BUILD_DIR/install # i.e. /path/to/robomongo/build/debug/package PACK_PREFIX=$BUILD_DIR/package PREFIX_PATH=$ROBOMONGO_CMAKE_PREFIX_PATH mkdir -p $BUILD_DIR ================================================ FILE: bin/common/setup.bat ================================================ rem Check that ROBOMONGO_CMAKE_PREFIX_PATH is set if not defined ROBOMONGO_CMAKE_PREFIX_PATH ( echo Set environment variable ROBOMONGO_CMAKE_PREFIX_PATH in order to use this script echo. echo For example, execute the following command: echo setx ROBOMONGO_CMAKE_PREFIX_PATH "c:\Qt-5\5.5\msvc2013_64;c:\robomongo-shell" echo. echo You also need to reopen your Windows Command Prompt. exit /b 1 ) rem Check for debug build type if %1. == debug. ( set BUILD_TYPE=Debug ) else ( rem If build type wasn't specified - Release mode assumed if %1. ==. ( set BUILD_TYPE=Release ) else ( echo Invalid build type "%1". Supported build types: debug or release. exit /b 1 ) ) rem Build dir name (i.e. Release) set BUILD_DIR_NAME=%BUILD_TYPE% rem i.e. /path/to/robomongo/build set VARIANT_DIR=%PROJECT_DIR%\build rem i.e. /path/to/robomongo/build/debug set BUILD_DIR=%VARIANT_DIR%\%BUILD_DIR_NAME% rem i.e. /path/to/robomongo/build/debug/install set INSTALL_PREFIX=%BUILD_DIR%\install rem i.e. /path/to/robomongo/build/debug/package set PACK_PREFIX=%BUILD_DIR%/package rem Get value from environment variable set PREFIX_PATH=%ROBOMONGO_CMAKE_PREFIX_PATH% rem Create BUILD_DIR if it is not exists already if not exist "%BUILD_DIR%" ( md "%BUILD_DIR%" ) ================================================ FILE: bin/configure ================================================ #!/usr/bin/env bash BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project source $BIN_DIR/common/setup $1 # $1: release or debug cd $BUILD_DIR # $BUILD_TYPE: Release or Debug cmake -D "CMAKE_PREFIX_PATH=$PREFIX_PATH" -D "CMAKE_BUILD_TYPE=$BUILD_TYPE" -D "CMAKE_INSTALL_PREFIX=$INSTALL_PREFIX" "${@:2}" $PROJECT_DIR ================================================ FILE: bin/configure.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion rem Path to bin and project folder set BIN_DIR_WITH_BACKSLASH=%~dp0% set BIN_DIR=%BIN_DIR_WITH_BACKSLASH:~0,-1% set PROJECT_DIR=%BIN_DIR%\.. rem Run common setup code call "%BIN_DIR%\common\setup.bat" %* if %ERRORLEVEL% neq 0 (exit /b 1) rem Run CMake configuration step rem BUILD_TYPE: Release or Debug cd "%BUILD_DIR%" cmake -G "Visual Studio 15 2017 Win64" -D "CMAKE_PREFIX_PATH=%PREFIX_PATH%" -D "CMAKE_BUILD_TYPE=%BUILD_TYPE%" -D "CMAKE_INSTALL_PREFIX=%INSTALL_PREFIX%" %PROJECT_DIR% @REM echo ___________________________________________________________________ @REM rem Enable Clang Tidy for Visual Studio 2019 IDE ... @REM set ROBO_PROJ_FILE=%BUILD_DIR%/src/robomongo/robomongo.vcxproj @REM python %BIN_DIR%\enable-visual-studio-clang-tidy.py %ROBO_PROJ_FILE% %BIN_DIR% ================================================ FILE: bin/enable-visual-studio-clang-tidy.py ================================================ import sys from lxml import etree print('\nEnabling Clang Tidy for Visual Studio 2019') robo_proj_xml = sys.argv[1] clang_tidy_xml = sys.argv[2] + '/clang-tidy.xml' clang_checks_txt = sys.argv[2] + '/clang-checks.txt' print(sys.argv[0] + ":\nProcessing: " + sys.argv[1]) ## Read clang checks clang_checks = None with open(clang_checks_txt, "r") as file: clang_checks = file.read().replace('\n', ',') ## tree = etree.parse(robo_proj_xml) ns = {'ns':'http://schemas.microsoft.com/developer/msbuild/2003'} if(tree.find('//ns:EnableClangTidyCodeAnalysis', namespaces=ns) is not None): tree.find('//ns:ClangTidyChecks', namespaces=ns).text = clang_checks tree.write(robo_proj_xml) print('Finished') sys.exit() ## Append clang_tidy_xml - Todo: refactor using etree with open(robo_proj_xml, "r") as in_file: buf = in_file.readlines() with open(clang_tidy_xml, "r") as in_file: clang_tidy_xml_buf = in_file.read() appended = False with open(robo_proj_xml, "w") as out_file: for line in buf: if "" in line and appended == False: line = line + clang_tidy_xml_buf appended = True out_file.write(line) ## Add clang checks tree = etree.parse(robo_proj_xml) tree.find('//ns:ClangTidyChecks', namespaces=ns).text = clang_checks tree.write(robo_proj_xml) print('Finished') ================================================ FILE: bin/install ================================================ #!/usr/bin/env bash BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project source $BIN_DIR/common/setup $1 cd $BUILD_DIR cmake --build . --target install/strip "${@:2}" ================================================ FILE: bin/install-debug-dlls.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion rem // This script installs debug *.dll files rem ------------------------------------------- set INSTALL_DIR=%BUILD_DIR%\src\robomongo\Debug\ if not exist %INSTALL_DIR%*.dll ( echo ---------------------------------------- echo Installing debug *.dll files ... echo INSTALL_DIR: %INSTALL_DIR% echo ---------------------------------------- ) rem // Find OpenSSL and Qt paths for %%i in (%ROBOMONGO_CMAKE_PREFIX_PATH%) do ( set xx=%%i if not "!xx!"=="!xx:ssl=!" ( set OPENSSL_DIR=!xx! ) if not "!xx!"=="!xx:qt=!" ( set Qt_DIR=!xx! ) ) set OPENSSL_DIR=%OPENSSL_DIR: =% set Qt_DIR=%Qt_DIR: =% rem // Copy dll files for /d %%a in ( "%VS140COMNTOOLS%\..\..\VC\redist\debug_nonredist\x64\Microsoft.VC140.DebugCRT\msvcp140d.dll" "%VS140COMNTOOLS%\..\..\VC\redist\debug_nonredist\x64\Microsoft.VC140.DebugCRT\vcruntime140d.dll" "%OPENSSL_DIR%\libssl-1_1-x64.dll" "%OPENSSL_DIR%\libcrypto-1_1-x64.dll" "%Qt_DIR%\bin\Qt5Cored.dll" "%Qt_DIR%\bin\Qt5Guid.dll" "%Qt_DIR%\bin\Qt5Networkd.dll" "%Qt_DIR%\bin\Qt5Widgetsd.dll" "%Qt_DIR%\bin\Qt5Positioningd.dll" "%Qt_DIR%\bin\Qt5Qmld.dll" "%Qt_DIR%\bin\Qt5Quickd.dll" "%Qt_DIR%\bin\Qt5QuickWidgetsd.dll" "%Qt_DIR%\bin\Qt5WebChanneld.dll" "%Qt_DIR%\bin\Qt5WebEngineCored.dll" "%Qt_DIR%\bin\Qt5WebEngineWidgetsd.dll" "%Qt_DIR%\bin\Qt5PrintSupportd.dll" ) do ( if not exist !INSTALL_DIR!%%~NXa ( xcopy %%a !INSTALL_DIR! /d /y ) ) if not exist %INSTALL_DIR%\imageformats\qgifd.dll ( xcopy /s "%Qt_DIR%\plugins\imageformats" %INSTALL_DIR%\imageformats\ /d /y ) if not exist %INSTALL_DIR%\platforms\qminimald.dll ( xcopy /s "%Qt_DIR%\plugins\platforms" %INSTALL_DIR%\platforms\ /d /y ) ================================================ FILE: bin/install.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion rem Path to bin and project folder set BIN_DIR_WITH_BACKSLASH=%~dp0% set BIN_DIR=%BIN_DIR_WITH_BACKSLASH:~0,-1% set PROJECT_DIR=%BIN_DIR%\.. rem Run common setup code call "%BIN_DIR%\common\setup.bat" %* if %ERRORLEVEL% neq 0 (exit /b 1) rem Run install cd "%BUILD_DIR%" cmake --build . --config %BUILD_TYPE% --target install ================================================ FILE: bin/pack ================================================ #!/usr/bin/env bash BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project source $BIN_DIR/common/setup $1 cd $BUILD_DIR cmake --build . --target package "${@:2}" ================================================ FILE: bin/pack.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion rem Path to bin and project folder set BIN_DIR_WITH_BACKSLASH=%~dp0% set BIN_DIR=%BIN_DIR_WITH_BACKSLASH:~0,-1% set PROJECT_DIR=%BIN_DIR%\.. rem Run common setup code call "%BIN_DIR%\common\setup.bat" %* if %ERRORLEVEL% neq 0 (exit /b 1) rem Run install cd "%BUILD_DIR%" cmake --build . --config %BUILD_TYPE% --target package ================================================ FILE: bin/rebuild.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion call "clean.bat" %* call "configure.bat" %* call "build.bat" %* ================================================ FILE: bin/run ================================================ #!/usr/bin/env bash BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project source $BIN_DIR/common/setup $1 echo "Note: ./install must be executed before this script." if [ "$(uname -s)" == "Darwin" ]; then echo "Starting $BUILD_DIR/install/Robo 3T.app/Contents/MacOS/Robo 3T" "$BUILD_DIR/install/Robo 3T.app/Contents/MacOS/Robo 3T" else echo "Starting $BUILD_DIR/install/bin/robo3t" "$BUILD_DIR/install/bin/robo3t" fi ================================================ FILE: bin/run-clang-tidy ================================================ #!/usr/bin/env bash ### Common Setup BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project source $BIN_DIR/common/setup $1 # $1: release or debug ### Setup CLANG_TIDY_BUILD_DIR=$PROJECT_DIR/build/clang-tidy-$BUILD_TYPE rm -rf $CLANG_TIDY_BUILD_DIR mkdir -p $CLANG_TIDY_BUILD_DIR && cd $CLANG_TIDY_BUILD_DIR ### Configure cmake # Info: $BUILD_TYPE == Release or Debug cmake -D "CMAKE_EXPORT_COMPILE_COMMANDS=ON" -D "CMAKE_PREFIX_PATH=$PREFIX_PATH" -D "CMAKE_BUILD_TYPE=$BUILD_TYPE" -D "CMAKE_INSTALL_PREFIX=$INSTALL_PREFIX" "${@:2}" $PROJECT_DIR echo -e "\n------------------------------------------------" echo "clang-tidy configured. Mode: $BUILD_TYPE" echo -e "------------------------------------------------\n" ### Run clang-tidy FILE_OR_PATH=/opt/robo/src/robomongo/ # or MainWindow.cpp CHECKS=clang-analyzer-*,readability-implicit-bool-conversion # All Checks: https://clang.llvm.org/extra/clang-tidy/checks/list.html # e.g. $BIN_DIR/run-clang-tidy.py -checks=clang-analyzer-*,readability-implicit-bool-conversion /opt/robo/src/robomongo/ $BIN_DIR/run-clang-tidy.py -checks=$CHECKS $FILE_OR_PATH echo -e "\n------------------------------------------------" echo "clang-tidy finished" echo "Mode : $BUILD_TYPE" echo "CLANG_TIDY_BUILD_DIR: $CLANG_TIDY_BUILD_DIR" echo "Command executed : $BIN_DIR/run-clang-tidy.py -checks=$CHECKS $FILE_OR_PATH" echo -e "------------------------------------------------\n" ================================================ FILE: bin/run-clang-tidy.py ================================================ #!/usr/bin/env python # #===- run-clang-tidy.py - Parallel clang-tidy runner ---------*- 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 # #===------------------------------------------------------------------------===# # FIXME: Integrate with clang-tidy-diff.py """ Parallel clang-tidy runner ========================== Runs clang-tidy over all files in a compilation database. Requires clang-tidy and clang-apply-replacements in $PATH. Example invocations. - Run clang-tidy on all files in the current working directory with a default set of checks and show warnings in the cpp files and all project headers. run-clang-tidy.py $PWD - Fix all header guards. run-clang-tidy.py -fix -checks=-*,llvm-header-guard - Fix all header guards included from clang-tidy and header guards for clang-tidy headers. run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \ -header-filter=extra/clang-tidy Compilation database setup: http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html """ from __future__ import print_function import argparse import glob import json import multiprocessing import os import re import shutil import subprocess import sys import tempfile import threading import traceback try: import yaml except ImportError: yaml = None is_py2 = sys.version[0] == '2' if is_py2: import Queue as queue else: import queue as queue def find_compilation_database(path): """Adjusts the directory until a compilation database is found.""" result = './' while not os.path.isfile(os.path.join(result, path)): if os.path.realpath(result) == '/': print('Error: could not find compilation database.') sys.exit(1) result += '../' return os.path.realpath(result) def make_absolute(f, directory): if os.path.isabs(f): return f return os.path.normpath(os.path.join(directory, f)) def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path, header_filter, extra_arg, extra_arg_before, quiet, config): """Gets a command line for clang-tidy.""" start = [clang_tidy_binary] if header_filter is not None: start.append('-header-filter=' + header_filter) if checks: start.append('-checks=' + checks) if tmpdir is not None: start.append('-export-fixes') # Get a temporary file. We immediately close the handle so clang-tidy can # overwrite it. (handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir) os.close(handle) start.append(name) for arg in extra_arg: start.append('-extra-arg=%s' % arg) for arg in extra_arg_before: start.append('-extra-arg-before=%s' % arg) start.append('-p=' + build_path) if quiet: start.append('-quiet') if config: start.append('-config=' + config) start.append(f) return start def merge_replacement_files(tmpdir, mergefile): """Merge all replacement files in a directory into a single file""" # The fixes suggested by clang-tidy >= 4.0.0 are given under # the top level key 'Diagnostics' in the output yaml files mergekey="Diagnostics" merged=[] for replacefile in glob.iglob(os.path.join(tmpdir, '*.yaml')): content = yaml.safe_load(open(replacefile, 'r')) if not content: continue # Skip empty files. merged.extend(content.get(mergekey, [])) if merged: # MainSourceFile: The key is required by the definition inside # include/clang/Tooling/ReplacementsYaml.h, but the value # is actually never used inside clang-apply-replacements, # so we set it to '' here. output = { 'MainSourceFile': '', mergekey: merged } with open(mergefile, 'w') as out: yaml.safe_dump(output, out) else: # Empty the file: open(mergefile, 'w').close() def check_clang_apply_replacements_binary(args): """Checks if invoking supplied clang-apply-replacements binary works.""" try: subprocess.check_call([args.clang_apply_replacements_binary, '--version']) except: print('Unable to run clang-apply-replacements. Is clang-apply-replacements ' 'binary correctly specified?', file=sys.stderr) traceback.print_exc() sys.exit(1) def apply_fixes(args, tmpdir): """Calls clang-apply-fixes on a given directory.""" invocation = [args.clang_apply_replacements_binary] if args.format: invocation.append('-format') if args.style: invocation.append('-style=' + args.style) invocation.append(tmpdir) subprocess.call(invocation) def run_tidy(args, tmpdir, build_path, queue, lock, failed_files): """Takes filenames out of queue and runs clang-tidy on them.""" while True: name = queue.get() invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks, tmpdir, build_path, args.header_filter, args.extra_arg, args.extra_arg_before, args.quiet, args.config) proc = subprocess.Popen(invocation, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = proc.communicate() if proc.returncode != 0: failed_files.append(name) with lock: sys.stdout.write(' '.join(invocation) + '\n' + output.decode('utf-8')) if len(err) > 0: sys.stdout.flush() sys.stderr.write(err.decode('utf-8')) queue.task_done() def main(): parser = argparse.ArgumentParser(description='Runs clang-tidy over all files ' 'in a compilation database. Requires ' 'clang-tidy and clang-apply-replacements in ' '$PATH.') parser.add_argument('-clang-tidy-binary', metavar='PATH', default='clang-tidy', help='path to clang-tidy binary') parser.add_argument('-clang-apply-replacements-binary', metavar='PATH', default='clang-apply-replacements', help='path to clang-apply-replacements binary') parser.add_argument('-checks', default=None, help='checks filter, when not specified, use clang-tidy ' 'default') parser.add_argument('-config', default=None, help='Specifies a configuration in YAML/JSON format: ' ' -config="{Checks: \'*\', ' ' CheckOptions: [{key: x, ' ' value: y}]}" ' 'When the value is empty, clang-tidy will ' 'attempt to find a file named .clang-tidy for ' 'each source file in its parent directories.') parser.add_argument('-header-filter', default=None, help='regular expression matching the names of the ' 'headers to output diagnostics from. Diagnostics from ' 'the main file of each translation unit are always ' 'displayed.') if yaml: parser.add_argument('-export-fixes', metavar='filename', dest='export_fixes', help='Create a yaml file to store suggested fixes in, ' 'which can be applied with clang-apply-replacements.') parser.add_argument('-j', type=int, default=0, help='number of tidy instances to be run in parallel.') parser.add_argument('files', nargs='*', default=['.*'], help='files to be processed (regex on path)') parser.add_argument('-fix', action='store_true', help='apply fix-its') parser.add_argument('-format', action='store_true', help='Reformat code ' 'after applying fixes') parser.add_argument('-style', default='file', help='The style of reformat ' 'code after applying fixes') parser.add_argument('-p', dest='build_path', help='Path used to read a compile command database.') parser.add_argument('-extra-arg', dest='extra_arg', action='append', default=[], help='Additional argument to append to the compiler ' 'command line.') parser.add_argument('-extra-arg-before', dest='extra_arg_before', action='append', default=[], help='Additional argument to prepend to the compiler ' 'command line.') parser.add_argument('-quiet', action='store_true', help='Run clang-tidy in quiet mode') args = parser.parse_args() db_path = 'compile_commands.json' if args.build_path is not None: build_path = args.build_path else: # Find our database build_path = find_compilation_database(db_path) try: invocation = [args.clang_tidy_binary, '-list-checks'] invocation.append('-p=' + build_path) if args.checks: invocation.append('-checks=' + args.checks) invocation.append('-') if args.quiet: # Even with -quiet we still want to check if we can call clang-tidy. with open(os.devnull, 'w') as dev_null: subprocess.check_call(invocation, stdout=dev_null) else: subprocess.check_call(invocation) except: print("Unable to run clang-tidy.", file=sys.stderr) sys.exit(1) # Load the database and extract all files. database = json.load(open(os.path.join(build_path, db_path))) files = [make_absolute(entry['file'], entry['directory']) for entry in database] max_task = args.j if max_task == 0: max_task = multiprocessing.cpu_count() tmpdir = None if args.fix or (yaml and args.export_fixes): check_clang_apply_replacements_binary(args) tmpdir = tempfile.mkdtemp() # Build up a big regexy filter from all command line arguments. file_name_re = re.compile('|'.join(args.files)) return_code = 0 try: # Spin up a bunch of tidy-launching threads. task_queue = queue.Queue(max_task) # List of files with a non-zero return code. failed_files = [] lock = threading.Lock() for _ in range(max_task): t = threading.Thread(target=run_tidy, args=(args, tmpdir, build_path, task_queue, lock, failed_files)) t.daemon = True t.start() # Fill the queue with files. for name in files: if file_name_re.search(name): task_queue.put(name) # Wait for all threads to be done. task_queue.join() if len(failed_files): return_code = 1 except KeyboardInterrupt: # This is a sad hack. Unfortunately subprocess goes # bonkers with ctrl-c and we start forking merrily. print('\nCtrl-C detected, goodbye.') if tmpdir: shutil.rmtree(tmpdir) os.kill(0, 9) if yaml and args.export_fixes: print('Writing fixes to ' + args.export_fixes + ' ...') try: merge_replacement_files(tmpdir, args.export_fixes) except: print('Error exporting fixes.\n', file=sys.stderr) traceback.print_exc() return_code=1 if args.fix: print('Applying fixes ...') try: apply_fixes(args, tmpdir) except: print('Error applying fixes.\n', file=sys.stderr) traceback.print_exc() return_code=1 if tmpdir: shutil.rmtree(tmpdir) sys.exit(return_code) if __name__ == '__main__': main() ================================================ FILE: bin/run-cppcheck.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion set CPP_VER=c++17 set FILE_OR_DIR=..\src\robomongo\ set PARAM_1=%1 if not "%PARAM_1%" == "" ( set FILE_OR_DIR=%PARAM_1% ) rem Run analysis echo. echo ---------------------- Running cppcheck ---------------------- echo. call cppcheck %FILE_OR_DIR% --enable=all --std=%CPP_VER% echo. echo ---------------------- End of cppcheck ---------------------- echo. echo ---------------------- Info ---------------------- call cppcheck --version echo Command called: cppcheck %FILE_OR_DIR% --enable=all --std=%CPP_VER% ================================================ FILE: bin/run-scan-build ================================================ #!/usr/bin/env bash BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project source $BIN_DIR/common/setup $1 # $1: release or debug mkdir -p ../scan-build && cd ../scan-build # $BUILD_TYPE: Release or Debug # Hint: Use "--use-analyzer /usr/local/opt/llvm/bin/clang++" after "-v" scan-build -v cmake -D "CMAKE_PREFIX_PATH=$PREFIX_PATH" -D "CMAKE_BUILD_TYPE=$BUILD_TYPE" -D "CMAKE_INSTALL_PREFIX=$INSTALL_PREFIX" "${@:2}" $PROJECT_DIR echo -e "\n------------------------------------------------" echo "Configured with scan-build in $BUILD_TYPE mode" echo -e "------------------------------------------------\n" scan-build cmake --build . "${@:2}" -- -j8 echo -e "\n------------------------------------------------" echo "Built with scan-build in $BUILD_TYPE mode" echo -e "------------------------------------------------\n" ================================================ FILE: bin/run-tests ================================================ #!/usr/bin/env bash BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project source $BIN_DIR/common/setup $1 echo echo "******************* Running unit tests in $BUILD_TYPE mode *******************" echo if [ "$(uname -s)" == "Darwin" ]; then EXE="$BUILD_DIR/src/robomongo-unit-tests/robo_unit_tests" echo "Running file: $EXE" "$EXE" else # Linux echo "Error: Currently unit testing is disabled for Linux due to MongoDB linking problem" exit 0 EXE="$BUILD_DIR/src/robomongo-unit-tests/robo_unit_tests" echo "Running file: $EXE" "$EXE" fi ================================================ FILE: bin/run-tests.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion rem Path to bin and project folder set BIN_DIR_WITH_BACKSLASH=%~dp0% set BIN_DIR=%BIN_DIR_WITH_BACKSLASH:~0,-1% set PROJECT_DIR=%BIN_DIR%\.. rem Run common setup code call "%BIN_DIR%\common\setup.bat" %* if %ERRORLEVEL% neq 0 (exit /b 1) rem Run tests echo. echo ******************* Running unit tests ******************* echo Mode: %BUILD_TYPE% set TEST_EXE_DIR=%PROJECT_DIR%\\build\%BUILD_TYPE%\src\robomongo-unit-tests\%BUILD_TYPE% echo Run : %TEST_EXE_DIR%\robo_unit_tests.exe echo. call "%TEST_EXE_DIR%\robo_unit_tests.exe" %* ================================================ FILE: bin/run-vs-code-analysis.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion rem Path to bin and project folder set BIN_DIR_WITH_BACKSLASH=%~dp0% set BIN_DIR=%BIN_DIR_WITH_BACKSLASH:~0,-1% set PROJECT_DIR=%BIN_DIR%\.. rem Run common setup code call "%BIN_DIR%\common\setup.bat" %* if %ERRORLEVEL% neq 0 (exit /b 1) rem Run Visual Studio with RunCodeAnalysis cd "%BUILD_DIR%" set RULE_SET="/p:CodeAnalysisRuleSet=NativeRecommendedRules.ruleset" set OUT_FILE=vs-code-analysis-%BUILD_TYPE%.txt echo ------------- Running Visual Studio with RunCodeAnalysis ------------- echo Result will be written into file "%OUT_FILE%" in the end. echo ... cmake --build . --config %BUILD_TYPE% -- "/p:RunCodeAnalysis=true" %RULE_SET% > %BIN_DIR%\%OUT_FILE% rem Rules: C:\Program Files (x86)\Microsoft Visual Studio 14.0\Team Tools\Static Analysis Tools\Rule Sets rem https://docs.microsoft.com/en-us/visualstudio/code-quality/rule-set-reference?view=vs-2019 echo ------------- End of RunCodeAnalysis ------------- echo. echo ------------- Info ------------- echo Mode : %BUILD_TYPE% echo RULE_SET: %RULE_SET% (see the script comments for more info) ================================================ FILE: bin/set-mongo-warning-level-3.py ================================================ import os from os.path import join ### def force_warning_level_3(file): with open(file, "r") as in_file: buf = in_file.readlines() push = '#pragma warning(push, 3)\n' pop = '#pragma warning(pop)\n' last_line_has_mongo = False with open(file, "w") as out_file: for line in buf: if line.startswith(('#include " echo "Example: tag 0.9.0-rc9" exit fi if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then echo "Error: You are not on master branch" exit fi cd $PROJECT_DIR git tag -a "v${VERSION}" -m "${VERSION}" git push --tags ================================================ FILE: cmake/FindMongoDB.cmake ================================================ # # Find the MongoDB libraries # # This module defines the following variables: # MongoDB_FOUND # MongoDB_LIBS # MongoDB_INCLUDE_DIRS # MongoDB_DEFINITIONS # # Imported target "mongodb" is created # # We assume, that at least "mongo" target was build by the following command: # $ scons mongo # # # Try to find MongoDB directory (uses CMAKE_PREFIX_PATH locations) find_path( MongoDB_DIR src/mongo/config.h.in DOC "Path to MongoDB (github.com/robomongo-shell) root directory" ) # Find relative path to build directory if(BUILD_RELEASE OR BUILD_RELWITHDEBINFO OR BUILD_MINSIZEREL) set(MongoDB_RELATIVE_BUILD_DIR build/opt) set(MongoDB_OBJECT_LIST_BUILD_TYPE_PART release) elseif(BUILD_DEBUG) set(MongoDB_RELATIVE_BUILD_DIR build/debug) set(MongoDB_OBJECT_LIST_BUILD_TYPE_PART debug) endif() # Set absolute path to build directory set(MongoDB_BUILD_DIR ${MongoDB_DIR}/${MongoDB_RELATIVE_BUILD_DIR}) # Set commong compiler definitons set(MongoDB_DEFINITIONS PCRE_STATIC BOOST_THREAD_VERSION=4 BOOST_THREAD_DONT_PROVIDE_VARIADIC_THREAD BOOST_THREAD_NO_DEPRECATED BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS BOOST_THREAD_HAS_BUG MONGO_CONFIG_HAVE_HEADER_UNISTD_H # Suppress "warning C4996: std::allocator..." from third_party\abseil-cpp-master _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING # Suppress "warning C4996: std::iterator..." from third_party\mozjs-60 _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING ) # Set common compiler include directories set(MOZJS_VER mozjs-60) set(MongoDB_INCLUDE_DIRS ${MongoDB_DIR}/src ${MongoDB_DIR}/src/third_party/abseil-cpp-master/abseil-cpp ${MongoDB_DIR}/src/third_party/boost-1.70.0 ${MongoDB_DIR}/src/third_party/fmt/dist/include ${MongoDB_DIR}/src/third_party/${MOZJS_VER}/include ${MongoDB_DIR}/src/third_party/${MOZJS_VER}/mongo_sources ${MongoDB_DIR}/src/third_party/pcre-8.42 ${MongoDB_DIR}/src/third_party/SafeInt ${MongoDB_BUILD_DIR} ) if(SYSTEM_LINUX) set(MongoDB_OBJECT_LIST_PLATFORM_PART linux) list(APPEND MongoDB_INCLUDE_DIRS ${MongoDB_DIR}/src/third_party/${MOZJS_VER}/platform/x86_64/linux/include) elseif(SYSTEM_WINDOWS) set(MongoDB_OBJECT_LIST_PLATFORM_PART windows) list(APPEND MongoDB_INCLUDE_DIRS ${MongoDB_DIR}/src/third_party/${MOZJS_VER}/platform/x86_64/windows/include) elseif(SYSTEM_MACOSX) set(MongoDB_OBJECT_LIST_PLATFORM_PART macosx) list(APPEND MongoDB_INCLUDE_DIRS ${MongoDB_DIR}/src/third_party/${MOZJS_VER}/platform/x86_64/macOS/include) elseif(SYSTEM_FREEBSD) set(MongoDB_OBJECT_LIST_PLATFORM_PART freebsd) list(APPEND MongoDB_INCLUDE_DIRS ${MongoDB_DIR}/src/third_party/${MOZJS_VER}/platform/x86_64/freebsd/include) elseif(SYSTEM_OPENBSD) set(MongoDB_OBJECT_LIST_PLATFORM_PART openbsd) list(APPEND MongoDB_INCLUDE_DIRS ${MongoDB_DIR}/src/third_party/${MOZJS_VER}/platform/x86_64/openbsd/include) endif() # Read list of object files # See "mongodb/README.md" file (relative to the current folder) for more information. file(READ ${CMAKE_CURRENT_LIST_DIR}/mongodb/${MongoDB_OBJECT_LIST_PLATFORM_PART}-${MongoDB_OBJECT_LIST_BUILD_TYPE_PART}.objects MongoDB_RELATIVE_LIBS) string(STRIP "${MongoDB_RELATIVE_LIBS}" MongoDB_RELATIVE_LIBS) # Convert string to list string(REPLACE " " ";" MongoDB_RELATIVE_LIBS ${MongoDB_RELATIVE_LIBS}) # todo: Find a better solution # Robo 1.4: Added as workaround fix for build error: # fatal error LNK1170: line in command file contains 131071 or more characters # Happens when the path of robo-shell is a long string (e.g. "E:/user/code/robomongo-shell") set(MongoDB_OBJECTS_DIR_ENV $ENV{MongoDB_OBJECTS}) if(MongoDB_OBJECTS_DIR_ENV) set(MongoDB_OBJECTS_DIR ${MongoDB_OBJECTS_DIR_ENV}) else() set(MongoDB_OBJECTS_DIR ${MongoDB_DIR}) endif() # Convert to absolute path foreach(lib ${MongoDB_RELATIVE_LIBS}) list(APPEND MongoDB_LIBS ${MongoDB_OBJECTS_DIR}/${lib}) endforeach() if(SYSTEM_WINDOWS) list(APPEND MongoDB_LIBS Crypt32.lib Secur32.lib Dnsapi.lib winhttp.lib) elseif(SYSTEM_MACOSX) list(APPEND MongoDB_LIBS -lcurl) elseif(SYSTEM_LINUX) list(APPEND MongoDB_LIBS -lresolv -lcurl) endif() # Get MongoDB repository recent tag execute_process( COMMAND git describe --abbrev=0 --tags WORKING_DIRECTORY ${MongoDB_DIR} OUTPUT_VARIABLE MongoDB_RECENT_TAG OUTPUT_STRIP_TRAILING_WHITESPACE ) # Handle the QUIETLY and REQUIRED arguments and set ALSA_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MongoDB FOUND_VAR MongoDB_FOUND # When using CMake 3.0 MONGODB_FOUND variable will be created. # Make it explicit that variable name is MongoDB_FOUND. REQUIRED_VARS MongoDB_DIR MongoDB_BUILD_DIR VERSION_VAR MongoDB_RECENT_TAG FAIL_MESSAGE "Could not find Robomongo Shell (MongoDB fork). Make sure that CMAKE_PREFIX_PATH points to Robomongo Shell project root.\n") if(MongoDB_FOUND) set(MongoDB_VERSION ${MongoDB_RECENT_TAG}) # Original MongoDB link command has the following in the end: m rt dl set(MongoDB_LIBS ${LINK_WHOLE_ARCHIVE_START} # Linux: -Wl,--whole-archive ${LINK_LIBGROUP_START} # Linux: -Wl,--start-group ${MongoDB_LIBS} ${LINK_LIBGROUP_END} # Linux: -Wl,--end-group ${LINK_WHOLE_ARCHIVE_END} # Linux: -Wl,--no-whole-archive ${CMAKE_DL_LIBS} # Linux: dl ) # Add imported target add_library(mongodb INTERFACE IMPORTED) # Specify INTERFACE properties for this target set_target_properties(mongodb PROPERTIES INTERFACE_LINK_LIBRARIES "${MongoDB_LIBS}" INTERFACE_COMPILE_DEFINITIONS "${MongoDB_DEFINITIONS}" INTERFACE_INCLUDE_DIRECTORIES "${MongoDB_INCLUDE_DIRS}" ) endif() # Cleanup unset(MongoDB_OBJECT_LIST_BUILD_TYPE_PART) unset(MongoDB_OBJECT_LIST_PLATFORM_PART) unset(MongoDB_RELATIVE_BUILD_DIR) unset(MongoDB_RELATIVE_LIBS) unset(MongoDB_RECENT_TAG) ================================================ FILE: cmake/FindOpenSSL.cmake ================================================ ################################################## # Find and Add OpenSSL shared (dynamic) library # # - This module defines the following variables: # OpenSSL_DIR # OPENSSL_VERSION # # - Imported shared(dynamic) libraries "ssl" and "crypto" are created. # # - It is assumed that OpenSSL has been built and path to OpenSSL build directory is # added into system(environment) variable ROBOMONGO_CMAKE_PREFIX_PATH. # ################################################## # Try to find OpenSSL directory (uses CMAKE_PREFIX_PATH locations) #------------------------------------------- find_path( OpenSSL_DIR include/openssl/ssl.h DOC "Path to OpenSSL (github.com/openssl/openssl) root directory" ) # Find OpenSSL version #------------------------------------------- set (OPENSSL_INCLUDE_DIR "${OpenSSL_DIR}/include") function(from_hex HEX DEC) string(TOUPPER "${HEX}" HEX) set(_res 0) string(LENGTH "${HEX}" _strlen) while (_strlen GREATER 0) math(EXPR _res "${_res} * 16") string(SUBSTRING "${HEX}" 0 1 NIBBLE) string(SUBSTRING "${HEX}" 1 -1 HEX) if (NIBBLE STREQUAL "A") math(EXPR _res "${_res} + 10") elseif (NIBBLE STREQUAL "B") math(EXPR _res "${_res} + 11") elseif (NIBBLE STREQUAL "C") math(EXPR _res "${_res} + 12") elseif (NIBBLE STREQUAL "D") math(EXPR _res "${_res} + 13") elseif (NIBBLE STREQUAL "E") math(EXPR _res "${_res} + 14") elseif (NIBBLE STREQUAL "F") math(EXPR _res "${_res} + 15") else() math(EXPR _res "${_res} + ${NIBBLE}") endif() string(LENGTH "${HEX}" _strlen) endwhile() set(${DEC} ${_res} PARENT_SCOPE) endfunction() if (OPENSSL_INCLUDE_DIR) if(OPENSSL_INCLUDE_DIR AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h") file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_str REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])+.*") # The version number is encoded as 0xMNNFFPPS: major minor fix patch status # The status gives if this is a developer or prerelease and is ignored here. # Major, minor, and fix directly translate into the version numbers shown in # the string. The patch field translates to the single character suffix that # indicates the bug fix state, which 00 -> nothing, 01 -> a, 02 -> b and so # on. string(REGEX REPLACE "^.*OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F]).*$" "\\1;\\2;\\3;\\4;\\5" OPENSSL_VERSION_LIST "${openssl_version_str}") list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR) list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR) from_hex("${OPENSSL_VERSION_MINOR}" OPENSSL_VERSION_MINOR) list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_FIX) from_hex("${OPENSSL_VERSION_FIX}" OPENSSL_VERSION_FIX) list(GET OPENSSL_VERSION_LIST 3 OPENSSL_VERSION_PATCH) if (NOT OPENSSL_VERSION_PATCH STREQUAL "00") from_hex("${OPENSSL_VERSION_PATCH}" _tmp) # 96 is the ASCII code of 'a' minus 1 math(EXPR OPENSSL_VERSION_PATCH_ASCII "${_tmp} + 96") unset(_tmp) # Once anyone knows how OpenSSL would call the patch versions beyond 'z' # this should be updated to handle that, too. This has not happened yet # so it is simply ignored here for now. string(ASCII "${OPENSSL_VERSION_PATCH_ASCII}" OPENSSL_VERSION_PATCH_STRING) endif () set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}${OPENSSL_VERSION_PATCH_STRING}") endif () endif () # Add imported ssl and crypto libraries #------------------------------------------- # Add imported target ssl (ssleay32) add_library(ssl SHARED IMPORTED) # Add imported target for crypto (libeay32) add_library(crypto SHARED IMPORTED) # todo: refactor if(SYSTEM_WINDOWS) set_target_properties(ssl PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include" IMPORTED_IMPLIB "${OpenSSL_DIR}/libssl.lib" ) set_target_properties(crypto PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include" IMPORTED_IMPLIB "${OpenSSL_DIR}/libcrypto.lib" ) else() if(SYSTEM_MACOSX) SET(EXT "dylib") elseif(SYSTEM_LINUX) SET(EXT "so") endif() set_target_properties(ssl PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include" IMPORTED_LOCATION "${OpenSSL_DIR}/libssl.${EXT}" ) set_target_properties(crypto PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include" IMPORTED_LOCATION "${OpenSSL_DIR}/libcrypto.${EXT}" ) endif() # End of file ================================================ FILE: cmake/FindThreading.cmake ================================================ # This wrapper is created in order to support CMake 3.0 # Only starting from CMake 3.1 "Threads" module exports # "Threads::Threads" target. find_package(Threads REQUIRED) # Support for CMake 3.0 # Export "Threads::Threads" imported target if it wasn't exported. # This lines are taken from the "FindThreads.cmake" of CMake 3.1: # https://github.com/Kitware/CMake/blob/v3.1.0/Modules/FindThreads.cmake if(THREADS_FOUND AND NOT TARGET Threads::Threads) add_library(Threads::Threads INTERFACE IMPORTED) if(THREADS_HAVE_PTHREAD_ARG) set_property(TARGET Threads::Threads PROPERTY INTERFACE_COMPILE_OPTIONS "-pthread") endif() if(CMAKE_THREAD_LIBS_INIT) set_property(TARGET Threads::Threads PROPERTY INTERFACE_LINK_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}") endif() endif() ================================================ FILE: cmake/RobomongoCMakeDefaults.cmake ================================================ # Put the include dirs which are in the source or build tree # before all other include dirs, so the headers in the sources # are prefered over the already installed ones # Since cmake 2.4.1 set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) # Use colored output # Since cmake 2.4.0 set(CMAKE_COLOR_MAKEFILE ON) # MongoDB compiled with this option set(CMAKE_POSITION_INDEPENDENT_CODE ON) # This variable is used by CMake "FindThreads" module # From documentation to FindThreads module: # > Please note that the compiler flag can only be # > used with the imported target. Use of both the imported # > target as well as this switch is highly recommended for new code. set(THREADS_PREFER_PTHREAD_FLAG ON) # Set the default build type to release with debug info if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build, options are: Debug, Release, RelWithDebInfo, MinSizeRel and None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used)" FORCE) endif() # Do not use Qt moc, uic and rcc by default # Since CMake 2.8.6 set(CMAKE_AUTOMOC OFF) set(CMAKE_AUTORCC OFF) set(CMAKE_AUTOUIC OFF) ================================================ FILE: cmake/RobomongoCommon.cmake ================================================ set(ROBOMONGO_DISPLAY_NAME "Robo 3T") set(ROBOMONGO_DISPLAY_FULL_NAME "Robo 3T ${PROJECT_VERSION}") ================================================ FILE: cmake/RobomongoConfigurationSummary.cmake ================================================ # Set MONGO_BUILD_FOLDER set(MONGO_BUILD_FOLDER "build/opt") # Release if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(MONGO_BUILD_FOLDER "build/debug") endif() # Show configuration summary and build tip message("") message(" _______________[ Robo 3T configuration summary ]________________") message("| ") message("|-- Dependencies:") message("| ") message("| Qt5 Core: ${Qt5Core_VERSION} ${Qt5Core_DIR}") message("| Qt5 Gui: ${Qt5Gui_VERSION} ${Qt5Gui_DIR}") message("| Qt5 Widgets: ${Qt5Widgets_VERSION} ${Qt5Widgets_DIR}") message("| Qt5 PrintSupport: ${Qt5Widgets_VERSION} ${Qt5PrintSupport_DIR}") message("| Qt5 Network: ${Qt5Network_VERSION} ${Qt5Network_DIR}") message("| Qt5 Xml: ${Qt5Xml_VERSION} ${Qt5Xml_DIR}") if(NOT SYSTEM_LINUX) message("| Qt5 WebEng*Widgets: ${Qt5WebEngineWidgets_VERSION} ${Qt5WebEngineWidgets_DIR}") endif() message("| MongoDB: ${MongoDB_VERSION} ${MongoDB_DIR}") message("| MongoDB Objects: ${MongoDB_VERSION} ${MongoDB_OBJECTS_DIR}/${MONGO_BUILD_FOLDER}") message("| OpenSSL: ${OPENSSL_VERSION} ${OpenSSL_DIR}") message("| libssh2: ${LIBSSH2_VERSION} ${LIBSSH2_DIR}") message("| QJson: ${QJSON_VERSION} ${QJSON_DIR}") message("| QScintilla: ${QSCINTILLA_VERSION} ${QSCINTILLA_DIR}") message("| Google Test: ${GOOGLE_TEST_VERSION} ${GOOGLE_TEST_DIR}") message("| ESPRIMA: ${ESPRIMA_VERSION} ${ESPRIMA_DIR}") message("|") message("|-- Summary:") message("|") message("| Version: ${PROJECT_VERSION}") message("| System: ${CMAKE_SYSTEM_NAME}") if (SYSTEM_MACOSX) message("| Target SDK: ${CMAKE_OSX_DEPLOYMENT_TARGET}") endif() message("| Build type: ${CMAKE_BUILD_TYPE}") message("| Commit: ${BUILD_NUMBER}") message("| Install path: ${CMAKE_INSTALL_PREFIX}") message("|") message("|-- Build, Run and Install:") message("| ") message("| $ bin/build") message("| $ bin/run") message("| $ bin/install") message("|") message("|-- Run Unit Tests:") message("| ") message("| $ bin/build-and-run-tests") message("| $ bin/run-tests") message("|") message("|-- Run Static Code Analysis:") message("|") if (SYSTEM_WINDOWS) message("| $ bin/run-cppcheck (Default: /src/robomongo/)") message("| $ bin/run-vs-code-analysis") else() message("| $ bin/run-clang-tidy") message("| $ bin/run-scan-build") endif() message("|__________________________________________________________________") message("") ================================================ FILE: cmake/RobomongoDefaults.cmake ================================================ # This files defines: # # 1) Platform checks # # SYSTEM_MACOSX # SYSTEM_WINDOWS # SYSTEM_LINUX # SYSTEM_FREEBSD # SYSTEM_OPENBSD # SYSTEM_NETBSD # # 2) Build types checks # # BUILD_DEBUG # BUILD_RELEASE # BUILD_RELWITHDEBINFO # BUILD_MINSIZEREL # # 3) Link "lib groups" and "whole-archive" options # # LINK_LIBGROUP_START # LINK_LIBGROUP_END # LINK_WHOLE_ARCHIVE_START # LINK_WHOLE_ARCHIVE_END # Platform checks if(CMAKE_SYSTEM_NAME MATCHES "Darwin") set(SYSTEM_MACOSX TRUE) elseif(CMAKE_SYSTEM_NAME MATCHES "Windows") set(SYSTEM_WINDOWS TRUE) elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") set(SYSTEM_LINUX TRUE) elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") set(SYSTEM_FREEBSD TRUE) set(SYSTEM_BSD TRUE) elseif(CMAKE_SYSTEM_NAME MATCHES "OpenBSD") set(SYSTEM_OPENBSD TRUE) set(SYSTEM_BSD TRUE) elseif(CMAKE_SYSTEM_NAME MATCHES "NetBSD") set(SYSTEM_NETBSD TRUE) set(SYSTEM_BSD TRUE) endif() # Build types if("${CMAKE_BUILD_TYPE}" MATCHES "Debug") set(BUILD_DEBUG TRUE) elseif("${CMAKE_BUILD_TYPE}" MATCHES "Release") set(BUILD_RELEASE TRUE) elseif("${CMAKE_BUILD_TYPE}" MATCHES "RelWithDebInfo") set(BUILD_RELWITHDEBINFO TRUE) elseif("${CMAKE_BUILD_TYPE}" MATCHES "MinSizeRel") set(BUILD_MINSIZEREL TRUE) endif() # Compiler checks # We check Clang using MATCH instead of strict equality, because as of CMake 3.0.0 the # CMAKE__COMPILER_ID value for Apple-provided Clang is AppleClang. To test # for both the Apple-provided Clang and the regular Clang we use MATCH. if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(COMPILER_CLANG TRUE) elseif(CMAKE_CXX_COMPILER_ID MATCHES "^GNU$") set(COMPILER_GCC TRUE) elseif(CMAKE_CXX_COMPILER_ID MATCHES "^MSVC$") set(COMPILER_MSVC TRUE) endif() # Link "groups" and "whole-archive" options if(SYSTEM_LINUX OR SYSTEM_FREEBSD OR SYSTEM_OPENBSD) set(LINK_LIBGROUP_START -Wl,--start-group) set(LINK_LIBGROUP_END -Wl,--end-group) set(LINK_WHOLE_ARCHIVE_START -Wl,--whole-archive) set(LINK_WHOLE_ARCHIVE_END -Wl,--no-whole-archive) elseif(SYSTEM_MACOSX) set(LINK_LIBGROUP_START "") set(LINK_LIBGROUP_END "") set(LINK_WHOLE_ARCHIVE_START -Wl,-all_load) set(LINK_WHOLE_ARCHIVE_END -Wl,-noall_load) endif() ================================================ FILE: cmake/RobomongoInstall.cmake ================================================ # Note for maintainers # -------------------- # # Do not use absolute paths in DESTINATION arguments for install() command. # Because the same install code will be executed again by CPack. And CPack will # change internally CMAKE_INSTALL_PREFIX to point to some temporary folder # for package content. # # # Temporary change set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE STRING "Install path prefix, prepended onto install directories" FORCE) if(SYSTEM_LINUX) set(bin_dir bin) set(lib_dir lib) set(resources_dir share) set(license_dir .) set(qt_plugins_dir ${lib_dir}) set(qt_conf_dir ${bin_dir}) set(qt_conf_plugins "../lib") elseif(SYSTEM_MACOSX) set(bundle_name "Robo 3T.app") set(contents_path ${bundle_name}/Contents) set(bin_dir ${contents_path}/MacOS) set(styles_dir ${contents_path}/MacOS/styles) set(lib_dir ${contents_path}/Frameworks) set(resources_dir ${contents_path}/Resources) set(license_dir ${resources_dir}) set(qt_plugins_dir ${contents_path}/PlugIns/Qt) set(qt_conf_dir ${resources_dir}) set(qt_conf_plugins "PlugIns/Qt") elseif(SYSTEM_WINDOWS) set(bin_dir .) set(styles_dir ${bin_dir}/styles) set(lib_dir .) set(resources_dir ./resources) set(license_dir .) set(qt_plugins_dir ${lib_dir}) set(qt_conf_dir ${bin_dir}) set(qt_conf_plugins .) endif() # Generate qt.conf file configure_file( "${CMAKE_SOURCE_DIR}/install/qt.conf.in" "${CMAKE_BINARY_DIR}/qt.conf") # Install qt.conf file install( FILES "${CMAKE_BINARY_DIR}/qt.conf" DESTINATION "${qt_conf_dir}") # Install OpenSSL dynamic lib files if(SYSTEM_WINDOWS) install( FILES "${OpenSSL_DIR}/libssl-1_1-x64.dll" "${OpenSSL_DIR}/libcrypto-1_1-x64.dll" DESTINATION ${bin_dir}) elseif(SYSTEM_MACOSX) install( FILES "${OpenSSL_DIR}/libssl.1.1.dylib" "${OpenSSL_DIR}/libcrypto.1.1.dylib" DESTINATION ${lib_dir}/lib) elseif(SYSTEM_LINUX) install( FILES "${OpenSSL_DIR}/libssl.so" "${OpenSSL_DIR}/libssl.so.1.0.0" "${OpenSSL_DIR}/libcrypto.so" "${OpenSSL_DIR}/libcrypto.so.1.0.0" DESTINATION ${lib_dir}) endif() # Install binary install( TARGETS robomongo RUNTIME DESTINATION ${bin_dir} BUNDLE DESTINATION .) # Install license, copyright and changelogs files install( FILES ${CMAKE_SOURCE_DIR}/LICENSE ${CMAKE_SOURCE_DIR}/COPYRIGHT ${CMAKE_SOURCE_DIR}/CHANGELOG ${CMAKE_SOURCE_DIR}/DESCRIPTION DESTINATION ${license_dir}) # Install common dependencies SET(QT_LIBS Core Gui Widgets PrintSupport Network Xml) if(NOT SYSTEM_LINUX) SET(QT_LIBS ${QT_LIBS} WebEngineWidgets WebEngineCore Quick QuickWidgets WebChannel Qml Positioning) endif() install_qt_lib(${QT_LIBS}) install_qt_plugins(QGifPlugin QICOPlugin) install_icu_libs() set(QT_STYLES_DIR ${Qt5Core_DIR}/../../../plugins/styles/) set(QT_BIN_DIR ${Qt5Core_DIR}/../../../bin/) set(QT_RESOURCES_DIR ${Qt5Core_DIR}/../../../resources/) if(SYSTEM_LINUX) install_qt_lib(XcbQpa DBus) install_qt_plugins( QXcbIntegrationPlugin) install( FILES "/usr/lib/x86_64-linux-gnu/libstdc++.so.6" "/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28" DESTINATION ${lib_dir}) elseif(SYSTEM_MACOSX) install_qt_lib(MacExtras DBus) install_qt_plugins( QCocoaIntegrationPlugin QMinimalIntegrationPlugin QOffscreenIntegrationPlugin) # Install icon install( FILES "${CMAKE_SOURCE_DIR}/install/macosx/robomongo.icns" DESTINATION "${resources_dir}") # Install styles install(FILES "${QT_STYLES_DIR}/libqmacstyle.dylib" DESTINATION ${styles_dir}) elseif(SYSTEM_WINDOWS) install_qt_plugins( QWindowsIntegrationPlugin QMinimalIntegrationPlugin QOffscreenIntegrationPlugin) # Qt WebEngine dependencies install(DIRECTORY ${QT_RESOURCES_DIR} DESTINATION ${resources_dir}) install(FILES "${QT_BIN_DIR}/libEGL.dll" "${QT_BIN_DIR}/libGLESv2.dll" "${QT_BIN_DIR}/opengl32sw.dll" "${QT_BIN_DIR}/QtWebEngineProcess.exe" DESTINATION ${bin_dir}) # Install Styles install(FILES "${QT_STYLES_DIR}/qwindowsvistastyle.dll" DESTINATION ${styles_dir}) # Install runtime libraries: # msvcp120.dll # msvcr120.dll set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .) include(InstallRequiredSystemLibraries) endif() ================================================ FILE: cmake/RobomongoInstallQt.cmake ================================================ function(install_qt_lib) foreach(module ${ARGV}) set(target_name Qt5::${module}) set(module_name Qt5${module}) # Get full path to some known Qt library (i.e. /path/to/lib/libQt5Widgets.so.5.5.1) get_target_property(target_path Qt5::Core LOCATION) # Get folder path of library (i.e. /path/to/lib) get_filename_component(qt_lib_dir ${target_path} DIRECTORY) if (SYSTEM_LINUX) # Not very good solution, but we simply take all files with *Qt5* in names (including symlinks) file(GLOB module_libs ${qt_lib_dir}/${CMAKE_SHARED_LIBRARY_PREFIX}${module_name}*) # Install to "lib" folder install(FILES ${module_libs} DESTINATION ${lib_dir}) endif() if(SYSTEM_MACOSX) set(module_name Qt${module}) # On Mac OS we are still located in .framework folder, # and we need to go one level up get_filename_component(qt_lib_dir ${qt_lib_dir} DIRECTORY) install( DIRECTORY ${qt_lib_dir}/${module_name}.framework DESTINATION ${lib_dir} USE_SOURCE_PERMISSIONS PATTERN "*_debug" EXCLUDE # Exclude debug libraries PATTERN "Headers" EXCLUDE) # Exclude Headers folders endif() if (SYSTEM_WINDOWS) # Install single DLL to lib directory install(FILES ${qt_lib_dir}/${module_name}${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION ${lib_dir}) endif() endforeach() endfunction() function(install_icu_libs) # We are trying to get 'lib' folder of Qt installation. # For this we take some known target (Qt5::Core in this case) # and taking path to this library. # Get full path to known library (i.e. /path/to/lib/libQt5Core.so.5.5.1) get_target_property(target_path Qt5::Core LOCATION) # Get absolute path to 'lib' folder (which is a parent folder of 'known' library) get_filename_component(qt_lib_dir ${target_path} DIRECTORY) # Not very good solution, but we simply take all files with *icu* in names (including symlinks) file(GLOB icu_libs ${qt_lib_dir}/${CMAKE_SHARED_LIBRARY_PREFIX}icu*) # Install to "lib" folder install(FILES ${icu_libs} DESTINATION ${lib_dir}) endfunction() # Installs Qt5 plugins. Uses value of global variable "qt_plugins_dir" as # absolute path to where install plugins. # # You can find list of plugin names by examing this variable: Qt5_PLUGINS # For example, Qt5Gui_PLUGINS. # # Debug plugins, running application in this way: # QT_DEBUG_PLUGINS=1 ./yourapp # # Some examples of plugin names: # QGifPlugin, QGtk2ThemePlugin, QIbusPlatformInputContextPlugin # # Sample: # install_qt_plugins(QGifPlugin QGtk2ThemePlugin QIbusPlatformInputContextPlugin) # function(install_qt_plugins) foreach(name ${ARGV}) set(target_name Qt5::${name}) # We are trying to get 'plugins' folder of Qt installation. # Get full path to plugin library (i.e. /path/to/plugins/imageformats/libqgif.so) get_target_property(target_path ${target_name} LOCATION) # Get absolute path to parent folder (i.e. /path/to/plugins/imageformats for Qt5::QGifPlugin) get_filename_component(plugin_dir ${target_path} DIRECTORY) # Get plugin parent directory (i.e. "imageformats" for Qt5::QGifPlugin) get_filename_component(plugin_dir_name ${plugin_dir} NAME) # Get plugin file name (i.e. "libqgif.so" for Qt5::QGifPlugin) get_filename_component(plugin_file_name ${target_path} NAME) # Get absolute path to plugins folder (i.e. /path/to/plugins) get_filename_component(plugins_dir ${plugin_dir} DIRECTORY) # Install to "lib" folder install( FILES ${target_path} DESTINATION ${qt_plugins_dir}/${plugin_dir_name}) endforeach() endfunction() ================================================ FILE: cmake/RobomongoPackage.cmake ================================================ # Notes for maintainers. # # 1. Make sure that we use only lowercase letters in file name of outputed # package for all platforms and package types. # # Get last commit hash execute_process( COMMAND git rev-parse --short HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE git_hash OUTPUT_STRIP_TRAILING_WHITESPACE) # Timestamp (not used for now) string(TIMESTAMP timestamp "%Y-%m-%d") # Package name (as it should appear in UI) set(CPACK_PACKAGE_NAME robo3t) # Version of the package set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) # Where to put generated package set(CPACK_PACKAGE_DIRECTORY ${CMAKE_BINARY_DIR}/package) # Disables the component-based install process for installers # that support it (NSIS, for instance) set(CPACK_MONOLITHIC_INSTALL ON) # Strip debug information on platforms that support it set(CPACK_STRIP_FILES ON) # Additional information set(CPACK_PACKAGE_VENDOR "3T Software Labs Ltd") set(CPACK_PACKAGE_CONTACT robo3t@3t.io) set(CPACK_PACKAGE_DESCRIPTION "Shell-centric cross-platform MongoDB management tool.") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Robo 3T is a shell-centric cross-platform MongoDB management tool.") # Use lowercase for system name and package file name string(TOLOWER ${CMAKE_SYSTEM_NAME} system_name) string(TOLOWER ${CPACK_PACKAGE_NAME} package_file_name) string(TOLOWER ${CPACK_PACKAGE_VERSION} package_file_version) # We use function from TargetArch.cmake module # Returns string with target architecture value # Output for common architectures is: i386 or x86_64 target_architecture(target_arch) # Package file name set(CPACK_PACKAGE_FILE_NAME ${package_file_name}-${package_file_version}-${system_name}-${target_arch}-${git_hash}) if(SYSTEM_LINUX) set(CPACK_GENERATOR TGZ) elseif(SYSTEM_MACOSX) set(CPACK_GENERATOR DragNDrop) set(CPACK_DMG_DS_STORE ${CMAKE_SOURCE_DIR}/install/macosx/DMG_DS_Store) elseif(SYSTEM_WINDOWS) set(files_dir "${CMAKE_SOURCE_DIR}/install/windows") set(exe_name "robo3t.exe") set(CPACK_GENERATOR NSIS ZIP) set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/DESCRIPTION") # License file will be shown in the installation wizard set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") # Default installation directory (not full path), just the path after # default "c:\Program Files" set(CPACK_PACKAGE_INSTALL_DIRECTORY "${ROBOMONGO_DISPLAY_FULL_NAME}") # A path to the executable inside installed package that contains icon # This icon is used in Windows "Add or remove programs" control panel set(CPACK_NSIS_INSTALLED_ICON_NAME "\\\\${exe_name}") # Create shortcuts on desktop and start-up menu set(CPACK_NSIS_CREATE_ICONS_EXTRA " CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${ROBOMONGO_DISPLAY_FULL_NAME}.lnk' '$INSTDIR\\\\${exe_name}' CreateShortCut '$DESKTOP\\\\${ROBOMONGO_DISPLAY_FULL_NAME}.lnk' '$INSTDIR\\\\${exe_name}' ") # Cleanup during uninstallation. This cleanup is required because shortcuts # will not be deleted automatically. # Without line '!insertmacro...' shortcut in start menu will not be removed # (i.e. it doesn't work when we referece '$STARTMENU_FOLDER' that is used # when creating shortcuts). This solution was found somewhere in internet. set(CPACK_NSIS_DELETE_ICONS_EXTRA " !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP Delete '$DESKTOP\\\\${ROBOMONGO_DISPLAY_FULL_NAME}.lnk' Delete '$SMPROGRAMS\\\\$MUI_TEMP\\\\${ROBOMONGO_DISPLAY_FULL_NAME}.lnk' ") # The title displayed at the top of the installer set(CPACK_NSIS_PACKAGE_NAME "${ROBOMONGO_DISPLAY_FULL_NAME}") # Installer images (for sidebar and topbar) set(CPACK_PACKAGE_ICON "${files_dir}\\\\nsis-topbar.bmp") set(CPACK_NSIS_INSTALLER_MUI_ICON_CODE "!define MUI_WELCOMEFINISHPAGE_BITMAP \\\"${files_dir}\\\\nsis-sidebar.bmp\\\"") # URL to a web site providing more information about application # This url was noticed at least in "Add or remove programs" control # panel. set(CPACK_NSIS_URL_INFO_ABOUT "www.robomongo.org") # Specify an executable to add an option to run on the finish # page of the NSIS installer. Without "..\\\\" it dowsn't work for NSIS 2.5 set(CPACK_NSIS_MUI_FINISHPAGE_RUN "..\\\\${exe_name}") # Trying to set for installer and uninstaller, but this is # doesn't work and default icons are still used... set(CPACK_NSIS_MUI_ICON "${files_dir}\\\\robomongo.ico") set(CPACK_NSIS_MUI_UNIICON "${files_dir}\\\\robomongo.ico") endif() include(CPack) ================================================ FILE: cmake/RobomongoPrintUtils.cmake ================================================ ### # Prints CMake variables. # # Print all variables in the current scope: # # print_vars() # print_vars("") # the same result, as previous line # # Print all variables with names, matching regex, case insensitive: # # print_vars("SOMEVAR") # print_vars("someVar") # the same result, as previous line # print_vars("^CMAKE") # print all variables, that starts from "CMAKE" # print_vars("VAR$") # ends by "VAR" # print_vars("^VAR$") # exact match # function(print_vars) if(ARGV0) set(regex ${ARGV0}) endif() get_cmake_property(names VARIABLES) foreach(name ${names}) # Make lowercase variants for case-insensitive comparing string(TOLOWER "${name}" lc_name) string(TOLOWER "${regex}" lc_regex) if(lc_name MATCHES "${lc_regex}") message("${name}=${${name}}") endif() endforeach() endfunction() ### # Print content of variable. # # print_var(PROJECT_NAME) # print_var("PROJECT_NAME") # the same result as previous line # function(print_var name) # Show nice output for not defined or empty variables if(NOT DEFINED ${name}) set(value " is not defined.") elseif("${${name}}" STREQUAL "") set(value " is defined but empty.") else() set(value "=${${name}}") endif() message("${name}${value}") endfunction() ### # Prints included directories # function(print_include_dirs) get_property(dirs DIRECTORY PROPERTY INCLUDE_DIRECTORIES) foreach(dir ${dirs}) message("${dir}") endforeach() endfunction() ### # Prints link directories # function(print_link_dirs) get_property(dirs DIRECTORY PROPERTY LINK_DIRECTORIES) foreach(dir ${dirs}) message("${dir}") endforeach() endfunction() ### # Prints compile definitions # function(print_compile_definitions) get_property(defs DIRECTORY PROPERTY COMPILE_DEFINITIONS) foreach(def ${defs}) message("${def}") endforeach() endfunction() ### # Prints target include directories # function(print_target_include_dirs target) get_property(dirs TARGET ${target} PROPERTY INTERFACE_INCLUDE_DIRECTORIES) foreach(dir ${dirs}) message("${dir}") endforeach() endfunction() ### # Prints target compile definitions # function(print_target_compile_definitions target) get_property(defs TARGET ${target} PROPERTY INTERFACE_COMPILE_DEFINITIONS) foreach(def ${defs}) message("${def}") endforeach() endfunction() ### # Prints target link libraries # function(print_target_link_libraries target) get_property(defs TARGET ${target} PROPERTY INTERFACE_LINK_LIBRARIES) foreach(def ${defs}) message("${def}") endforeach() endfunction() ### # Prints Qt5 module plugins # function(print_qt_module_plugins module) foreach(plugin ${Qt5${module}_PLUGINS}) get_target_property(loc ${plugin} LOCATION) message("Plugin ${plugin} is at location ${loc}") endforeach() endfunction() ================================================ FILE: cmake/RobomongoTargetArch.cmake ================================================ # Copyright (c) 2012 Petroules Corporation. 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. # # 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. # Based on the Qt 5 processor detection code, so should be very accurate # https://qt.gitorious.org/qt/qtbase/blobs/master/src/corelib/global/qprocessordetection.h # Currently handles arm (v5, v6, v7), x86 (32/64), ia64, and ppc (32/64) # Regarding POWER/PowerPC, just as is noted in the Qt source, # "There are many more known variants/revisions that we do not handle/detect." set(archdetect_c_code " #if defined(__arm__) || defined(__TARGET_ARCH_ARM) #if defined(__ARM_ARCH_7__) \\ || defined(__ARM_ARCH_7A__) \\ || defined(__ARM_ARCH_7R__) \\ || defined(__ARM_ARCH_7M__) \\ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 7) #error cmake_ARCH armv7 #elif defined(__ARM_ARCH_6__) \\ || defined(__ARM_ARCH_6J__) \\ || defined(__ARM_ARCH_6T2__) \\ || defined(__ARM_ARCH_6Z__) \\ || defined(__ARM_ARCH_6K__) \\ || defined(__ARM_ARCH_6ZK__) \\ || defined(__ARM_ARCH_6M__) \\ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 6) #error cmake_ARCH armv6 #elif defined(__ARM_ARCH_5TEJ__) \\ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 5) #error cmake_ARCH armv5 #else #error cmake_ARCH arm #endif #elif defined(__i386) || defined(__i386__) || defined(_M_IX86) #error cmake_ARCH i386 #elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) #error cmake_ARCH x86_64 #elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) #error cmake_ARCH ia64 #elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \\ || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \\ || defined(_M_MPPC) || defined(_M_PPC) #if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__) #error cmake_ARCH ppc64 #else #error cmake_ARCH ppc #endif #endif #error cmake_ARCH unknown ") # Set ppc_support to TRUE before including this file or ppc and ppc64 # will be treated as invalid architectures since they are no longer supported by Apple function(target_architecture output_var) if(APPLE AND CMAKE_OSX_ARCHITECTURES) # On OS X we use CMAKE_OSX_ARCHITECTURES *if* it was set # First let's normalize the order of the values # Note that it's not possible to compile PowerPC applications if you are using # the OS X SDK version 10.6 or later - you'll need 10.4/10.5 for that, so we # disable it by default # See this page for more information: # http://stackoverflow.com/questions/5333490/how-can-we-restore-ppc-ppc64-as-well-as-full-10-4-10-5-sdk-support-to-xcode-4 # Architecture defaults to i386 or ppc on OS X 10.5 and earlier, depending on the CPU type detected at runtime. # On OS X 10.6+ the default is x86_64 if the CPU supports it, i386 otherwise. foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES}) if("${osx_arch}" STREQUAL "ppc" AND ppc_support) set(osx_arch_ppc TRUE) elseif("${osx_arch}" STREQUAL "i386") set(osx_arch_i386 TRUE) elseif("${osx_arch}" STREQUAL "x86_64") set(osx_arch_x86_64 TRUE) elseif("${osx_arch}" STREQUAL "ppc64" AND ppc_support) set(osx_arch_ppc64 TRUE) else() message(FATAL_ERROR "Invalid OS X arch name: ${osx_arch}") endif() endforeach() # Now add all the architectures in our normalized order if(osx_arch_ppc) list(APPEND ARCH ppc) endif() if(osx_arch_i386) list(APPEND ARCH i386) endif() if(osx_arch_x86_64) list(APPEND ARCH x86_64) endif() if(osx_arch_ppc64) list(APPEND ARCH ppc64) endif() else() file(WRITE "${CMAKE_BINARY_DIR}/arch.c" "${archdetect_c_code}") enable_language(C) # Detect the architecture in a rather creative way... # This compiles a small C program which is a series of ifdefs that selects a # particular #error preprocessor directive whose message string contains the # target architecture. The program will always fail to compile (both because # file is not a valid C program, and obviously because of the presence of the # #error preprocessor directives... but by exploiting the preprocessor in this # way, we can detect the correct target architecture even when cross-compiling, # since the program itself never needs to be run (only the compiler/preprocessor) try_run( run_result_unused compile_result_unused "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}/arch.c" COMPILE_OUTPUT_VARIABLE ARCH CMAKE_FLAGS CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} ) # Parse the architecture name from the compiler output string(REGEX MATCH "cmake_ARCH ([a-zA-Z0-9_]+)" ARCH "${ARCH}") # Get rid of the value marker leaving just the architecture name string(REPLACE "cmake_ARCH " "" ARCH "${ARCH}") # If we are compiling with an unknown architecture this variable should # already be set to "unknown" but in the case that it's empty (i.e. due # to a typo in the code), then set it to unknown if (NOT ARCH) set(ARCH unknown) endif() endif() set(${output_var} "${ARCH}" PARENT_SCOPE) endfunction() ================================================ FILE: cmake/RobomongoTrashSymbols.cmake ================================================ if(SYSTEM_LINUX OR SYSTEM_MACOSX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") elseif(SYSTEM_WINDOWS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32 -D_CRT_SECURE_NO_WARNINGS -DBOOST_ALL_NO_LIB") # Do not show some warnings. We turned off the same warnings as MongoDB do set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4355 /wd4800 /wd4267 /wd4244 /wd4290 /wd4068 /wd4351") # The /MP option can reduce the total time to compile the source files. This option # causes the compiler to create one or more copies of itself, each in a separate process. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} /MP") set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} psapi.lib bcrypt.lib Iphlpapi.lib dbghelp.lib ws2_32.lib winmm.lib version.lib") endif() set(PROJECT_NAME "Robo 3T") set(PROJECT_NAME_TITLE ${PROJECT_NAME}) set(PROJECT_DOMAIN "www.robomongo.org") set(PROJECT_COMPANYNAME "3T Software Labs Ltd") set(PROJECT_COPYRIGHT "Copyright (C) 2014-2017 ${PROJECT_COMPANYNAME} All Rights Reserved.") set(PROJECT_COMPANYNAME_DOMAIN "https://studio3t.com/") set(PROJECT_GITHUB_FORK "www.github.com/Studio3T/robomongo") set(PROJECT_GITHUB_ISSUES "www.github.com/Studio3T/robomongo/issues") string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE) ================================================ FILE: cmake/mongodb/README.md ================================================ See these docs for the latest info about this topic: ------------- - [Build Robo 3T - Mac OS X and Linux](https://github.com/paralect/robomongo/blob/master/docs/BuildRobo3TOnMacAndLinux.md) - [Build Robo 3T - Windows](https://github.com/paralect/robomongo/blob/master/docs/BuildRobo3TOnWindows.md) ------------------------------------ What is this? ------------- Files in this folder are read by FindMongoDB.cmake script. We link with MongoDB shell using objects instead of static libs, because: > We require the use of the 'object' mode for release builds because it is the only linking > model that works across all of our platforms. We would like to ensure that all of our > released artifacts are built with the same known-good-everywhere model. This is a quote from root SConstruct file in MongoDB repository. It is possible to build MongoDB shell in the following way: $ scons mongo --link-model=static Than we will have produced static libs that we can link with. But in release mode MongoDB SConstruct file do not support static link mode by default. This command will *not* work: $ scons mongo --release --link-model=static We decided to use object link mode even for debug builds, in order to use single mode across all platforms and configurations. How did you receive content for this files? ------------------------------------------- First we build MongoDB shell with the following command: $ scons mongo -j8 --release Than we remove compiled `mongo` and run the same command again to see only final link command: $ rm build/opt/mongo/mongo $ scons mongo -j8 --release > release-link-command.txt And finally we manually copy/paste list of objects to `-release.objects` file. Make sure that they are single-line files. Almost the same steps we are doing for debug builds: $ scons mongo -j8 --dbg $ rm build/debug/mongo/mongo $ scons mongo -j8 --dbg > debug-link-command.txt And list of objects is copied to `-debug.objects` file. We found, that on Linux (Ubuntu 14.10), list of debug objects differ from list of release objects only by two files. File `debugallocation.o` is linked in debug mode, but not in release. File `debugallocation.o` is linked in release mode, but not in debug. Both are located in `third_party/gperftools-2.2/src` folder. In any way, we keep separate lists of object files for both configurations. Still we need to test that objects are the same even across different versions of Linux. ================================================ FILE: cmake/mongodb/linux-debug.objects ================================================ build/debug/third_party/icu4c-57.1/source/i18n/nfrs.o build/debug/mongo/db/index/expression_keys_private.o build/debug/third_party/gperftools-2.7/dist/src/emergency_malloc_for_stacktrace.o build/debug/third_party/icu4c-57.1/source/i18n/pluralaffix.o build/debug/third_party/icu4c-57.1/source/i18n/translit.o build/debug/mongo/shell/bench.o build/debug/third_party/boost-1.70.0/libs/program_options/src/winmain.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_finnish.o build/debug/mongo/util/net/hostandport.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lround.o build/debug/mongo/db/pipeline/aggregation_request.o build/debug/mongo/s/request_types/create_collection_gen.o build/debug/third_party/s2/s2regioncoverer.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log2.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod32.o build/debug/mongo/util/net/ssl_parameters_gen.o build/debug/third_party/icu4c-57.1/source/common/ucnv_bld.o build/debug/mongo/db/matcher/extensions_callback_noop.o build/debug/third_party/icu4c-57.1/source/i18n/coleitr.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_next.o build/debug/third_party/yaml-cpp-0.6.2/src/contrib/graphbuilderadapter.o build/debug/mongo/client/dbclient_cursor.o build/debug/mongo/s/request_types/add_shard_to_zone_request_type.o build/debug/third_party/mozjs-60/extract/mozglue/misc/Printf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int8.o build/debug/third_party/icu4c-57.1/source/i18n/vtzone.o build/debug/mongo/transport/message_compressor_manager.o build/debug/mongo/s/request_types/add_shard_request_type.o build/debug/third_party/icu4c-57.1/source/i18n/tztrans.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_logb.o build/debug/mongo/crypto/symmetric_key.o build/debug/mongo/crypto/symmetric_crypto_openssl.o build/debug/mongo/crypto/symmetric_crypto.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_lazy.o build/debug/mongo/util/errno_util.o build/debug/mongo/scripting/mozjs/mongohelpers_js.o build/debug/third_party/icu4c-57.1/source/i18n/tzfmt.o build/debug/mongo/db/matcher/expression_with_placeholder.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/zstd_common.o build/debug/mongo/db/fts/fts_matcher.o build/debug/third_party/boost-1.70.0/libs/program_options/src/utf8_codecvt_facet.o build/debug/third_party/boost-1.70.0/libs/program_options/src/parsers.o build/debug/third_party/boost-1.70.0/libs/program_options/src/options_description.o build/debug/third_party/boost-1.70.0/libs/program_options/src/split.o build/debug/third_party/boost-1.70.0/libs/program_options/src/variables_map.o build/debug/third_party/boost-1.70.0/libs/program_options/src/cmdline.o build/debug/third_party/boost-1.70.0/libs/program_options/src/config_file.o build/debug/third_party/boost-1.70.0/libs/program_options/src/value_semantic.o build/debug/third_party/boost-1.70.0/libs/program_options/src/convert.o build/debug/third_party/boost-1.70.0/libs/program_options/src/positional_options.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src27.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_acos.o build/debug/third_party/icu4c-57.1/source/common/ucnv.o build/debug/mongo/s/request_types/balance_chunk_request_type.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_asin.o build/debug/third_party/mozjs-60/extract/js/src/jit/x86-shared/Disassembler-x86-shared.o build/debug/mongo/base/global_initializer_registerer.o build/debug/third_party/gperftools-2.7/dist/src/thread_cache.o build/debug/third_party/icu4c-57.1/source/i18n/uregion.o build/debug/third_party/yaml-cpp-0.6.2/src/node.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_rem.o build/debug/mongo/s/catalog/type_locks.o build/debug/mongo/executor/network_interface_tl.o build/debug/mongo/util/net/private/socket_poll.o build/debug/mongo/util/net/sock.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_trig.o build/debug/third_party/icu4c-57.1/source/common/unistr_case.o build/debug/mongo/s/catalog/type_config_version.o build/debug/mongo/db/auth/address_restriction.o build/debug/mongo/db/auth/address_restriction_gen.o build/debug/third_party/s2/s2loop.o build/debug/mongo/scripting/mozjs/db.o build/debug/mongo/db/commands/test_commands_enabled.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint32.o build/debug/mongo/db/query/collation/collation_index_key.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_exp.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src29.o build/debug/mongo/rpc/metadata/impersonated_user_metadata_gen.o build/debug/third_party/icu4c-57.1/source/common/uarrsort.o build/debug/mongo/util/net/http_client_curl.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_quantexpd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_compare.o build/debug/third_party/yaml-cpp-0.6.2/src/exceptions.o build/debug/mongo/db/commands/test_commands_enabled_gen.o build/debug/third_party/icu4c-57.1/source/i18n/utf16collationiterator.o build/debug/mongo/db/repl/repl_settings_gen.o build/debug/third_party/icu4c-57.1/source/i18n/rbt_pars.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fma.o build/debug/mongo/client/replica_set_change_notifier.o build/debug/mongo/db/server_options_helpers_gen.o build/debug/third_party/icu4c-57.1/source/common/servls.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atanh.o build/debug/mongo/transport/message_compressor_registry.o build/debug/mongo/transport/message_compressor_metrics.o build/debug/mongo/transport/message_compressor_snappy.o build/debug/mongo/transport/message_compressor_zstd.o build/debug/mongo/transport/message_compressor_zlib.o build/debug/third_party/icu4c-57.1/source/i18n/fphdlimp.o build/debug/third_party/pcre-8.42/pcre_scanner.o build/debug/mongo/scripting/mozjs/maxkey.o build/debug/mongo/bson/bson_comparator_interface_base.o build/debug/mongo/db/catalog/index_catalog.o build/debug/third_party/icu4c-57.1/source/i18n/rbtz.o build/debug/third_party/pcre-8.42/pcre_globals.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp2.o build/debug/mongo/util/concurrency/thread_name.o build/debug/third_party/yaml-cpp-0.6.2/src/emitter.o build/debug/third_party/icu4c-57.1/source/common/dtintrv.o build/debug/mongo/util/periodic_runner.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lgamma.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src16.o build/debug/mongo/db/repl_set_member_in_standalone_mode.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atanh.o build/debug/mongo/db/query/datetime/date_time_support.o build/debug/mongo/scripting/mozjs/session.o build/debug/mongo/rpc/object_check.o build/debug/third_party/icu4c-57.1/source/common/ustr_cnv.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_asin.o build/debug/mongo/util/fail_point.o build/debug/mongo/db/query/hint_gen.o build/debug/mongo/scripting/mozjs/proxyscope.o build/debug/mongo/platform/decimal128.o build/debug/mongo/s/request_types/get_database_version_gen.o build/debug/mongo/platform/posix_fadvise.o build/debug/third_party/icu4c-57.1/source/i18n/sortkey.o build/debug/mongo/scripting/mozjs/status.o build/debug/third_party/icu4c-57.1/source/i18n/csrsbcs.o build/debug/mongo/shell/shell_options_gen.o build/debug/mongo/rpc/metadata/repl_set_metadata.o build/debug/third_party/yaml-cpp-0.6.2/src/convert.o build/debug/mongo/db/fts/unicode/codepoints_delimiter_list.o build/debug/mongo/db/pipeline/exchange_spec_gen.o build/debug/third_party/zlib-1.2.11/inftrees.o build/debug/mongo/db/dbmessage.o build/debug/third_party/fmt/dist/src/posix.o build/debug/third_party/fmt/dist/src/format.o build/debug/mongo/s/request_types/clone_collection_options_from_primary_shard_gen.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_hungarian.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_porter.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_dutch.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_russian.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_romanian.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_danish.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_portuguese.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_french.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_italian.o build/debug/third_party/libstemmer_c/runtime/api.o build/debug/third_party/libstemmer_c/libstemmer/libstemmer_utf8.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_swedish.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_german.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_spanish.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_norwegian.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_turkish.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_english.o build/debug/mongo/db/matcher/schema/expression_internal_schema_str_length.o build/debug/third_party/icu4c-57.1/source/i18n/msgfmt.o build/debug/third_party/abseil-cpp-master/abseil-cpp/absl/hash/internal/city.o build/debug/mongo/util/net/hostandport_gen.o build/debug/mongo/transport/transport_layer.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_log10.o build/debug/third_party/s2/s1angle.o build/debug/mongo/util/debugger.o build/debug/mongo/db/repl/bson_extract_optime.o build/debug/third_party/icu4c-57.1/source/i18n/collationfastlatin.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint64.o build/debug/third_party/icu4c-57.1/source/common/ucharstriebuilder.o build/debug/third_party/icu4c-57.1/source/i18n/search.o build/debug/mongo/util/background.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fdimd.o build/debug/mongo/s/catalog/mongo_version_range.o build/debug/third_party/icu4c-57.1/source/i18n/regextxt.o build/debug/mongo/util/secure_compare_memory.o build/debug/mongo/logger/component_message_log_domain.o build/debug/third_party/icu4c-57.1/source/i18n/affixpatternparser.o build/debug/third_party/icu4c-57.1/source/i18n/collationdatawriter.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fdimd.o build/debug/mongo/db/commands/server_status.o build/debug/mongo/scripting/mozjs/engine.o build/debug/third_party/icu4c-57.1/source/common/usetiter.o build/debug/third_party/icu4c-57.1/source/common/locdispnames.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_from_int.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cbrt.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src0.o build/debug/mongo/util/options_parser/value.o build/debug/third_party/icu4c-57.1/source/i18n/rulebasedcollator.o build/debug/mongo/db/index/sort_key_generator.o build/debug/third_party/icu4c-57.1/source/common/uniset.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tan.o build/debug/mongo/db/fts/fts_unicode_tokenizer.o build/debug/third_party/icu4c-57.1/source/i18n/region.o build/debug/third_party/icu4c-57.1/source/i18n/tridpars.o build/debug/third_party/icu4c-57.1/source/i18n/tmunit.o build/debug/third_party/icu4c-57.1/source/common/servslkf.o build/debug/third_party/pcre-8.42/pcre_get.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_string.o build/debug/third_party/asio-master/asio/src/asio.o build/debug/mongo/s/request_types/remove_shard_from_zone_request_type.o build/debug/third_party/timelib-2018.01/parse_iso_intervals.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_bid128.o build/debug/third_party/mozjs-60/extract/js/src/vm/JSAtom.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_frexp.o build/debug/third_party/icu4c-57.1/source/i18n/collationbuilder.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src40.o build/debug/mongo/base/data_type_terminated.o build/debug/third_party/icu4c-57.1/source/i18n/repattrn.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src45.o build/debug/mongo/util/platform_init.o build/debug/mongo/db/auth/authorization_session.o build/debug/mongo/db/auth/role_name.o build/debug/mongo/db/auth/auth_decorations.o build/debug/mongo/db/auth/authorization_manager.o build/debug/mongo/db/auth/user_name.o build/debug/mongo/db/keys_collection_document.o build/debug/third_party/icu4c-57.1/source/i18n/uspoof.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_scalb.o build/debug/mongo/db/commands/server_status_internal.o build/debug/mongo/db/logical_clock_gen.o build/debug/mongo/db/logical_clock.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_rem.o build/debug/mongo/util/net/socket_exception.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log1p.o build/debug/third_party/icu4c-57.1/source/common/normalizer2.o build/debug/mongo/util/net/hostname_canonicalization.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_expm1.o build/debug/third_party/icu4c-57.1/source/i18n/coll.o build/debug/mongo/db/baton.o build/debug/mongo/db/operation_context.o build/debug/mongo/db/unclean_shutdown.o build/debug/mongo/db/server_recovery.o build/debug/mongo/db/service_context.o build/debug/mongo/db/default_baton.o build/debug/mongo/db/client.o build/debug/mongo/db/operation_context_group.o build/debug/mongo/db/query/getmore_request.o build/debug/third_party/icu4c-57.1/source/common/pluralmap.o build/debug/mongo/db/matcher/expression_array.o build/debug/third_party/icu4c-57.1/source/i18n/collationdata.o build/debug/third_party/shim_timelib.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src20.o build/debug/third_party/icu4c-57.1/source/common/uhash.o build/debug/third_party/icu4c-57.1/source/common/cmemory.o build/debug/mongo/db/keypattern.o build/debug/third_party/icu4c-57.1/source/i18n/digitgrouping.o build/debug/third_party/icu4c-57.1/source/i18n/tmutfmt.o build/debug/third_party/icu4c-57.1/source/i18n/tznames.o build/debug/mongo/s/request_types/move_primary_gen.o build/debug/mongo/util/fail_point_server_parameter_gen.o build/debug/mongo/s/request_types/set_shard_version_request.o build/debug/third_party/icu4c-57.1/source/i18n/collationfcd.o build/debug/third_party/mozjs-60/extract/mozglue/misc/Mutex_posix.o build/debug/third_party/icu4c-57.1/source/common/bytestream.o build/debug/third_party/icu4c-57.1/source/i18n/csdetect.o build/debug/mongo/scripting/mozjs/object.o build/debug/mongo/db/matcher/matcher.o build/debug/third_party/pcre-8.42/pcre_version.o build/debug/mongo/db/fts/unicode/string.o build/debug/mongo/db/ops/write_ops_parsers.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int32.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/xxhash.o build/debug/third_party/icu4c-57.1/source/common/unistr_cnv.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src9.o build/debug/mongo/db/repl/replication_consistency_markers.o build/debug/mongo/db/repl/replication_process.o build/debug/mongo/db/auth/impersonation_session.o build/debug/third_party/icu4c-57.1/source/i18n/fmtable.o build/debug/mongo/db/matcher/matcher_type_set.o build/debug/mongo/scripting/mozjs/idwrapper.o build/debug/mongo/util/background_thread_clock_source.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops_64.o build/debug/third_party/icu4c-57.1/source/common/unifilt.o build/debug/mongo/base/data_range_cursor.o build/debug/mongo/s/request_types/create_database_gen.o build/debug/mongo/scripting/mozjs/mongohelpers.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_log2.o build/debug/mongo/db/repl/is_master_response.o build/debug/third_party/yaml-cpp-0.6.2/src/contrib/graphbuilder.o build/debug/mongo/util/concurrency/ticketholder.o build/debug/third_party/icu4c-57.1/source/i18n/utrans.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/utf8_codecvt_facet.o build/debug/third_party/icu4c-57.1/source/common/ulistformatter.o build/debug/mongo/util/net/socket_utils.o build/debug/mongo/db/command_generic_argument.o build/debug/mongo/db/logical_session_id_helpers.o build/debug/mongo/scripting/deadline_monitor.o build/debug/mongo/s/request_types/split_chunk_request_type.o build/debug/third_party/icu4c-57.1/source/i18n/regexst.o build/debug/third_party/s2/s2pointregion.o build/debug/mongo/util/md5.o build/debug/third_party/icu4c-57.1/source/common/bytestrie.o build/debug/mongo/transport/service_executor_synchronous.o build/debug/mongo/s/request_types/merge_chunk_request_type.o build/debug/mongo/rpc/metadata/tracking_metadata.o build/debug/third_party/icu4c-57.1/source/i18n/gregocal.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_string.o build/debug/third_party/icu4c-57.1/source/i18n/tolowtrn.o build/debug/third_party/pcre-8.42/pcre_refcount.o build/debug/third_party/yaml-cpp-0.6.2/src/binary.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_double_fast.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_nexttowardd.o build/debug/mongo/util/exit.o build/debug/third_party/yaml-cpp-0.6.2/src/tag.o build/debug/third_party/yaml-cpp-0.6.2/src/directives.o build/debug/third_party/yaml-cpp-0.6.2/src/simplekey.o build/debug/third_party/yaml-cpp-0.6.2/src/scantag.o build/debug/third_party/yaml-cpp-0.6.2/src/singledocparser.o build/debug/third_party/yaml-cpp-0.6.2/src/nodeevents.o build/debug/third_party/yaml-cpp-0.6.2/src/memory.o build/debug/third_party/yaml-cpp-0.6.2/src/ostream_wrapper.o build/debug/third_party/yaml-cpp-0.6.2/src/emitfromevents.o build/debug/third_party/yaml-cpp-0.6.2/src/regex_yaml.o build/debug/third_party/yaml-cpp-0.6.2/src/scanscalar.o build/debug/third_party/yaml-cpp-0.6.2/src/stream.o build/debug/third_party/yaml-cpp-0.6.2/src/emitterutils.o build/debug/third_party/yaml-cpp-0.6.2/src/exp.o build/debug/third_party/yaml-cpp-0.6.2/src/nodebuilder.o build/debug/third_party/yaml-cpp-0.6.2/src/scantoken.o build/debug/third_party/yaml-cpp-0.6.2/src/null.o build/debug/third_party/yaml-cpp-0.6.2/src/parser.o build/debug/third_party/yaml-cpp-0.6.2/src/parse.o build/debug/third_party/yaml-cpp-0.6.2/src/scanner.o build/debug/third_party/yaml-cpp-0.6.2/src/emitterstate.o build/debug/third_party/yaml-cpp-0.6.2/src/emit.o build/debug/third_party/yaml-cpp-0.6.2/src/node_data.o build/debug/mongo/db/query/killcursors_request.o build/debug/mongo/db/pipeline/field_path.o build/debug/third_party/icu4c-57.1/source/i18n/zrule.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_quantize.o build/debug/mongo/platform/shared_library_posix.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_nearbyint.o build/debug/third_party/icu4c-57.1/source/i18n/digitinterval.o build/debug/mongo/db/storage/duplicate_key_error_info.o build/debug/third_party/icu4c-57.1/source/common/ucharstrieiterator.o build/debug/third_party/pcre-8.42/pcre_stringpiece.o build/debug/third_party/icu4c-57.1/source/i18n/csr2022.o build/debug/mongo/db/auth/action_set.o build/debug/third_party/mozjs-60/extract/mozglue/misc/ConditionVariable_posix.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_nearbyintd.o build/debug/mongo/db/catalog/index_key_validate.o build/debug/third_party/gperftools-2.7/dist/src/internal_logging.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sin.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_acos.o build/debug/mongo/bson/timestamp.o build/debug/mongo/db/global_settings.o build/debug/third_party/icu4c-57.1/source/i18n/sharedbreakiterator.o build/debug/third_party/icu4c-57.1/source/common/ures_cnv.o build/debug/third_party/s2/util/math/mathutil.o build/debug/mongo/util/time_support.o build/debug/third_party/wiredtiger/src/checksum/x86/crc32-x86-alt.o build/debug/third_party/s2/strings/strutil.o build/debug/third_party/icu4c-57.1/source/i18n/measure.o build/debug/mongo/s/request_types/update_zone_key_range_request_type.o build/debug/mongo/db/pipeline/expression_context.o build/debug/mongo/db/repl/rollback_gen.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lrintd.o build/debug/third_party/mozjs-60/extract/js/src/frontend/Parser.o build/debug/mongo/util/net/ssl_options_client.o build/debug/third_party/gperftools-2.7/dist/src/debugallocation.o build/debug/mongo/rpc/metadata/egress_metadata_hook_list.o build/debug/mongo/rpc/metadata/config_server_metadata.o build/debug/mongo/rpc/metadata.o build/debug/mongo/rpc/metadata/logical_time_metadata.o build/debug/mongo/rpc/metadata/oplog_query_metadata.o build/debug/mongo/rpc/metadata/sharding_metadata.o build/debug/third_party/icu4c-57.1/source/i18n/digitformatter.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src22.o build/debug/third_party/icu4c-57.1/source/i18n/decimalformatpattern.o build/debug/mongo/db/namespace_string.o build/debug/mongo/executor/connection_pool_tl.o build/debug/mongo/db/keys_collection_client_sharded.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tan.o build/debug/mongo/db/query/explain_options.o build/debug/third_party/icu4c-57.1/source/i18n/ucol_sit.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src37.o build/debug/mongo/shell/shell_options_init.o build/debug/mongo/db/index_names.o build/debug/mongo/db/bson/dotted_path_support.o build/debug/third_party/icu4c-57.1/source/common/ucase.o build/debug/mongo/db/matcher/expression_parser.o build/debug/third_party/kms-message/src/sort.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_lgamma.o build/debug/mongo/db/repl/repl_set_heartbeat_response.o build/debug/third_party/icu4c-57.1/source/common/ucnv_u16.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_truncf.o build/debug/mongo/db/fts/stop_words.o build/debug/mongo/s/catalog/type_chunk.o build/debug/third_party/gperftools-2.7/dist/src/base/elf_mem_image.o build/debug/third_party/zlib-1.2.11/deflate.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstdmt_compress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/pool.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_opt.o build/debug/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_compress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/decompress/huf_decompress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/entropy_common.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_ldm.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/fse_compress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/huf_compress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/divsufsort.o build/debug/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/zdict.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/hist.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/threading.o build/debug/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/fastcover.o build/debug/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_common.o build/debug/third_party/zstandard-1.3.7/zstd/lib/decompress/zstd_decompress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/error_private.o build/debug/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_decompress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_fast.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/fse_decompress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/cover.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_compress.o build/debug/third_party/icu4c-57.1/source/i18n/plurfmt.o build/debug/mongo/s/request_types/flush_database_cache_updates_gen.o build/debug/mongo/db/catalog/collection.o build/debug/mongo/db/repl/optime.o build/debug/mongo/base/init.o build/debug/third_party/gperftools-2.7/dist/src/base/logging.o build/debug/third_party/icu4c-57.1/source/common/uiter.o build/debug/third_party/icu4c-57.1/source/common/ustrtrns.o build/debug/mongo/util/winutil.o build/debug/third_party/shim_asio.o build/debug/third_party/icu4c-57.1/source/common/ubidi_props.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint64.o build/debug/third_party/icu4c-57.1/source/i18n/anytrans.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src28.o build/debug/mongo/shell/mongo.o build/debug/mongo/s/request_types/flush_routing_table_cache_updates_gen.o build/debug/mongo/s/catalog/type_mongos.o build/debug/mongo/s/catalog/type_collection.o build/debug/mongo/s/catalog/type_chunk_base_gen.o build/debug/mongo/s/shard_id.o build/debug/mongo/s/request_types/wait_for_fail_point_gen.o build/debug/mongo/s/catalog/type_shard_database.o build/debug/mongo/s/request_types/clear_jumbo_flag_gen.o build/debug/mongo/s/database_version_gen.o build/debug/mongo/s/catalog/type_shard_collection.o build/debug/mongo/s/catalog/type_lockpings.o build/debug/mongo/s/catalog/type_tags.o build/debug/mongo/s/request_types/shard_collection_gen.o build/debug/mongo/s/request_types/clone_catalog_data_gen.o build/debug/mongo/s/cannot_implicitly_create_collection_info.o build/debug/mongo/s/would_change_owning_shard_exception.o build/debug/mongo/s/request_types/migration_secondary_throttle_options.o build/debug/mongo/s/chunk_version_gen.o build/debug/mongo/s/request_types/commit_chunk_migration_request_type.o build/debug/mongo/s/catalog/type_changelog.o build/debug/mongo/s/catalog/type_database.o build/debug/mongo/s/chunk_version.o build/debug/mongo/s/catalog/type_shard.o build/debug/mongo/s/stale_exception.o build/debug/mongo/s/database_version_helpers.o build/debug/mongo/s/request_types/move_chunk_request.o build/debug/mongo/db/query/tailable_mode_gen.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp10.o build/debug/mongo/util/assert_util.o build/debug/mongo/shell/linenoise.o build/debug/mongo/shell/shell_utils_extended.o build/debug/mongo/shell/shell_options.o build/debug/mongo/shell/shell_utils.o build/debug/mongo/shell/shell_utils_launcher.o build/debug/mongo/shell/mk_wcwidth.o build/debug/mongo/shell/mongo-server.o build/debug/mongo/client/authenticate.o build/debug/mongo/util/signal_handlers.o build/debug/mongo/platform/strcasestr.o build/debug/mongo/util/password_digest.o build/debug/mongo/scripting/mozjs/cursor.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_hypot.o build/debug/third_party/icu4c-57.1/source/common/unistr_titlecase_brkiter.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_minmax.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fdimd.o build/debug/mongo/db/logical_session_id_gen.o build/debug/mongo/db/write_concern_options.o build/debug/third_party/icu4c-57.1/source/common/ustrcase.o build/debug/mongo/db/catalog/collection_catalog.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_llrintd.o build/debug/third_party/icu4c-57.1/source/i18n/uspoof_build.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_next.o build/debug/mongo/base/parse_number.o build/debug/mongo/util/boost_assert_shim.o build/debug/third_party/s2/s2r2rect.o build/debug/mongo/util/clock_source.o build/debug/mongo/util/fast_clock_source_factory.o build/debug/mongo/db/pipeline/document_source_merge_spec.o build/debug/third_party/icu4c-57.1/source/i18n/csrucode.o build/debug/mongo/db/pipeline/dependencies.o build/debug/third_party/icu4c-57.1/source/common/uniset_props.o build/debug/third_party/icu4c-57.1/source/common/ucnv_lmb.o build/debug/mongo/db/fts/fts_spec_legacy.o build/debug/third_party/icu4c-57.1/source/common/ucnvdisp.o build/debug/mongo/platform/strnlen.o build/debug/mongo/db/logical_session_id.o build/debug/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.o build/debug/mongo/db/repl/storage_interface.o build/debug/third_party/shim_yaml.o build/debug/mongo/scripting/mozjs/valuereader.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sub.o build/debug/mongo/db/matcher/matchable.o build/debug/third_party/icu4c-57.1/source/common/rbbitblb.o build/debug/third_party/snappy-1.1.7/snappy.o build/debug/third_party/snappy-1.1.7/snappy-sinksource.o build/debug/third_party/snappy-1.1.7/snappy-c.o build/debug/third_party/icu4c-57.1/source/i18n/funcrepl.o build/debug/third_party/icu4c-57.1/source/i18n/collationfastlatinbuilder.o build/debug/mongo/bson/simple_bsonelement_comparator.o build/debug/third_party/s2/s1interval.o build/debug/third_party/kms-message/src/kms_crypto_openssl.o build/debug/third_party/icu4c-57.1/source/common/uresbund.o build/debug/mongo/db/storage/storage_parameters_gen.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src1.o build/debug/mongo/util/fail_point_registry.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_flag_operations.o build/debug/third_party/icu4c-57.1/source/i18n/hebrwcal.o build/debug/mongo/util/icu.o build/debug/third_party/s2/base/stringprintf.o build/debug/mongo/client/global_conn_pool_gen.o build/debug/mongo/util/file.o build/debug/mongo/crypto/aead_encryption.o build/debug/third_party/icu4c-57.1/source/common/ucnvhz.o build/debug/mongo/scripting/deadline_monitor_gen.o build/debug/third_party/icu4c-57.1/source/i18n/brktrans.o build/debug/mongo/db/pipeline/document_path_support.o build/debug/third_party/icu4c-57.1/source/common/rbbinode.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod128.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src10.o build/debug/third_party/pcre-8.42/pcre_xclass.o build/debug/third_party/s2/strings/stringprintf.o build/debug/mongo/db/fts/fts_query_noop.o build/debug/third_party/s2/s2cellid.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src44.o build/debug/third_party/icu4c-57.1/source/common/dictionarydata.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_round_integral.o build/debug/mongo/scripting/mozjs/jsthread.o build/debug/mongo/transport/service_executor_adaptive.o build/debug/mongo/db/multi_key_path_tracker.o build/debug/third_party/icu4c-57.1/source/common/uenum.o build/debug/third_party/icu4c-57.1/source/common/parsepos.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_ceil.o build/debug/third_party/mozjs-60/extract/js/src/perf/pm_stub.o build/debug/third_party/mozjs-60/extract/js/src/vm/Interpreter.o build/debug/third_party/icu4c-57.1/source/i18n/cecal.o build/debug/mongo/scripting/mozjs/bson.o build/debug/third_party/gperftools-2.7/dist/src/malloc_extension.o build/debug/third_party/icu4c-57.1/source/common/ucnv_err.o build/debug/third_party/icu4c-57.1/source/i18n/valueformatter.o build/debug/third_party/icu4c-57.1/source/i18n/curramt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atan.o build/debug/third_party/gperftools-2.7/dist/src/base/spinlock.o build/debug/third_party/icu4c-57.1/source/common/cwchar.o build/debug/third_party/shim_snappy.o build/debug/mongo/db/fts/fts_spec.o build/debug/mongo/util/startup_test.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_mod.o build/debug/mongo/util/net/sockaddr.o build/debug/mongo/db/concurrency/lock_manager.o build/debug/mongo/scripting/mozjs/base.o build/debug/mongo/util/system_clock_source.o build/debug/third_party/icu4c-57.1/source/i18n/ucln_in.o build/debug/mongo/db/stats/counters.o build/debug/mongo/db/wire_version.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint8.o build/debug/third_party/s2/util/coding/coder.o build/debug/mongo/db/repl/read_concern_args.o build/debug/mongo/db/pipeline/variables.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log.o build/debug/third_party/gperftools-2.7/dist/src/memfs_malloc.o build/debug/third_party/icu4c-57.1/source/common/unistr.o build/debug/third_party/icu4c-57.1/source/i18n/measfmt.o build/debug/mongo/db/fts/fts_basic_tokenizer.o build/debug/third_party/icu4c-57.1/source/i18n/csmatch.o build/debug/mongo/util/intrusive_counter.o build/debug/third_party/icu4c-57.1/source/i18n/ucal.o build/debug/mongo/db/pipeline/parsed_aggregation_projection_node.o build/debug/mongo/rpc/legacy_request.o build/debug/mongo/util/concurrency/idle_thread_block.o build/debug/third_party/gperftools-2.7/dist/src/stack_trace_table.o build/debug/third_party/shim_zlib.o build/debug/third_party/shim_icu.o build/debug/mongo/scripting/engine.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_log.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src15.o build/debug/third_party/mozjs-60/extract/js/src/builtin/RegExp.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src4.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src23.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_atan.o build/debug/third_party/mozjs-60/extract/js/src/jsarray.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_sinh.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src6.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_atanh.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_floorf.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src12.o build/debug/third_party/mozjs-60/extract/mfbt/lz4.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_fabs.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/k_exp.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_floor.o build/debug/third_party/mozjs-60/extract/js/src/util/DoubleToString.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_ceilf.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src2.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src39.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_trunc.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_sqrt.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src7.o build/debug/third_party/mozjs-60/extract/js/src/mfbt/Unified_cpp_mfbt0.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src43.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_pow.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src31.o build/debug/third_party/mozjs-60/mongo_sources/freeOpToJSContext.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src13.o build/debug/third_party/mozjs-60/mongo_sources/mongoErrorReportToString.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src19.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src18.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_asinh.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src30.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src14.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src34.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src42.o build/debug/third_party/mozjs-60/extract/js/src/gc/StoreBuffer.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src26.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src8.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src25.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_tanh.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_rint.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_atan2.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src36.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src17.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src33.o build/debug/third_party/mozjs-60/extract/mozglue/misc/TimeStamp.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src5.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src3.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src24.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src35.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src21.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_copysign.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_acosh.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src11.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_rintf.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src38.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_expm1.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_cbrt.o build/debug/third_party/mozjs-60/extract/js/src/jsmath.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_scalbn.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src32.o build/debug/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src41.o build/debug/third_party/mozjs-60/extract/mfbt/Compression.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_log1p.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_asin.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_cosh.o build/debug/third_party/mozjs-60/extract/mfbt/double-conversion/double-conversion/strtod.o build/debug/third_party/mozjs-60/extract/mozglue/misc/TimeStamp_posix.o build/debug/third_party/mozjs-60/extract/mozglue/misc/StackWalk.o build/debug/third_party/shim_intel_decimal128.o build/debug/third_party/s2/base/strtoint.o build/debug/third_party/s2/s2latlngrect.o build/debug/third_party/icu4c-57.1/source/i18n/rbt_data.o build/debug/mongo/crypto/sha256_block.o build/debug/third_party/icu4c-57.1/source/i18n/indiancal.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_expm1.o build/debug/mongo/client/native_sasl_client_session.o build/debug/mongo/db/field_ref_set.o build/debug/third_party/icu4c-57.1/source/i18n/decfmtst.o build/debug/mongo/db/matcher/expression_algo.o build/debug/third_party/kms-message/src/kms_response.o build/debug/third_party/kms-message/src/hexlify.o build/debug/third_party/kms-message/src/kms_request_opt.o build/debug/third_party/kms-message/src/kms_b64.o build/debug/third_party/kms-message/src/kms_message.o build/debug/third_party/kms-message/src/kms_encrypt_request.o build/debug/third_party/kms-message/src/kms_kv_list.o build/debug/third_party/kms-message/src/kms_request.o build/debug/third_party/kms-message/src/kms_response_parser.o build/debug/third_party/kms-message/src/kms_decrypt_request.o build/debug/third_party/kms-message/src/kms_request_str.o build/debug/mongo/db/fts/unicode/codepoints_casefold.o build/debug/mongo/db/ops/write_ops_gen.o build/debug/third_party/icu4c-57.1/source/i18n/csrutf8.o build/debug/mongo/db/matcher/schema/encrypt_schema_types.o build/debug/third_party/s2/s2cellunion.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_acosh.o build/debug/mongo/util/net/ssl_options.o build/debug/third_party/icu4c-57.1/source/i18n/uni2name.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_pow.o build/debug/third_party/icu4c-57.1/source/i18n/ulocdata.o build/debug/third_party/icu4c-57.1/source/common/loclikely.o build/debug/third_party/s2/s2.o build/debug/third_party/timelib-2018.01/interval.o build/debug/mongo/db/matcher/rewrite_expr.o build/debug/mongo/db/matcher/schema/expression_internal_schema_eq.o build/debug/mongo/db/pipeline/document.o build/debug/third_party/icu4c-57.1/source/common/resbund_cnv.o build/debug/mongo/base/secure_allocator.o build/debug/mongo/bson/oid.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/operations.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/windows_file_codecvt.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/codecvt_error_category.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/path_traits.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/portability.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/path.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/unique_path.o build/debug/third_party/icu4c-57.1/source/common/uts46.o build/debug/mongo/db/repl/split_horizon.o build/debug/third_party/icu4c-57.1/source/common/uset.o build/debug/third_party/shim_allocator.o build/debug/third_party/pcre-8.42/pcre_ucd.o build/debug/mongo/bson/bson_depth.o build/debug/third_party/s2/base/int128.o build/debug/third_party/pcre-8.42/pcre_maketables.o build/debug/mongo/db/catalog/disable_index_spec_namespace_generation_gen.o build/debug/third_party/icu4c-57.1/source/common/ushape.o build/debug/third_party/gperftools-2.7/dist/src/sampler.o build/debug/mongo/util/net/private/ssl_expiration.o build/debug/mongo/util/timer.o build/debug/mongo/db/server_options_helpers.o build/debug/mongo/logger/log_severity.o build/debug/mongo/db/fts/unicode/codepoints_diacritic_map.o build/debug/mongo/base/transaction_error.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lrintd.o build/debug/mongo/executor/remote_command_request.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sin.o build/debug/mongo/rpc/metadata/client_metadata.o build/debug/mongo/rpc/metadata/client_metadata_ismaster.o build/debug/mongo/shell/linenoise_utf8.o build/debug/mongo/util/net/cidr.o build/debug/third_party/icu4c-57.1/source/common/usc_impl.o build/debug/third_party/icu4c-57.1/source/i18n/dayperiodrules.o build/debug/third_party/icu4c-57.1/source/common/ucharstrie.o build/debug/mongo/db/repl/update_position_args.o build/debug/mongo/scripting/mozjs/code.o build/debug/mongo/bson/bsonobjbuilder.o build/debug/third_party/boost-1.70.0/libs/system/src/error_code.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tgamma.o build/debug/mongo/db/server_options.o build/debug/third_party/icu4c-57.1/source/common/propname.o build/debug/mongo/rpc/metadata/impersonated_user_metadata.o build/debug/mongo/db/index/index_descriptor.o build/debug/mongo/db/matcher/expression.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_asin.o build/debug/third_party/pcre-8.42/pcre_string_utils.o build/debug/third_party/icu4c-57.1/source/common/ucol_swp.o build/debug/third_party/icu4c-57.1/source/common/propsvec.o build/debug/mongo/db/matcher/schema/expression_internal_schema_num_properties.o build/debug/mongo/db/fts/fts_util.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lgamma.o build/debug/third_party/zlib-1.2.11/trees.o build/debug/mongo/db/fts/fts_element_iterator.o build/debug/mongo/util/net/ssl_parameters.o build/debug/mongo/util/net/ssl_manager_openssl.o build/debug/mongo/util/net/ssl_manager.o build/debug/mongo/util/net/ssl_stream.o build/debug/third_party/icu4c-57.1/source/common/ucnv_cnv.o build/debug/mongo/crypto/sha1_block.o build/debug/mongo/rpc/object_check_gen.o build/debug/mongo/db/repl/repl_set_config_gen.o build/debug/third_party/icu4c-57.1/source/i18n/rbnf.o build/debug/third_party/icu4c-57.1/source/i18n/collationweights.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_expm1.o build/debug/mongo/scripting/mozjs/regexp.o build/debug/mongo/util/processinfo_linux.o build/debug/mongo/db/pipeline/document_source_merge_gen.o build/debug/third_party/icu4c-57.1/source/i18n/digitlst.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint64.o build/debug/mongo/db/key_generator.o build/debug/third_party/icu4c-57.1/source/i18n/uitercollationiterator.o build/debug/third_party/icu4c-57.1/source/common/ustrfmt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log.o build/debug/third_party/icu4c-57.1/source/i18n/nfrule.o build/debug/third_party/s2/s2region.o build/debug/mongo/db/auth/privilege.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atan2.o build/debug/third_party/boost-1.70.0/libs/iostreams/src/file_descriptor.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fegetexceptflag.o build/debug/mongo/db/server_options_general_gen.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tanh.o build/debug/mongo/db/query/killcursors_response.o build/debug/mongo/db/query/view_response_formatter.o build/debug/mongo/db/query/cursor_response.o build/debug/mongo/db/query/find_and_modify_request.o build/debug/mongo/db/query/count_command_as_aggregation_command.o build/debug/mongo/db/query/count_command_gen.o build/debug/mongo/db/query/cursor_request.o build/debug/mongo/db/query/count_request.o build/debug/mongo/db/pipeline/value.o build/debug/mongo/base/system_error.o build/debug/third_party/icu4c-57.1/source/i18n/upluralrules.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_minmax.o build/debug/mongo/rpc/legacy_reply_builder.o build/debug/mongo/transport/session.o build/debug/mongo/transport/service_entry_point_utils.o build/debug/mongo/rpc/legacy_reply.o build/debug/third_party/icu4c-57.1/source/common/uset_props.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_decimal_globals.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_logbd.o build/debug/third_party/icu4c-57.1/source/common/uinvchar.o build/debug/mongo/db/traffic_reader.o build/debug/mongo/client/sasl_client_authenticate_impl.o build/debug/mongo/client/sasl_plain_client_conversation.o build/debug/mongo/client/sasl_scram_client_conversation.o build/debug/mongo/client/sasl_client_session.o build/debug/mongo/client/sasl_client_conversation.o build/debug/mongo/client/sasl_client_authenticate.o build/debug/third_party/icu4c-57.1/source/i18n/taiwncal.o build/debug/mongo/db/fts/fts_basic_phrase_matcher.o build/debug/third_party/icu4c-57.1/source/common/serv.o build/debug/third_party/icu4c-57.1/source/common/locdspnm.o build/debug/mongo/db/repl/replication_coordinator_noop.o build/debug/mongo/db/repl/repl_client_info.o build/debug/mongo/db/repl/replication_coordinator.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_add.o build/debug/mongo/db/pipeline/value_comparator.o build/debug/third_party/icu4c-57.1/source/common/uchriter.o build/debug/mongo/s/is_mongos.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int64.o build/debug/third_party/icu4c-57.1/source/common/util.o build/debug/third_party/murmurhash3/MurmurHash3.o build/debug/third_party/icu4c-57.1/source/common/uobject.o build/debug/mongo/util/password_params_gen.o build/debug/mongo/util/version.o build/debug/third_party/icu4c-57.1/source/i18n/uspoof_impl.o build/debug/third_party/icu4c-57.1/source/i18n/inputext.o build/debug/third_party/s2/s2cell.o build/debug/third_party/icu4c-57.1/source/i18n/ucol_res.o build/debug/third_party/icu4c-57.1/source/i18n/precision.o build/debug/mongo/util/fail_point_service.o build/debug/third_party/pcre-8.42/pcre_study.o build/debug/mongo/scripting/jsexception.o build/debug/mongo/scripting/mozjs/internedstring.o build/debug/third_party/s2/base/logging.o build/debug/mongo/shell/mongodbcr.o build/debug/mongo/db/fts/unicode/codepoints_diacritic_list.o build/debug/third_party/icu4c-57.1/source/common/brkiter.o build/debug/mongo/client/index_spec.o build/debug/mongo/util/icu_init.o build/debug/mongo/scripting/mozjs/exception.o build/debug/third_party/icu4c-57.1/source/i18n/transreg.o build/debug/mongo/db/server_options_nongeneral_gen.o build/debug/third_party/icu4c-57.1/source/common/umath.o build/debug/third_party/icu4c-57.1/source/i18n/plurrule.o build/debug/mongo/db/storage/key_string.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_inv_trig.o build/debug/mongo/base/initializer.o build/debug/third_party/timelib-2018.01/parse_zoneinfo.o build/debug/third_party/timelib-2018.01/timelib.o build/debug/third_party/timelib-2018.01/parse_tz.o build/debug/third_party/timelib-2018.01/parse_date.o build/debug/third_party/timelib-2018.01/unixtime2tm.o build/debug/third_party/timelib-2018.01/dow.o build/debug/third_party/timelib-2018.01/astro.o build/debug/third_party/timelib-2018.01/tm2unixtime.o build/debug/mongo/db/auth/restriction_environment.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_ldexp.o build/debug/mongo/platform/process_id.o build/debug/third_party/icu4c-57.1/source/common/ucnvbocu.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod64.o build/debug/mongo/util/hex.o build/debug/third_party/icu4c-57.1/source/i18n/windtfmt.o build/debug/third_party/icu4c-57.1/source/i18n/collation.o build/debug/mongo/db/pipeline/document_source_merge_modes_gen.o build/debug/mongo/rpc/message.o build/debug/third_party/icu4c-57.1/source/common/unistr_case_locale.o build/debug/third_party/s2/strings/split.o build/debug/third_party/icu4c-57.1/source/common/schriter.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_pow.o build/debug/third_party/icu4c-57.1/source/common/errorcode.o build/debug/third_party/icu4c-57.1/source/common/ubidi.o build/debug/mongo/client/connection_string.o build/debug/third_party/icu4c-57.1/source/i18n/ufieldpositer.o build/debug/third_party/icu4c-57.1/source/common/chariter.o build/debug/mongo/db/pipeline/parsed_inclusion_projection.o build/debug/mongo/scripting/mozjs/uri.o build/debug/mongo/db/fts/fts_unicode_phrase_matcher.o build/debug/third_party/icu4c-57.1/source/i18n/gregoimp.o build/debug/mongo/logger/rotatable_file_writer.o build/debug/third_party/icu4c-57.1/source/common/locbased.o build/debug/mongo/scripting/mozjs/nativefunction.o build/debug/mongo/db/matcher/expression_geo.o build/debug/mongo/db/matcher/expression_expr.o build/debug/third_party/gperftools-2.7/dist/src/stacktrace.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lgamma.o build/debug/mongo/db/matcher/schema/encrypt_schema_gen.o build/debug/mongo/db/concurrency/flow_control_ticketholder.o build/debug/mongo/db/audit.o build/debug/third_party/icu4c-57.1/source/common/loadednormalizer2impl.o build/debug/mongo/logger/rotatable_file_manager.o build/debug/mongo/util/signal_win32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sqrt.o build/debug/mongo/client/connection_string_connect.o build/debug/mongo/client/connpool.o build/debug/mongo/client/dbclient_connection.o build/debug/mongo/client/mongo_uri_connect.o build/debug/mongo/client/replica_set_monitor.o build/debug/mongo/client/dbclient_rs.o build/debug/mongo/client/replica_set_monitor_manager.o build/debug/mongo/client/global_conn_pool.o build/debug/mongo/base/error_codes.o build/debug/mongo/logger/message_event_utf8_encoder.o build/debug/mongo/scripting/mozjs/bindata.o build/debug/third_party/icu4c-57.1/source/common/utrie2.o build/debug/mongo/db/repl/last_vote.o build/debug/third_party/icu4c-57.1/source/i18n/visibledigits.o build/debug/third_party/icu4c-57.1/source/common/putil.o build/debug/third_party/zlib-1.2.11/uncompr.o build/debug/mongo/db/concurrency/replication_state_transition_lock_guard.o build/debug/mongo/rpc/factory.o build/debug/mongo/rpc/reply_builder_interface.o build/debug/mongo/rpc/legacy_request_builder.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp.o build/debug/third_party/abseil-cpp-master/abseil-cpp/absl/container/internal/raw_hash_set.o build/debug/third_party/s2/s2polyline.o build/debug/third_party/icu4c-57.1/source/i18n/unesctrn.o build/debug/third_party/icu4c-57.1/source/common/rbbisetb.o build/debug/third_party/gperftools-2.7/dist/src/base/spinlock_internal.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_erfc.o build/debug/mongo/bson/mutable/element.o build/debug/third_party/icu4c-57.1/source/common/uvectr64.o build/debug/mongo/db/matcher/expression_text_base.o build/debug/third_party/boost-1.70.0/libs/iostreams/src/mapped_file.o build/debug/third_party/icu4c-57.1/source/common/utrie.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_pow.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_decimal_data.o build/debug/third_party/icu4c-57.1/source/i18n/udateintervalformat.o build/debug/third_party/icu4c-57.1/source/common/udata.o build/debug/mongo/db/keys_collection_cache.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_add.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_erfc.o build/debug/third_party/icu4c-57.1/source/i18n/nfsubs.o build/debug/third_party/icu4c-57.1/source/i18n/gender.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_scalbl.o build/debug/third_party/pcre-8.42/pcre_newline.o build/debug/third_party/icu4c-57.1/source/common/uresdata.o build/debug/mongo/shell/kms.o build/debug/third_party/icu4c-57.1/source/common/filteredbrk.o build/debug/third_party/icu4c-57.1/source/common/rbbirb.o build/debug/third_party/icu4c-57.1/source/common/ucnv_io.o build/debug/third_party/wiredtiger/src/checksum/software/checksum.o build/debug/third_party/icu4c-57.1/source/common/ucnvscsu.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int64.o build/debug/mongo/scripting/mozjs/mongo.o build/debug/mongo/db/fts/stop_words_list.o build/debug/mongo/transport/service_executor_reserved.o build/debug/mongo/shell/kms_aws.o build/debug/mongo/idl/idl_parser.o build/debug/third_party/icu4c-57.1/source/i18n/datefmt.o build/debug/third_party/icu4c-57.1/source/common/uvector.o build/debug/mongo/scripting/mozjs/objectwrapper.o build/debug/third_party/icu4c-57.1/source/common/stringpiece.o build/debug/mongo/db/matcher/schema/expression_internal_schema_fmod.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_mul.o build/debug/mongo/logger/redaction.o build/debug/third_party/icu4c-57.1/source/i18n/usearch.o build/debug/mongo/base/data_type.o build/debug/mongo/util/secure_zero_memory.o build/debug/third_party/icu4c-57.1/source/common/unames.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/sqrt_tab_t.o build/debug/third_party/icu4c-57.1/source/common/utypes.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tgamma.o build/debug/mongo/util/duration.o build/debug/third_party/icu4c-57.1/source/common/listformatter.o build/debug/third_party/icu4c-57.1/source/i18n/wintzimpl.o build/debug/mongo/client/read_preference.o build/debug/mongo/util/stacktrace_posix.o build/debug/mongo/db/matcher/expression_leaf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cosh.o build/debug/third_party/icu4c-57.1/source/common/ucnvisci.o build/debug/mongo/db/index/s2_common.o build/debug/third_party/icu4c-57.1/source/common/uloc_tag.o build/debug/third_party/s2/util/coding/varint.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_nexttowardd.o build/debug/mongo/base/data_range.o build/debug/mongo/db/query/query_knobs_gen.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_scalbl.o build/debug/third_party/pcre-8.42/pcre_tables.o build/debug/third_party/zlib-1.2.11/infback.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int32.o build/debug/mongo/logger/log_manager.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint16.o build/debug/mongo/db/logical_time_validator.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_pow.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fetestexcept.o build/debug/third_party/gperftools-2.7/dist/src/base/vdso_support.o build/debug/third_party/icu4c-57.1/source/common/utext.o build/debug/third_party/icu4c-57.1/source/common/ubidiwrt.o build/debug/mongo/db/concurrency/d_concurrency.o build/debug/third_party/s2/s2regionunion.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_compare.o build/debug/mongo/shell/kms_shell.o build/debug/mongo/util/version_impl.o build/debug/third_party/icu4c-57.1/source/common/servlk.o build/debug/mongo/client/query.o build/debug/mongo/util/options_parser/options_parser.o build/debug/mongo/logger/ramlog.o build/debug/third_party/icu4c-57.1/source/i18n/smpdtfst.o build/debug/mongo/db/index/wildcard_key_generator.o build/debug/mongo/db/index/btree_key_generator.o build/debug/third_party/icu4c-57.1/source/common/uidna.o build/debug/mongo/util/options_parser/startup_option_init.o build/debug/third_party/icu4c-57.1/source/common/unistr_props.o build/debug/mongo/db/matcher/match_details.o build/debug/mongo/platform/mutex.o build/debug/third_party/icu4c-57.1/source/common/normalizer2impl.o build/debug/mongo/client/async_client.o build/debug/mongo/db/server_options_server_helpers.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_quantexpd.o build/debug/mongo/transport/transport_layer_manager.o build/debug/mongo/shell/fle_shell_options_gen.o build/debug/mongo/util/regex_util.o build/debug/third_party/icu4c-57.1/source/common/locmap.o build/debug/third_party/zlib-1.2.11/inflate.o build/debug/third_party/icu4c-57.1/source/i18n/persncal.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_logb.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_noncomp.o build/debug/mongo/client/dbclient_base.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log10.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_ldexp.o build/debug/mongo/scripting/utils.o build/debug/third_party/icu4c-57.1/source/i18n/collationtailoring.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_bid128.o build/debug/third_party/icu4c-57.1/source/common/ulist.o build/debug/third_party/shim_zstd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lrintd.o build/debug/mongo/db/fts/fts_language.o build/debug/mongo/db/repl/repl_set_tag.o build/debug/mongo/db/views/resolved_view.o build/debug/third_party/icu4c-57.1/source/i18n/bocsu.o build/debug/mongo/executor/egress_tag_closer_manager.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cosh.o build/debug/third_party/icu4c-57.1/source/i18n/decContext.o build/debug/third_party/shim_mozjs.o build/debug/third_party/icu4c-57.1/source/common/locavailable.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_logb.o build/debug/third_party/gperftools-2.7/dist/src/page_heap.o build/debug/mongo/shell/shell_options_storage.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_frexp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_hypot.o build/debug/third_party/pcre-8.42/pcre_dfa_exec.o build/debug/third_party/icu4c-57.1/source/common/ucnv_u8.o build/debug/mongo/base/global_initializer.o build/debug/third_party/icu4c-57.1/source/i18n/csrecog.o build/debug/mongo/scripting/mozjs/PosixNSPR.o build/debug/mongo/rpc/write_concern_error_detail.o build/debug/third_party/icu4c-57.1/source/common/resource.o build/debug/mongo/scripting/mozjs/engine_gen.o build/debug/mongo/shell/encrypted_dbclient_base.o build/debug/third_party/gperftools-2.7/dist/src/base/dynamic_annotations.o build/debug/third_party/icu4c-57.1/source/i18n/alphaindex.o build/debug/mongo/util/exception_filter_win32.o build/debug/third_party/icu4c-57.1/source/common/servnotf.o build/debug/third_party/icu4c-57.1/source/i18n/ucsdet.o build/debug/third_party/icu4c-57.1/source/common/uscript_props.o build/debug/third_party/icu4c-57.1/source/i18n/collationruleparser.o build/debug/mongo/db/pipeline/document_source_list_sessions_gen.o build/debug/mongo/db/auth/user_management_commands_parser.o build/debug/third_party/shim_stemmer.o build/debug/third_party/icu4c-57.1/source/i18n/utmscale.o build/debug/mongo/db/pipeline/runtime_constants_gen.o build/debug/third_party/shim_kms_message.o build/debug/third_party/icu4c-57.1/source/common/cstring.o build/debug/mongo/db/storage/storage_options.o build/debug/mongo/db/pipeline/resume_token.o build/debug/mongo/db/matcher/schema/expression_internal_schema_cond.o build/debug/mongo/bson/bsonmisc.o build/debug/third_party/abseil-cpp-master/abseil-cpp/absl/hash/internal/hash.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_four_over_pi.o build/debug/mongo/util/safe_num.o build/debug/third_party/icu4c-57.1/source/common/locutil.o build/debug/third_party/gperftools-2.7/dist/src/span.o build/debug/third_party/icu4c-57.1/source/common/dictbe.o build/debug/third_party/icu4c-57.1/source/common/ucnvmbcs.o build/debug/third_party/icu4c-57.1/source/common/ucln_cmn.o build/debug/mongo/util/net/ssl_types.o build/debug/mongo/logger/logstream_builder.o build/debug/third_party/icu4c-57.1/source/i18n/regexcmp.o build/debug/mongo/executor/network_interface.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log10.o build/debug/third_party/icu4c-57.1/source/i18n/zonemeta.o build/debug/mongo/db/commands/server_status_metric.o build/debug/third_party/icu4c-57.1/source/common/stringtriebuilder.o build/debug/third_party/icu4c-57.1/source/i18n/uspoof_conf.o build/debug/mongo/scripting/mozjs/cursor_handle.o build/debug/third_party/icu4c-57.1/source/i18n/decNumber.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint16.o build/debug/mongo/util/options_parser/options_parser_init.o build/debug/third_party/icu4c-57.1/source/i18n/choicfmt.o build/debug/mongo/db/geo/big_polygon.o build/debug/mongo/db/geo/r2_region_coverer.o build/debug/mongo/db/geo/hash.o build/debug/mongo/db/geo/shapes.o build/debug/mongo/db/matcher/schema/expression_internal_schema_unique_items.o build/debug/third_party/icu4c-57.1/source/i18n/islamcal.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_nearbyintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_minmax.o build/debug/mongo/bson/bson_validate.o build/debug/mongo/db/pipeline/value_gen.o build/debug/third_party/icu4c-57.1/source/i18n/udatpg.o build/debug/third_party/icu4c-57.1/source/i18n/compactdecimalformat.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_compare.o build/debug/third_party/wiredtiger/src/checksum/x86/crc32-x86.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_quantexpd.o build/debug/mongo/db/pipeline/document_source_change_stream_gen.o build/debug/mongo/db/pipeline/document_source_replace_root_gen.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_erf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fmod.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_acosh.o build/debug/mongo/db/query/collation/collator_interface.o build/debug/mongo/db/query/collation/collation_spec.o build/debug/third_party/icu4c-57.1/source/common/ustr_wcs.o build/debug/mongo/util/quick_exit.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cos.o build/debug/third_party/icu4c-57.1/source/i18n/timezone.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_erf.o build/debug/third_party/icu4c-57.1/source/i18n/rbt_rule.o build/debug/third_party/icu4c-57.1/source/common/uloc.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp2.o build/debug/mongo/db/commands.o build/debug/mongo/executor/remote_command_response.o build/debug/third_party/icu4c-57.1/source/common/sharedobject.o build/debug/third_party/icu4c-57.1/source/common/unifiedcache.o build/debug/mongo/scripting/mozjs/scripting_util_gen.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_rem.o build/debug/mongo/platform/stack_locator.o build/debug/mongo/base/error_extra_info.o build/debug/mongo/db/fts/fts_query_parser.o build/debug/third_party/icu4c-57.1/source/i18n/currunit.o build/debug/mongo/db/operation_time_tracker.o build/debug/mongo/db/logical_time.o build/debug/mongo/idl/server_parameter.o build/debug/mongo/logger/message_log_domain.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_div.o build/debug/mongo/scripting/mozjs/timestamp.o build/debug/third_party/icu4c-57.1/source/common/filterednormalizer2.o build/debug/mongo/util/log.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_add.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_hypot.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log10.o build/debug/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.o build/debug/mongo/scripting/mozjs/valuewriter.o build/debug/third_party/icu4c-57.1/source/common/simpleformatter.o build/debug/third_party/icu4c-57.1/source/i18n/tmutamt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_nearbyintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_acosh.o build/debug/third_party/icu4c-57.1/source/i18n/collationcompare.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atan2.o build/debug/third_party/icu4c-57.1/source/common/uchar.o build/debug/mongo/db/pipeline/expression.o build/debug/mongo/db/exec/projection_exec_agg.o build/debug/third_party/pcre-8.42/pcrecpp.o build/debug/third_party/icu4c-57.1/source/i18n/simpletz.o build/debug/mongo/util/uuid.o build/debug/third_party/zlib-1.2.11/zutil.o build/debug/third_party/zlib-1.2.11/inffast.o build/debug/third_party/zlib-1.2.11/crc32.o build/debug/third_party/zlib-1.2.11/compress.o build/debug/third_party/zlib-1.2.11/adler32.o build/debug/mongo/db/query/query_request.o build/debug/mongo/db/query/collation/collator_factory_interface.o build/debug/mongo/bson/mutable/document.o build/debug/mongo/scripting/mozjs/minkey.o build/debug/third_party/icu4c-57.1/source/i18n/quant.o build/debug/mongo/rpc/protocol.o build/debug/third_party/icu4c-57.1/source/i18n/rbt_set.o build/debug/third_party/icu4c-57.1/source/stubdata/stubdata.o build/debug/mongo/logger/logger.o build/debug/mongo/db/fts/fts_query_impl.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_erfc.o build/debug/third_party/icu4c-57.1/source/i18n/coptccal.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fma.o build/debug/mongo/scripting/mozjs/dbcollection.o build/debug/third_party/icu4c-57.1/source/common/unormcmp.o build/debug/mongo/util/net/ssl_options_client_gen.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_mul.o build/debug/third_party/icu4c-57.1/source/i18n/cpdtrans.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_binarydecimal.o build/debug/third_party/pcre-8.42/pcre_config.o build/debug/third_party/icu4c-57.1/source/common/ucasemap.o build/debug/third_party/icu4c-57.1/source/i18n/quantityformatter.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_modf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_round_integral.o build/debug/third_party/icu4c-57.1/source/i18n/japancal.o build/debug/mongo/db/auth/action_type.o build/debug/mongo/db/auth/privilege_parser.o build/debug/mongo/db/auth/resource_pattern.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cos.o build/debug/mongo/db/pipeline/expression_trigonometric.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_feraiseexcept.o build/debug/mongo/db/index/expression_params.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_noncomp.o build/debug/mongo/shell/kms_gen.o build/debug/mongo/db/cluster_auth_mode_option_gen.o build/debug/third_party/icu4c-57.1/source/common/uprops.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_log.o build/debug/third_party/icu4c-57.1/source/common/cstr.o build/debug/third_party/icu4c-57.1/source/i18n/collationsets.o build/debug/third_party/icu4c-57.1/source/common/ubrk.o build/debug/third_party/icu4c-57.1/source/i18n/collationroot.o build/debug/third_party/icu4c-57.1/source/i18n/dangical.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_asinh.o build/debug/mongo/scripting/mozjs/jscustomallocator.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_acos.o build/debug/mongo/scripting/mozjs/error.o build/debug/mongo/base/status.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sinh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_nexttowardd.o build/debug/mongo/db/matcher/expression_where_noop.o build/debug/third_party/icu4c-57.1/source/i18n/collationdatabuilder.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sqrt.o build/debug/third_party/icu4c-57.1/source/common/utrace.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_asinh.o build/debug/mongo/util/processinfo.o build/debug/third_party/s2/s2polygon.o build/debug/mongo/bson/bsonobj.o build/debug/mongo/base/string_data.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_int.o build/debug/mongo/db/pipeline/parsed_exclusion_projection.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_next.o build/debug/mongo/db/hasher.o build/debug/mongo/util/stacktrace.o build/debug/third_party/icu4c-57.1/source/i18n/basictz.o build/debug/third_party/icu4c-57.1/source/i18n/decimfmtimpl.o build/debug/third_party/shim_abseil.o build/debug/mongo/executor/network_interface_thread_pool.o build/debug/third_party/icu4c-57.1/source/i18n/collationsettings.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_quantize.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atanh.o build/debug/third_party/shim_pcrecpp.o build/debug/third_party/icu4c-57.1/source/common/locid.o build/debug/third_party/icu4c-57.1/source/i18n/winnmfmt.o build/debug/mongo/idl/server_parameter_with_storage.o build/debug/third_party/icu4c-57.1/source/i18n/vzone.o build/debug/third_party/gperftools-2.7/dist/src/central_freelist.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_round.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_exception.o build/debug/third_party/icu4c-57.1/source/common/punycode.o build/debug/third_party/icu4c-57.1/source/common/ucnv_cb.o build/debug/third_party/icu4c-57.1/source/i18n/digitaffixesandpadding.o build/debug/mongo/db/repl/member_config.o build/debug/third_party/icu4c-57.1/source/i18n/identifier_info.o build/debug/third_party/icu4c-57.1/source/i18n/tzgnames.o build/debug/mongo/executor/connection_pool_stats.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_scalb.o build/debug/mongo/transport/message_compressor_options_client_gen.o build/debug/third_party/icu4c-57.1/source/common/unisetspan.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_convert_data.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lround.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp10.o build/debug/third_party/icu4c-57.1/source/i18n/fmtable_cnv.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_asinh.o build/debug/mongo/base/validate_locale.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sinh.o build/debug/mongo/db/catalog/index_catalog_entry.o build/debug/third_party/gperftools-2.7/dist/src/maybe_threads.o build/debug/mongo/bson/bsonelement.o build/debug/third_party/icu4c-57.1/source/common/util_props.o build/debug/mongo/bson/simple_bsonobj_comparator.o build/debug/third_party/icu4c-57.1/source/i18n/titletrn.o build/debug/third_party/icu4c-57.1/source/common/udataswp.o build/debug/mongo/db/matcher/schema/expression_internal_schema_match_array_index.o build/debug/third_party/icu4c-57.1/source/i18n/currpinf.o build/debug/third_party/icu4c-57.1/source/i18n/astro.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_2_str_tables.o build/debug/third_party/icu4c-57.1/source/i18n/fpositer.o build/debug/mongo/util/itoa.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int16.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tgamma.o build/debug/third_party/icu4c-57.1/source/common/unorm.o build/debug/mongo/db/concurrency/lock_stats.o build/debug/mongo/db/concurrency/lock_state.o build/debug/third_party/icu4c-57.1/source/common/utf_impl.o build/debug/third_party/icu4c-57.1/source/i18n/collationdatareader.o build/debug/third_party/icu4c-57.1/source/i18n/rbt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fma.o build/debug/third_party/icu4c-57.1/source/common/uloc_keytype.o build/debug/third_party/icu4c-57.1/source/i18n/uspoof_wsconf.o build/debug/mongo/bson/util/bson_extract.o build/debug/mongo/scripting/mozjs/numberdecimal.o build/debug/mongo/logger/log_component.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_scalb.o build/debug/third_party/icu4c-57.1/source/common/uinit.o build/debug/third_party/icu4c-57.1/source/i18n/scientificnumberformatter.o build/debug/third_party/gperftools-2.7/dist/src/system-alloc.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_llrintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tan.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_cbrt.o build/debug/mongo/crypto/sha_block_openssl.o build/debug/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.o build/debug/mongo/db/matcher/expression_tree.o build/debug/mongo/db/matcher/schema/expression_internal_schema_xor.o build/debug/mongo/db/matcher/schema/expression_internal_schema_object_match.o build/debug/mongo/db/matcher/schema/json_pointer.o build/debug/mongo/db/matcher/schema/json_schema_parser.o build/debug/mongo/db/matcher/expression_internal_expr_eq.o build/debug/mongo/db/matcher/expression_text_noop.o build/debug/mongo/db/matcher/expression_where_base.o build/debug/mongo/db/matcher/extensions_callback.o build/debug/mongo/db/matcher/schema/expression_internal_schema_num_array_items.o build/debug/third_party/icu4c-57.1/source/i18n/strmatch.o build/debug/mongo/bson/json.o build/debug/mongo/transport/service_executor_gen.o build/debug/third_party/icu4c-57.1/source/i18n/ucoleitr.o build/debug/mongo/db/fts/fts_index_format.o build/debug/third_party/icu4c-57.1/source/common/resbund.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_acos.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fmod.o build/debug/third_party/icu4c-57.1/source/common/ustack.o build/debug/third_party/icu4c-57.1/source/i18n/chnsecal.o build/debug/mongo/db/repl/repl_set_heartbeat_args_v1.o build/debug/third_party/icu4c-57.1/source/common/ruleiter.o build/debug/mongo/bson/bsontypes.o build/debug/third_party/icu4c-57.1/source/common/unifunct.o build/debug/mongo/util/concurrency/spin_lock.o build/debug/mongo/scripting/dbdirectclient_factory.o build/debug/third_party/icu4c-57.1/source/i18n/collationrootelements.o build/debug/third_party/icu4c-57.1/source/i18n/strrepl.o build/debug/mongo/db/keys_collection_manager.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log2.o build/debug/third_party/icu4c-57.1/source/common/ustrenum.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log2.o build/debug/third_party/icu4c-57.1/source/common/patternprops.o build/debug/mongo/shell/kms_local.o build/debug/third_party/icu4c-57.1/source/i18n/collationkeys.o build/debug/third_party/icu4c-57.1/source/common/ucnv_u7.o build/debug/mongo/db/time_proof_service.o build/debug/mongo/transport/transport_layer_asio.o build/debug/mongo/scripting/mozjs/numberint.o build/debug/mongo/scripting/mozjs/jsstringwrapper.o build/debug/mongo/scripting/mozjs/global.o build/debug/mongo/scripting/mozjs/countdownlatch.o build/debug/mongo/scripting/mozjs/dbpointer.o build/debug/mongo/scripting/mozjs/dbref.o build/debug/mongo/scripting/mozjs/numberlong.o build/debug/mongo/scripting/mozjs/oid.o build/debug/mongo/scripting/mozjs/implscope.o build/debug/mongo/scripting/mozjs/dbquery.o build/debug/third_party/pcre-8.42/pcre_valid_utf8.o build/debug/third_party/icu4c-57.1/source/i18n/collationiterator.o build/debug/third_party/icu4c-57.1/source/i18n/calendar.o build/debug/third_party/icu4c-57.1/source/common/messagepattern.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log1p.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_erf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_inv_hyper.o build/debug/third_party/icu4c-57.1/source/i18n/format.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cos.o build/debug/mongo/base/simple_string_data_comparator.o build/debug/third_party/icu4c-57.1/source/common/normlzr.o build/debug/mongo/db/pipeline/document_comparator.o build/debug/mongo/db/query/tailable_mode.o build/debug/mongo/db/log_process_details.o build/debug/third_party/icu4c-57.1/source/common/ubidiln.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_logbd.o build/debug/third_party/gperftools-2.7/dist/src/base/sysinfo.o build/debug/mongo/db/server_options_base_gen.o build/debug/third_party/icu4c-57.1/source/common/ucnv_ext.o build/debug/mongo/util/dns_query.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tanh.o build/debug/mongo/db/matcher/path.o build/debug/third_party/icu4c-57.1/source/common/utrie2_builder.o build/debug/third_party/s2/s2latlng.o build/debug/third_party/icu4c-57.1/source/i18n/ucol.o build/debug/third_party/icu4c-57.1/source/i18n/casetrn.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_hypot.o build/debug/mongo/executor/thread_pool_task_executor.o build/debug/mongo/db/fts/tokenizer.o build/debug/mongo/db/fts/stemmer.o build/debug/third_party/s2/s2edgeutil.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fesetexceptflag.o build/debug/third_party/icu4c-57.1/source/i18n/remtrans.o build/debug/mongo/db/server_options_base.o build/debug/mongo/util/password.o build/debug/mongo/db/keyfile_option_gen.o build/debug/third_party/icu4c-57.1/source/i18n/dtitvinf.o build/debug/third_party/gperftools-2.7/dist/src/static_vars.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_exp.o build/debug/third_party/icu4c-57.1/source/i18n/esctrn.o build/debug/third_party/icu4c-57.1/source/i18n/scriptset.o build/debug/mongo/db/repl/repl_set_config.o build/debug/mongo/db/repl/repl_set_request_votes_args.o build/debug/mongo/util/options_parser/startup_options.o build/debug/third_party/icu4c-57.1/source/i18n/nultrans.o build/debug/third_party/icu4c-57.1/source/i18n/reldtfmt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp.o build/debug/mongo/db/keys_collection_manager_gen.o build/debug/third_party/icu4c-57.1/source/common/ucat.o build/debug/third_party/icu4c-57.1/source/i18n/utf8collationiterator.o build/debug/mongo/rpc/op_msg.o build/debug/mongo/db/repl/repl_settings.o build/debug/mongo/logger/console.o build/debug/mongo/db/geo/geometry_container.o build/debug/mongo/db/geo/geoparser.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sqrt.o build/debug/mongo/util/allocator.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int16.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_logbd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_frexp.o build/debug/third_party/icu4c-57.1/source/common/servlkf.o build/debug/third_party/icu4c-57.1/source/i18n/tzrule.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint32.o build/debug/third_party/icu4c-57.1/source/common/icuplug.o build/debug/third_party/icu4c-57.1/source/common/appendable.o build/debug/mongo/client/mongo_uri.o build/debug/mongo/scripting/bson_template_evaluator.o build/debug/mongo/rpc/get_status_from_command_result.o build/debug/third_party/icu4c-57.1/source/common/caniter.o build/debug/third_party/icu4c-57.1/source/i18n/smpdtfmt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_div.o build/debug/third_party/s2/s2regionintersection.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_bid.o build/debug/third_party/icu4c-57.1/source/i18n/dtfmtsym.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_round_integral.o build/debug/mongo/util/str.o build/debug/third_party/icu4c-57.1/source/common/ucnvsel.o build/debug/third_party/icu4c-57.1/source/i18n/dtptngen.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_ldexp.o build/debug/third_party/icu4c-57.1/source/common/ucnv_ct.o build/debug/mongo/util/options_parser/constraints.o build/debug/third_party/icu4c-57.1/source/common/icudataver.o build/debug/third_party/icu4c-57.1/source/common/wintz.o build/debug/third_party/icu4c-57.1/source/common/rbbidata.o build/debug/third_party/icu4c-57.1/source/common/bmpset.o build/debug/third_party/icu4c-57.1/source/common/usprep.o build/debug/third_party/icu4c-57.1/source/common/ucnv_u32.o build/debug/third_party/icu4c-57.1/source/common/locresdata.o build/debug/third_party/icu4c-57.1/source/common/ucurr.o build/debug/third_party/icu4c-57.1/source/common/servrbf.o build/debug/third_party/icu4c-57.1/source/common/rbbiscan.o build/debug/third_party/icu4c-57.1/source/common/bytestriebuilder.o build/debug/third_party/icu4c-57.1/source/common/ucmndata.o build/debug/third_party/icu4c-57.1/source/common/ucnv_set.o build/debug/third_party/icu4c-57.1/source/common/ustr_titlecase_brkiter.o build/debug/third_party/icu4c-57.1/source/common/ustring.o build/debug/third_party/icu4c-57.1/source/common/ucasemap_titlecase_brkiter.o build/debug/third_party/icu4c-57.1/source/common/umutex.o build/debug/third_party/icu4c-57.1/source/common/ucnvlat1.o build/debug/third_party/icu4c-57.1/source/common/ustrcase_locale.o build/debug/third_party/icu4c-57.1/source/common/uhash_us.o build/debug/third_party/icu4c-57.1/source/common/uniset_closure.o build/debug/third_party/icu4c-57.1/source/common/charstr.o build/debug/third_party/icu4c-57.1/source/common/uscript.o build/debug/third_party/icu4c-57.1/source/common/rbbistbl.o build/debug/third_party/icu4c-57.1/source/common/ucnv2022.o build/debug/third_party/icu4c-57.1/source/common/udatamem.o build/debug/third_party/icu4c-57.1/source/common/rbbi.o build/debug/third_party/icu4c-57.1/source/common/brkeng.o build/debug/third_party/icu4c-57.1/source/common/uvectr32.o build/debug/third_party/icu4c-57.1/source/common/umapfile.o build/debug/third_party/icu4c-57.1/source/common/bytestrieiterator.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sinh.o build/debug/mongo/db/storage/write_unit_of_work.o build/debug/third_party/shim_fmt.o build/debug/third_party/pcre-8.42/pcre_compile.o build/debug/third_party/pcre-8.42/pcre_chartables.o build/debug/third_party/pcre-8.42/pcre_exec.o build/debug/third_party/pcre-8.42/pcre_ord2utf8.o build/debug/third_party/pcre-8.42/pcre_fullinfo.o build/debug/third_party/pcre-8.42/pcre_byte_order.o build/debug/mongo/s/catalog/sharding_catalog_client.o build/debug/third_party/icu4c-57.1/source/i18n/selfmt.o build/debug/third_party/icu4c-57.1/source/i18n/currfmt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod128.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cosh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_dpd.o build/debug/third_party/icu4c-57.1/source/i18n/toupptrn.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_modf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log.o build/debug/mongo/util/system_tick_source.o build/debug/mongo/db/pipeline/parsed_aggregation_projection.o build/debug/mongo/util/options_parser/option_section.o build/debug/third_party/icu4c-57.1/source/i18n/csrmbcs.o build/debug/mongo/util/boost_assert_impl.o build/debug/third_party/icu4c-57.1/source/i18n/buddhcal.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_mul.o build/debug/third_party/icu4c-57.1/source/i18n/tznames_impl.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_noncomp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_bid64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int16.o build/debug/mongo/db/pipeline/parsed_add_fields.o build/debug/mongo/platform/random.o build/debug/third_party/icu4c-57.1/source/i18n/nortrans.o build/debug/mongo/executor/connection_pool.o build/debug/third_party/gperftools-2.7/dist/src/symbolize.o build/debug/third_party/gperftools-2.7/dist/src/common.o build/debug/third_party/gperftools-2.7/dist/src/malloc_hook.o build/debug/mongo/util/options_parser/option_description.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_quantize.o build/debug/mongo/platform/stack_locator_linux.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_div.o build/debug/mongo/util/text.o build/debug/third_party/icu4c-57.1/source/i18n/reldatefmt.o build/debug/third_party/s2/s2edgeindex.o build/debug/mongo/logger/log_component_settings.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_scalbl.o build/debug/mongo/executor/network_interface_factory.o build/debug/third_party/icu4c-57.1/source/i18n/rematch.o build/debug/mongo/util/summation.o build/debug/mongo/db/signed_logical_time.o build/debug/third_party/icu4c-57.1/source/i18n/name2uni.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_string.o build/debug/third_party/s2/s2polygonbuilder.o build/debug/third_party/s2/s2cap.o build/debug/third_party/shim_boost.o build/debug/mongo/executor/task_executor.o build/debug/mongo/db/field_ref.o build/debug/third_party/icu4c-57.1/source/i18n/dcfmtsym.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_bessel.o build/debug/mongo/platform/shared_library.o build/debug/third_party/icu4c-57.1/source/i18n/regeximp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp2.o build/debug/third_party/icu4c-57.1/source/i18n/decimfmt.o build/debug/mongo/util/options_parser/environment.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint8.o build/debug/third_party/icu4c-57.1/source/i18n/dtitvfmt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log1p.o build/debug/mongo/base/data_type_string_data.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_modf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_llrintd.o build/debug/third_party/icu4c-57.1/source/i18n/numsys.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cbrt.o build/debug/mongo/util/shell_exec.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_powi.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sin.o build/debug/mongo/base/shim.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_feclearexcept.o build/debug/third_party/icu4c-57.1/source/i18n/udat.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atan.o build/debug/mongo/db/query/hint_parser.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lround.o build/debug/third_party/icu4c-57.1/source/i18n/measunit.o build/debug/mongo/db/field_parser.o build/debug/third_party/icu4c-57.1/source/i18n/dtrule.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp10.o build/debug/third_party/icu4c-57.1/source/i18n/olsontz.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fmod.o build/debug/third_party/icu4c-57.1/source/i18n/umsg.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tanh.o build/debug/mongo/util/cmdline_utils/censor_cmdline.o build/debug/mongo/base/make_string_vector.o build/debug/third_party/icu4c-57.1/source/i18n/stsearch.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops.o build/debug/mongo/db/matcher/path_internal.o build/debug/mongo/util/signal_handlers_synchronous.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int8.o build/debug/third_party/icu4c-57.1/source/i18n/numfmt.o build/debug/third_party/icu4c-57.1/source/i18n/digitaffix.o build/debug/third_party/icu4c-57.1/source/i18n/standardplural.o build/debug/third_party/icu4c-57.1/source/i18n/uregex.o build/debug/third_party/icu4c-57.1/source/i18n/smallintformatter.o build/debug/third_party/icu4c-57.1/source/i18n/uregexc.o build/debug/third_party/icu4c-57.1/source/i18n/unumsys.o build/debug/third_party/icu4c-57.1/source/i18n/ztrans.o build/debug/third_party/icu4c-57.1/source/i18n/unum.o build/debug/third_party/icu4c-57.1/source/i18n/ethpccal.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint8.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atan.o build/debug/mongo/util/base64.o build/debug/mongo/base/initializer_dependency_graph.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_sqrt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cbrt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_erf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint16.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atan2.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int8.o ================================================ FILE: cmake/mongodb/linux-release.objects ================================================ build/opt/third_party/mozjs-60/extract/mfbt/double-conversion/double-conversion/strtod.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/zstd_common.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src4.o build/opt/third_party/icu4c-57.1/source/common/ucol_swp.o build/opt/third_party/gperftools-2.7/dist/src/memfs_malloc.o build/opt/mongo/s/stale_exception.o build/opt/mongo/util/exception_filter_win32.o build/opt/mongo/s/catalog/type_changelog.o build/opt/mongo/transport/message_compressor_manager.o build/opt/third_party/s2/strings/stringprintf.o build/opt/mongo/scripting/bson_template_evaluator.o build/opt/mongo/scripting/mozjs/base.o build/opt/mongo/db/concurrency/flow_control_ticketholder.o build/opt/third_party/icu4c-57.1/source/i18n/rbtz.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sqrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_nearbyintd.o build/opt/mongo/util/system_tick_source.o build/opt/third_party/icu4c-57.1/source/i18n/unesctrn.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_norwegian.o build/opt/third_party/icu4c-57.1/source/stubdata/stubdata.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_sqrt.o build/opt/mongo/db/matcher/matcher_type_set.o build/opt/third_party/icu4c-57.1/source/i18n/collationsettings.o build/opt/mongo/client/replica_set_monitor_manager.o build/opt/third_party/icu4c-57.1/source/common/normlzr.o build/opt/mongo/db/pipeline/value_gen.o build/opt/mongo/db/keys_collection_manager_gen.o build/opt/mongo/db/keys_collection_cache.o build/opt/mongo/db/keys_collection_manager.o build/opt/mongo/db/key_generator.o build/opt/mongo/db/signed_logical_time.o build/opt/third_party/shim_asio.o build/opt/third_party/icu4c-57.1/source/common/stringpiece.o build/opt/mongo/db/query/collation/collator_factory_interface.o build/opt/mongo/util/concurrency/idle_thread_block.o build/opt/mongo/db/fts/fts_element_iterator.o build/opt/mongo/client/replica_set_change_notifier.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_ldexp.o build/opt/mongo/shell/linenoise.o build/opt/third_party/yaml-cpp-0.6.2/src/binary.o build/opt/third_party/yaml-cpp-0.6.2/src/exp.o build/opt/third_party/yaml-cpp-0.6.2/src/ostream_wrapper.o build/opt/third_party/yaml-cpp-0.6.2/src/contrib/graphbuilder.o build/opt/third_party/yaml-cpp-0.6.2/src/memory.o build/opt/third_party/yaml-cpp-0.6.2/src/contrib/graphbuilderadapter.o build/opt/third_party/yaml-cpp-0.6.2/src/regex_yaml.o build/opt/third_party/yaml-cpp-0.6.2/src/scantoken.o build/opt/third_party/yaml-cpp-0.6.2/src/parse.o build/opt/third_party/yaml-cpp-0.6.2/src/emitter.o build/opt/third_party/yaml-cpp-0.6.2/src/emit.o build/opt/third_party/yaml-cpp-0.6.2/src/node_data.o build/opt/third_party/yaml-cpp-0.6.2/src/null.o build/opt/third_party/yaml-cpp-0.6.2/src/node.o build/opt/third_party/yaml-cpp-0.6.2/src/nodebuilder.o build/opt/third_party/yaml-cpp-0.6.2/src/singledocparser.o build/opt/third_party/yaml-cpp-0.6.2/src/scanscalar.o build/opt/third_party/yaml-cpp-0.6.2/src/convert.o build/opt/third_party/yaml-cpp-0.6.2/src/scanner.o build/opt/third_party/yaml-cpp-0.6.2/src/nodeevents.o build/opt/third_party/yaml-cpp-0.6.2/src/scantag.o build/opt/third_party/yaml-cpp-0.6.2/src/exceptions.o build/opt/third_party/yaml-cpp-0.6.2/src/emitfromevents.o build/opt/third_party/yaml-cpp-0.6.2/src/emitterstate.o build/opt/third_party/yaml-cpp-0.6.2/src/stream.o build/opt/third_party/yaml-cpp-0.6.2/src/directives.o build/opt/third_party/yaml-cpp-0.6.2/src/parser.o build/opt/third_party/yaml-cpp-0.6.2/src/tag.o build/opt/third_party/yaml-cpp-0.6.2/src/emitterutils.o build/opt/third_party/yaml-cpp-0.6.2/src/simplekey.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_atanh.o build/opt/third_party/icu4c-57.1/source/i18n/csrutf8.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src37.o build/opt/mongo/rpc/legacy_request.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_ldm.o build/opt/mongo/db/repl/is_master_response.o build/opt/mongo/db/repl/last_vote.o build/opt/mongo/db/repl/repl_set_request_votes_args.o build/opt/mongo/db/repl/repl_set_config_gen.o build/opt/mongo/db/repl/repl_set_config.o build/opt/mongo/db/repl/update_position_args.o build/opt/mongo/db/repl/repl_set_tag.o build/opt/mongo/db/repl/repl_set_heartbeat_response.o build/opt/mongo/db/repl/repl_set_heartbeat_args_v1.o build/opt/mongo/db/repl/member_config.o build/opt/mongo/db/fts/stop_words_list.o build/opt/third_party/shim_intel_decimal128.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src15.o build/opt/mongo/base/status.o build/opt/third_party/icu4c-57.1/source/i18n/collationbuilder.o build/opt/mongo/util/concurrency/ticketholder.o build/opt/mongo/db/views/resolved_view.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/path.o build/opt/mongo/scripting/utils.o build/opt/mongo/scripting/engine.o build/opt/mongo/scripting/deadline_monitor_gen.o build/opt/mongo/scripting/jsexception.o build/opt/mongo/scripting/deadline_monitor.o build/opt/mongo/scripting/dbdirectclient_factory.o build/opt/third_party/mozjs-60/extract/js/src/gc/StoreBuffer.o build/opt/third_party/zlib-1.2.11/trees.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_ldexp.o build/opt/third_party/icu4c-57.1/source/i18n/reldtfmt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_asinh.o build/opt/mongo/s/catalog/type_database.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cosh.o build/opt/third_party/icu4c-57.1/source/common/filterednormalizer2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_div.o build/opt/mongo/db/logical_clock.o build/opt/third_party/icu4c-57.1/source/common/dtintrv.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_english.o build/opt/third_party/pcre-8.42/pcre_ord2utf8.o build/opt/mongo/client/connection_string_connect.o build/opt/mongo/client/connpool.o build/opt/mongo/client/dbclient_rs.o build/opt/mongo/client/global_conn_pool.o build/opt/mongo/client/replica_set_monitor.o build/opt/mongo/client/dbclient_connection.o build/opt/mongo/client/mongo_uri_connect.o build/opt/mongo/client/global_conn_pool_gen.o build/opt/mongo/db/fts/stemmer.o build/opt/third_party/icu4c-57.1/source/common/unisetspan.o build/opt/mongo/db/repl/read_concern_args.o build/opt/mongo/db/matcher/matcher.o build/opt/third_party/icu4c-57.1/source/i18n/csrucode.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atanh.o build/opt/mongo/util/summation.o build/opt/mongo/db/matcher/expression_where_base.o build/opt/third_party/icu4c-57.1/source/common/locresdata.o build/opt/mongo/db/logical_session_id_helpers.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_scalb.o build/opt/third_party/icu4c-57.1/source/i18n/esctrn.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint8.o build/opt/third_party/icu4c-57.1/source/i18n/gregocal.o build/opt/mongo/util/intrusive_counter.o build/opt/mongo/db/server_options_server_helpers.o build/opt/mongo/util/log.o build/opt/third_party/icu4c-57.1/source/i18n/selfmt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tanh.o build/opt/mongo/util/signal_handlers_synchronous.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_expm1.o build/opt/mongo/db/catalog/collection.o build/opt/mongo/db/auth/action_type.o build/opt/third_party/icu4c-57.1/source/i18n/regexst.o build/opt/third_party/icu4c-57.1/source/i18n/measure.o build/opt/mongo/db/matcher/schema/expression_internal_schema_object_match.o build/opt/mongo/rpc/write_concern_error_detail.o build/opt/mongo/rpc/get_status_from_command_result.o build/opt/mongo/scripting/mozjs/bson.o build/opt/mongo/client/read_preference.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src42.o build/opt/third_party/icu4c-57.1/source/i18n/collationtailoring.o build/opt/third_party/icu4c-57.1/source/i18n/csrecog.o build/opt/mongo/db/logical_time_validator.o build/opt/mongo/db/matcher/expression_array.o build/opt/third_party/icu4c-57.1/source/common/ulistformatter.o build/opt/mongo/db/fts/fts_matcher.o build/opt/third_party/wiredtiger/src/checksum/x86/crc32-x86-alt.o build/opt/mongo/db/concurrency/d_concurrency.o build/opt/third_party/icu4c-57.1/source/i18n/ucol_sit.o build/opt/third_party/pcre-8.42/pcre_version.o build/opt/mongo/executor/thread_pool_task_executor.o build/opt/third_party/icu4c-57.1/source/common/ustrcase_locale.o build/opt/mongo/scripting/mozjs/proxyscope.o build/opt/mongo/db/log_process_details.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_asin.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/hist.o build/opt/third_party/wiredtiger/src/checksum/x86/crc32-x86.o build/opt/mongo/util/net/ssl_manager_openssl.o build/opt/third_party/s2/base/stringprintf.o build/opt/mongo/db/matcher/schema/encrypt_schema_gen.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atan.o build/opt/mongo/db/field_ref_set.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_modf.o build/opt/mongo/db/query/datetime/date_time_support.o build/opt/mongo/db/query/hint_parser.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_ldexp.o build/opt/third_party/zlib-1.2.11/uncompr.o build/opt/mongo/db/query/hint_gen.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_bid128.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_nexttowardd.o build/opt/third_party/icu4c-57.1/source/common/rbbisetb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fegetexceptflag.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fmod.o build/opt/third_party/icu4c-57.1/source/common/locdspnm.o build/opt/mongo/db/keys_collection_document.o build/opt/third_party/icu4c-57.1/source/common/unifunct.o build/opt/third_party/boost-1.70.0/libs/program_options/src/convert.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_log.o build/opt/third_party/icu4c-57.1/source/common/udatamem.o build/opt/third_party/abseil-cpp-master/abseil-cpp/absl/container/internal/raw_hash_set.o build/opt/mongo/scripting/mozjs/uri.o build/opt/third_party/icu4c-57.1/source/common/ucase.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_exception.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src21.o build/opt/mongo/db/fts/fts_query_noop.o build/opt/mongo/db/pipeline/document_source_merge_spec.o build/opt/mongo/client/index_spec.o build/opt/third_party/s2/s2polyline.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_rint.o build/opt/third_party/icu4c-57.1/source/i18n/calendar.o build/opt/third_party/icu4c-57.1/source/i18n/regeximp.o build/opt/mongo/db/concurrency/lock_manager.o build/opt/third_party/s2/util/coding/coder.o build/opt/mongo/db/repl/optime.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_minmax.o build/opt/mongo/s/catalog/sharding_catalog_client.o build/opt/third_party/pcre-8.42/pcre_maketables.o build/opt/mongo/db/bson/dotted_path_support.o build/opt/third_party/icu4c-57.1/source/i18n/simpletz.o build/opt/third_party/icu4c-57.1/source/common/listformatter.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sin.o build/opt/third_party/icu4c-57.1/source/common/uarrsort.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src33.o build/opt/third_party/icu4c-57.1/source/common/umapfile.o build/opt/mongo/shell/shell_utils_extended.o build/opt/mongo/s/request_types/add_shard_to_zone_request_type.o build/opt/third_party/s2/s2r2rect.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sinh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_dpd.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/unique_path.o build/opt/mongo/db/auth/address_restriction.o build/opt/mongo/db/auth/address_restriction_gen.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_asinh.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src32.o build/opt/third_party/snappy-1.1.7/snappy-c.o build/opt/third_party/snappy-1.1.7/snappy-sinksource.o build/opt/third_party/snappy-1.1.7/snappy.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_portuguese.o build/opt/mongo/db/index_names.o build/opt/third_party/icu4c-57.1/source/common/unistr_cnv.o build/opt/mongo/db/query/collation/collation_index_key.o build/opt/mongo/db/query/collation/collator_interface.o build/opt/mongo/db/query/collation/collation_spec.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_round.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_lgamma.o build/opt/mongo/util/options_parser/environment.o build/opt/mongo/util/fast_clock_source_factory.o build/opt/mongo/util/background_thread_clock_source.o build/opt/mongo/util/clock_source.o build/opt/third_party/zlib-1.2.11/infback.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_log1p.o build/opt/third_party/shim_pcrecpp.o build/opt/mongo/util/text.o build/opt/mongo/db/pipeline/runtime_constants_gen.o build/opt/mongo/scripting/mozjs/dbcollection.o build/opt/mongo/shell/linenoise_utf8.o build/opt/third_party/icu4c-57.1/source/common/uvectr64.o build/opt/third_party/icu4c-57.1/source/common/unifiedcache.o build/opt/mongo/util/net/ssl_options_client_gen.o build/opt/third_party/boost-1.70.0/libs/program_options/src/winmain.o build/opt/mongo/db/matcher/expression_text_noop.o build/opt/third_party/icu4c-57.1/source/common/brkeng.o build/opt/third_party/zlib-1.2.11/crc32.o build/opt/mongo/db/repl_set_member_in_standalone_mode.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_string.o build/opt/third_party/icu4c-57.1/source/common/ucharstriebuilder.o build/opt/mongo/scripting/mozjs/dbpointer.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops.o build/opt/mongo/util/timer.o build/opt/mongo/db/pipeline/aggregation_request.o build/opt/third_party/icu4c-57.1/source/common/utrie2.o build/opt/third_party/icu4c-57.1/source/i18n/collationdata.o build/opt/mongo/util/options_parser/startup_option_init.o build/opt/mongo/util/allocator.o build/opt/third_party/icu4c-57.1/source/common/uvector.o build/opt/third_party/shim_allocator.o build/opt/third_party/icu4c-57.1/source/i18n/dtptngen.o build/opt/mongo/db/keys_collection_client_sharded.o build/opt/mongo/db/matcher/schema/expression_internal_schema_xor.o build/opt/third_party/icu4c-57.1/source/common/parsepos.o build/opt/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.o build/opt/mongo/s/request_types/commit_chunk_migration_request_type.o build/opt/mongo/db/repl/replication_process.o build/opt/mongo/db/repl/replication_consistency_markers.o build/opt/third_party/libstemmer_c/runtime/api.o build/opt/third_party/s2/base/strtoint.o build/opt/third_party/icu4c-57.1/source/i18n/rematch.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/codecvt_error_category.o build/opt/third_party/icu4c-57.1/source/common/ustr_cnv.o build/opt/mongo/bson/json.o build/opt/mongo/db/dbmessage.o build/opt/third_party/icu4c-57.1/source/i18n/measfmt.o build/opt/mongo/shell/shell_options.o build/opt/mongo/db/logical_session_id.o build/opt/mongo/db/repl/split_horizon.o build/opt/mongo/logger/message_log_domain.o build/opt/mongo/db/query/getmore_request.o build/opt/third_party/icu4c-57.1/source/i18n/sortkey.o build/opt/mongo/util/net/ssl_types.o build/opt/mongo/platform/random.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tanh.o build/opt/mongo/db/hasher.o build/opt/third_party/icu4c-57.1/source/i18n/fpositer.o build/opt/third_party/icu4c-57.1/source/i18n/digitaffixesandpadding.o build/opt/third_party/icu4c-57.1/source/i18n/csmatch.o build/opt/third_party/icu4c-57.1/source/i18n/tmunit.o build/opt/third_party/icu4c-57.1/source/i18n/curramt.o build/opt/third_party/icu4c-57.1/source/i18n/wintzimpl.o build/opt/third_party/icu4c-57.1/source/i18n/nultrans.o build/opt/third_party/icu4c-57.1/source/i18n/dayperiodrules.o build/opt/third_party/icu4c-57.1/source/i18n/decfmtst.o build/opt/third_party/icu4c-57.1/source/i18n/dangical.o build/opt/third_party/icu4c-57.1/source/i18n/regexcmp.o build/opt/third_party/icu4c-57.1/source/i18n/collationweights.o build/opt/third_party/icu4c-57.1/source/i18n/digitinterval.o build/opt/third_party/icu4c-57.1/source/i18n/buddhcal.o build/opt/third_party/icu4c-57.1/source/i18n/remtrans.o build/opt/third_party/icu4c-57.1/source/i18n/brktrans.o build/opt/third_party/icu4c-57.1/source/i18n/rbnf.o build/opt/third_party/icu4c-57.1/source/i18n/tzfmt.o build/opt/third_party/icu4c-57.1/source/i18n/regextxt.o build/opt/third_party/icu4c-57.1/source/i18n/cpdtrans.o build/opt/third_party/icu4c-57.1/source/i18n/timezone.o build/opt/third_party/icu4c-57.1/source/i18n/digitgrouping.o build/opt/third_party/icu4c-57.1/source/i18n/dtfmtsym.o build/opt/third_party/icu4c-57.1/source/i18n/alphaindex.o build/opt/third_party/icu4c-57.1/source/i18n/unum.o build/opt/third_party/icu4c-57.1/source/i18n/rbt_data.o build/opt/third_party/icu4c-57.1/source/i18n/uspoof_conf.o build/opt/third_party/icu4c-57.1/source/i18n/udatpg.o build/opt/third_party/icu4c-57.1/source/i18n/csdetect.o build/opt/third_party/icu4c-57.1/source/i18n/scientificnumberformatter.o build/opt/third_party/icu4c-57.1/source/i18n/tolowtrn.o build/opt/third_party/icu4c-57.1/source/i18n/astro.o build/opt/third_party/icu4c-57.1/source/i18n/uspoof_build.o build/opt/third_party/icu4c-57.1/source/i18n/translit.o build/opt/third_party/icu4c-57.1/source/i18n/smallintformatter.o build/opt/third_party/icu4c-57.1/source/i18n/plurrule.o build/opt/third_party/icu4c-57.1/source/i18n/plurfmt.o build/opt/third_party/icu4c-57.1/source/i18n/utmscale.o build/opt/third_party/icu4c-57.1/source/i18n/tridpars.o build/opt/third_party/icu4c-57.1/source/i18n/olsontz.o build/opt/third_party/icu4c-57.1/source/i18n/nfsubs.o build/opt/third_party/icu4c-57.1/source/i18n/titletrn.o build/opt/third_party/icu4c-57.1/source/i18n/collationcompare.o build/opt/third_party/icu4c-57.1/source/i18n/collationfastlatin.o build/opt/third_party/icu4c-57.1/source/i18n/valueformatter.o build/opt/third_party/icu4c-57.1/source/i18n/digitaffix.o build/opt/third_party/icu4c-57.1/source/i18n/tznames_impl.o build/opt/third_party/icu4c-57.1/source/i18n/collationruleparser.o build/opt/third_party/icu4c-57.1/source/i18n/currfmt.o build/opt/third_party/icu4c-57.1/source/i18n/decContext.o build/opt/third_party/icu4c-57.1/source/i18n/toupptrn.o build/opt/third_party/icu4c-57.1/source/i18n/tzrule.o build/opt/third_party/icu4c-57.1/source/i18n/ucol_res.o build/opt/third_party/icu4c-57.1/source/i18n/rbt_rule.o build/opt/third_party/icu4c-57.1/source/i18n/inputext.o build/opt/third_party/icu4c-57.1/source/i18n/numsys.o build/opt/third_party/icu4c-57.1/source/i18n/tznames.o build/opt/third_party/icu4c-57.1/source/i18n/collationsets.o build/opt/third_party/icu4c-57.1/source/i18n/uitercollationiterator.o build/opt/third_party/icu4c-57.1/source/i18n/uspoof_wsconf.o build/opt/third_party/icu4c-57.1/source/i18n/unumsys.o build/opt/third_party/icu4c-57.1/source/i18n/fphdlimp.o build/opt/third_party/icu4c-57.1/source/i18n/uregex.o build/opt/third_party/icu4c-57.1/source/i18n/sharedbreakiterator.o build/opt/third_party/icu4c-57.1/source/i18n/tmutfmt.o build/opt/third_party/icu4c-57.1/source/i18n/coptccal.o build/opt/third_party/icu4c-57.1/source/i18n/quant.o build/opt/third_party/icu4c-57.1/source/i18n/region.o build/opt/third_party/icu4c-57.1/source/i18n/decimfmt.o build/opt/third_party/icu4c-57.1/source/i18n/windtfmt.o build/opt/third_party/icu4c-57.1/source/i18n/uni2name.o build/opt/third_party/icu4c-57.1/source/i18n/uspoof_impl.o build/opt/third_party/icu4c-57.1/source/i18n/anytrans.o build/opt/third_party/icu4c-57.1/source/i18n/standardplural.o build/opt/third_party/icu4c-57.1/source/i18n/dtitvfmt.o build/opt/third_party/icu4c-57.1/source/i18n/ucal.o build/opt/third_party/icu4c-57.1/source/i18n/ulocdata.o build/opt/third_party/icu4c-57.1/source/i18n/usearch.o build/opt/third_party/icu4c-57.1/source/i18n/nfrs.o build/opt/third_party/icu4c-57.1/source/i18n/vzone.o build/opt/third_party/icu4c-57.1/source/i18n/uregexc.o build/opt/third_party/icu4c-57.1/source/i18n/zonemeta.o build/opt/third_party/icu4c-57.1/source/i18n/visibledigits.o build/opt/third_party/icu4c-57.1/source/i18n/ufieldpositer.o build/opt/third_party/icu4c-57.1/source/i18n/collation.o build/opt/third_party/icu4c-57.1/source/i18n/collationdatabuilder.o build/opt/third_party/icu4c-57.1/source/i18n/uregion.o build/opt/third_party/icu4c-57.1/source/i18n/taiwncal.o build/opt/third_party/icu4c-57.1/source/i18n/uspoof.o build/opt/third_party/icu4c-57.1/source/i18n/choicfmt.o build/opt/third_party/icu4c-57.1/source/i18n/collationdatareader.o build/opt/third_party/icu4c-57.1/source/i18n/casetrn.o build/opt/third_party/icu4c-57.1/source/i18n/fmtable.o build/opt/third_party/icu4c-57.1/source/i18n/ucoleitr.o build/opt/third_party/icu4c-57.1/source/i18n/tztrans.o build/opt/third_party/icu4c-57.1/source/i18n/coll.o build/opt/third_party/icu4c-57.1/source/i18n/vtzone.o build/opt/third_party/icu4c-57.1/source/i18n/utf8collationiterator.o build/opt/third_party/icu4c-57.1/source/i18n/collationiterator.o build/opt/third_party/icu4c-57.1/source/i18n/udateintervalformat.o build/opt/third_party/icu4c-57.1/source/i18n/ethpccal.o build/opt/third_party/icu4c-57.1/source/i18n/collationrootelements.o build/opt/third_party/icu4c-57.1/source/i18n/japancal.o build/opt/third_party/icu4c-57.1/source/i18n/rulebasedcollator.o build/opt/third_party/icu4c-57.1/source/i18n/tmutamt.o build/opt/third_party/icu4c-57.1/source/i18n/measunit.o build/opt/third_party/icu4c-57.1/source/i18n/fmtable_cnv.o build/opt/third_party/icu4c-57.1/source/i18n/currunit.o build/opt/third_party/icu4c-57.1/source/i18n/gregoimp.o build/opt/third_party/icu4c-57.1/source/i18n/udat.o build/opt/third_party/icu4c-57.1/source/i18n/rbt.o build/opt/third_party/icu4c-57.1/source/i18n/scriptset.o build/opt/third_party/icu4c-57.1/source/i18n/search.o build/opt/third_party/icu4c-57.1/source/i18n/transreg.o build/opt/third_party/icu4c-57.1/source/i18n/format.o build/opt/third_party/icu4c-57.1/source/i18n/smpdtfmt.o build/opt/third_party/icu4c-57.1/source/i18n/umsg.o build/opt/third_party/icu4c-57.1/source/i18n/persncal.o build/opt/third_party/icu4c-57.1/source/i18n/upluralrules.o build/opt/third_party/icu4c-57.1/source/i18n/numfmt.o build/opt/third_party/icu4c-57.1/source/i18n/digitlst.o build/opt/third_party/icu4c-57.1/source/i18n/bocsu.o build/opt/third_party/icu4c-57.1/source/i18n/repattrn.o build/opt/third_party/icu4c-57.1/source/i18n/csrsbcs.o build/opt/third_party/icu4c-57.1/source/i18n/currpinf.o build/opt/third_party/icu4c-57.1/source/i18n/collationkeys.o build/opt/third_party/icu4c-57.1/source/i18n/chnsecal.o build/opt/third_party/icu4c-57.1/source/i18n/msgfmt.o build/opt/third_party/icu4c-57.1/source/i18n/affixpatternparser.o build/opt/third_party/icu4c-57.1/source/i18n/decNumber.o build/opt/third_party/icu4c-57.1/source/i18n/digitformatter.o build/opt/third_party/icu4c-57.1/source/i18n/pluralaffix.o build/opt/third_party/icu4c-57.1/source/i18n/utf16collationiterator.o build/opt/third_party/icu4c-57.1/source/i18n/name2uni.o build/opt/third_party/icu4c-57.1/source/i18n/ucln_in.o build/opt/third_party/icu4c-57.1/source/i18n/winnmfmt.o build/opt/third_party/icu4c-57.1/source/i18n/identifier_info.o build/opt/third_party/icu4c-57.1/source/i18n/reldatefmt.o build/opt/third_party/icu4c-57.1/source/i18n/tzgnames.o build/opt/third_party/icu4c-57.1/source/i18n/islamcal.o build/opt/third_party/icu4c-57.1/source/i18n/strmatch.o build/opt/third_party/icu4c-57.1/source/i18n/basictz.o build/opt/third_party/icu4c-57.1/source/i18n/collationfastlatinbuilder.o build/opt/third_party/icu4c-57.1/source/i18n/nortrans.o build/opt/third_party/icu4c-57.1/source/i18n/collationroot.o build/opt/third_party/icu4c-57.1/source/i18n/decimfmtimpl.o build/opt/third_party/icu4c-57.1/source/i18n/ztrans.o build/opt/third_party/icu4c-57.1/source/i18n/collationfcd.o build/opt/third_party/icu4c-57.1/source/i18n/zrule.o build/opt/third_party/icu4c-57.1/source/i18n/dcfmtsym.o build/opt/third_party/icu4c-57.1/source/i18n/coleitr.o build/opt/third_party/icu4c-57.1/source/i18n/csr2022.o build/opt/third_party/icu4c-57.1/source/i18n/funcrepl.o build/opt/third_party/icu4c-57.1/source/i18n/datefmt.o build/opt/third_party/icu4c-57.1/source/i18n/ucsdet.o build/opt/third_party/icu4c-57.1/source/i18n/utrans.o build/opt/third_party/icu4c-57.1/source/i18n/ucol.o build/opt/third_party/icu4c-57.1/source/i18n/csrmbcs.o build/opt/third_party/icu4c-57.1/source/i18n/indiancal.o build/opt/third_party/icu4c-57.1/source/i18n/dtitvinf.o build/opt/third_party/icu4c-57.1/source/i18n/nfrule.o build/opt/third_party/icu4c-57.1/source/i18n/rbt_set.o build/opt/third_party/icu4c-57.1/source/i18n/collationdatawriter.o build/opt/third_party/icu4c-57.1/source/i18n/gender.o build/opt/third_party/icu4c-57.1/source/i18n/smpdtfst.o build/opt/third_party/icu4c-57.1/source/i18n/hebrwcal.o build/opt/third_party/icu4c-57.1/source/i18n/rbt_pars.o build/opt/third_party/icu4c-57.1/source/i18n/decimalformatpattern.o build/opt/third_party/icu4c-57.1/source/i18n/precision.o build/opt/third_party/icu4c-57.1/source/i18n/stsearch.o build/opt/third_party/icu4c-57.1/source/i18n/cecal.o build/opt/third_party/icu4c-57.1/source/i18n/quantityformatter.o build/opt/third_party/icu4c-57.1/source/i18n/strrepl.o build/opt/third_party/icu4c-57.1/source/i18n/dtrule.o build/opt/third_party/icu4c-57.1/source/i18n/compactdecimalformat.o build/opt/third_party/s2/base/int128.o build/opt/third_party/s2/base/logging.o build/opt/third_party/mozjs-60/extract/mfbt/Compression.o build/opt/third_party/icu4c-57.1/source/common/unistr_case_locale.o build/opt/mongo/util/dns_query.o build/opt/mongo/crypto/sha1_block.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src7.o build/opt/mongo/db/pipeline/parsed_aggregation_projection.o build/opt/mongo/s/request_types/create_database_gen.o build/opt/third_party/pcre-8.42/pcre_dfa_exec.o build/opt/third_party/icu4c-57.1/source/common/dictbe.o build/opt/mongo/db/ops/write_ops_gen.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_ceil.o build/opt/mongo/client/native_sasl_client_session.o build/opt/mongo/db/geo/geoparser.o build/opt/mongo/db/geo/geometry_container.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_from_int.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_trig.o build/opt/mongo/db/catalog/index_catalog_entry.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_hypot.o build/opt/mongo/base/data_type_string_data.o build/opt/mongo/client/sasl_plain_client_conversation.o build/opt/third_party/pcre-8.42/pcre_valid_utf8.o build/opt/mongo/platform/process_id.o build/opt/third_party/shim_fmt.o build/opt/mongo/shell/bench.o build/opt/third_party/icu4c-57.1/source/common/ustrtrns.o build/opt/third_party/icu4c-57.1/source/common/umath.o build/opt/mongo/s/database_version_gen.o build/opt/mongo/transport/message_compressor_registry.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_next.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_noncomp.o build/opt/mongo/util/errno_util.o build/opt/third_party/pcre-8.42/pcre_stringpiece.o build/opt/third_party/s2/util/coding/varint.o build/opt/mongo/db/matcher/schema/expression_internal_schema_str_length.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_pow.o build/opt/third_party/icu4c-57.1/source/common/patternprops.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_rem.o build/opt/mongo/rpc/op_msg.o build/opt/mongo/s/catalog/type_locks.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_bessel.o build/opt/mongo/shell/kms_gen.o build/opt/mongo/util/md5.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int8.o build/opt/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/fastcover.o build/opt/third_party/timelib-2018.01/timelib.o build/opt/mongo/db/commands/server_status_internal.o build/opt/mongo/db/auth/restriction_environment.o build/opt/third_party/abseil-cpp-master/abseil-cpp/absl/hash/internal/hash.o build/opt/third_party/abseil-cpp-master/abseil-cpp/absl/hash/internal/city.o build/opt/mongo/bson/bsontypes.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fetestexcept.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_erf.o build/opt/mongo/base/secure_allocator.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_compress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_double_fast.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_mul.o build/opt/third_party/pcre-8.42/pcre_ucd.o build/opt/third_party/icu4c-57.1/source/common/unames.o build/opt/third_party/icu4c-57.1/source/common/ustr_wcs.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tan.o build/opt/mongo/s/chunk_version.o build/opt/mongo/db/server_options_base.o build/opt/third_party/gperftools-2.7/dist/src/base/dynamic_annotations.o build/opt/mongo/db/query/count_command_gen.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_acosh.o build/opt/mongo/transport/service_executor_reserved.o build/opt/mongo/base/make_string_vector.o build/opt/mongo/db/pipeline/document_source_merge_gen.o build/opt/mongo/db/index/index_descriptor.o build/opt/third_party/icu4c-57.1/source/common/bytestream.o build/opt/third_party/mozjs-60/extract/js/src/vm/JSAtom.o build/opt/third_party/kms-message/src/kms_request.o build/opt/third_party/icu4c-57.1/source/common/utrace.o build/opt/third_party/icu4c-57.1/source/common/resource.o build/opt/mongo/util/shell_exec.o build/opt/mongo/platform/strnlen.o build/opt/mongo/logger/message_event_utf8_encoder.o build/opt/mongo/db/auth/resource_pattern.o build/opt/third_party/kms-message/src/kms_encrypt_request.o build/opt/third_party/kms-message/src/kms_response.o build/opt/third_party/kms-message/src/kms_request_opt.o build/opt/third_party/kms-message/src/sort.o build/opt/third_party/kms-message/src/kms_kv_list.o build/opt/third_party/kms-message/src/hexlify.o build/opt/third_party/kms-message/src/kms_message.o build/opt/third_party/kms-message/src/kms_response_parser.o build/opt/third_party/kms-message/src/kms_decrypt_request.o build/opt/third_party/kms-message/src/kms_b64.o build/opt/third_party/kms-message/src/kms_crypto_openssl.o build/opt/third_party/kms-message/src/kms_request_str.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_asin.o build/opt/mongo/logger/logstream_builder.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tan.o build/opt/mongo/util/background.o build/opt/mongo/db/fts/fts_language.o build/opt/mongo/db/fts/fts_spec_legacy.o build/opt/mongo/db/fts/tokenizer.o build/opt/mongo/db/fts/fts_basic_tokenizer.o build/opt/mongo/db/fts/stop_words.o build/opt/mongo/db/fts/fts_basic_phrase_matcher.o build/opt/mongo/db/fts/fts_query_parser.o build/opt/mongo/db/fts/fts_index_format.o build/opt/mongo/db/fts/fts_unicode_phrase_matcher.o build/opt/mongo/db/fts/fts_query_impl.o build/opt/mongo/db/fts/fts_util.o build/opt/mongo/db/fts/fts_spec.o build/opt/mongo/db/fts/fts_unicode_tokenizer.o build/opt/third_party/libstemmer_c/libstemmer/libstemmer_utf8.o build/opt/third_party/timelib-2018.01/parse_iso_intervals.o build/opt/mongo/transport/message_compressor_options_client_gen.o build/opt/mongo/scripting/mozjs/db.o build/opt/mongo/s/catalog/type_lockpings.o build/opt/third_party/icu4c-57.1/source/common/cstr.o build/opt/third_party/icu4c-57.1/source/common/wintz.o build/opt/mongo/util/quick_exit.o build/opt/third_party/icu4c-57.1/source/common/ucnv_err.o build/opt/mongo/db/server_options_helpers_gen.o build/opt/mongo/db/pipeline/dependencies.o build/opt/third_party/icu4c-57.1/source/common/dictionarydata.o build/opt/mongo/db/storage/key_string.o build/opt/mongo/db/catalog/disable_index_spec_namespace_generation_gen.o build/opt/mongo/logger/component_message_log_domain.o build/opt/mongo/util/net/hostandport_gen.o build/opt/third_party/s2/s2cap.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int64.o build/opt/third_party/icu4c-57.1/source/common/uhash_us.o build/opt/third_party/boost-1.70.0/libs/iostreams/src/file_descriptor.o build/opt/mongo/scripting/mozjs/mongohelpers_js.o build/opt/third_party/boost-1.70.0/libs/iostreams/src/mapped_file.o build/opt/mongo/db/logical_session_id_gen.o build/opt/mongo/rpc/protocol.o build/opt/third_party/pcre-8.42/pcre_scanner.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_decimal_globals.o build/opt/third_party/icu4c-57.1/source/common/ures_cnv.o build/opt/mongo/db/repl/repl_settings_gen.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/windows_file_codecvt.o build/opt/third_party/icu4c-57.1/source/common/resbund_cnv.o build/opt/mongo/logger/log_component_settings.o build/opt/mongo/db/geo/hash.o build/opt/third_party/pcre-8.42/pcre_compile.o build/opt/mongo/db/query/cursor_response.o build/opt/mongo/db/repl/replication_coordinator_noop.o build/opt/mongo/db/stats/counters.o build/opt/third_party/s2/s2polygon.o build/opt/mongo/util/net/ssl_options_client.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_2_str_tables.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src39.o build/opt/third_party/icu4c-57.1/source/common/cwchar.o build/opt/mongo/bson/bson_validate.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src18.o build/opt/third_party/icu4c-57.1/source/common/bmpset.o build/opt/mongo/util/fail_point.o build/opt/mongo/shell/shell_options_storage.o build/opt/mongo/client/authenticate.o build/opt/mongo/db/pipeline/document_comparator.o build/opt/third_party/icu4c-57.1/source/common/ucnv_bld.o build/opt/third_party/icu4c-57.1/source/common/icudataver.o build/opt/third_party/icu4c-57.1/source/common/brkiter.o build/opt/third_party/icu4c-57.1/source/common/ucnvhz.o build/opt/third_party/icu4c-57.1/source/common/ucasemap.o build/opt/third_party/icu4c-57.1/source/common/uchar.o build/opt/third_party/icu4c-57.1/source/common/uloc_keytype.o build/opt/third_party/icu4c-57.1/source/common/pluralmap.o build/opt/third_party/icu4c-57.1/source/common/ucnv_u8.o build/opt/third_party/icu4c-57.1/source/common/ubidi.o build/opt/third_party/icu4c-57.1/source/common/usetiter.o build/opt/third_party/icu4c-57.1/source/common/loclikely.o build/opt/third_party/icu4c-57.1/source/common/rbbitblb.o build/opt/third_party/icu4c-57.1/source/common/normalizer2.o build/opt/third_party/icu4c-57.1/source/common/ustring.o build/opt/third_party/icu4c-57.1/source/common/ucnv_ct.o build/opt/third_party/icu4c-57.1/source/common/cmemory.o build/opt/third_party/icu4c-57.1/source/common/uchriter.o build/opt/third_party/icu4c-57.1/source/common/uniset.o build/opt/third_party/icu4c-57.1/source/common/ucnv_set.o build/opt/third_party/icu4c-57.1/source/common/locutil.o build/opt/third_party/icu4c-57.1/source/common/ucnvscsu.o build/opt/third_party/icu4c-57.1/source/common/unistr.o build/opt/third_party/icu4c-57.1/source/common/charstr.o build/opt/third_party/icu4c-57.1/source/common/ulist.o build/opt/third_party/icu4c-57.1/source/common/ucnv2022.o build/opt/third_party/icu4c-57.1/source/common/normalizer2impl.o build/opt/third_party/icu4c-57.1/source/common/schriter.o build/opt/third_party/icu4c-57.1/source/common/ucnvlat1.o build/opt/third_party/icu4c-57.1/source/common/ucurr.o build/opt/third_party/icu4c-57.1/source/common/uniset_closure.o build/opt/third_party/icu4c-57.1/source/common/uscript_props.o build/opt/third_party/icu4c-57.1/source/common/ucnv_lmb.o build/opt/third_party/icu4c-57.1/source/common/ucnv.o build/opt/third_party/icu4c-57.1/source/common/uresdata.o build/opt/third_party/icu4c-57.1/source/common/utf_impl.o build/opt/third_party/icu4c-57.1/source/common/bytestrie.o build/opt/third_party/icu4c-57.1/source/common/utypes.o build/opt/third_party/icu4c-57.1/source/common/chariter.o build/opt/third_party/icu4c-57.1/source/common/servls.o build/opt/third_party/icu4c-57.1/source/common/ucnvdisp.o build/opt/third_party/icu4c-57.1/source/common/ucat.o build/opt/third_party/icu4c-57.1/source/common/uenum.o build/opt/third_party/icu4c-57.1/source/common/uinvchar.o build/opt/third_party/icu4c-57.1/source/common/uloc_tag.o build/opt/third_party/icu4c-57.1/source/common/simpleformatter.o build/opt/third_party/icu4c-57.1/source/common/ucnvmbcs.o build/opt/third_party/icu4c-57.1/source/common/locmap.o build/opt/third_party/icu4c-57.1/source/common/uts46.o build/opt/third_party/icu4c-57.1/source/common/bytestriebuilder.o build/opt/third_party/icu4c-57.1/source/common/uset_props.o build/opt/third_party/icu4c-57.1/source/common/icuplug.o build/opt/third_party/icu4c-57.1/source/common/ushape.o build/opt/third_party/icu4c-57.1/source/common/rbbirb.o build/opt/third_party/icu4c-57.1/source/common/ucnv_io.o build/opt/third_party/icu4c-57.1/source/common/unistr_props.o build/opt/third_party/icu4c-57.1/source/common/util_props.o build/opt/third_party/icu4c-57.1/source/common/rbbistbl.o build/opt/third_party/icu4c-57.1/source/common/unormcmp.o build/opt/third_party/icu4c-57.1/source/common/rbbinode.o build/opt/third_party/icu4c-57.1/source/common/servnotf.o build/opt/third_party/icu4c-57.1/source/common/ucnv_u32.o build/opt/third_party/icu4c-57.1/source/common/ucnv_u7.o build/opt/third_party/icu4c-57.1/source/common/loadednormalizer2impl.o build/opt/third_party/icu4c-57.1/source/common/ubidi_props.o build/opt/third_party/icu4c-57.1/source/common/sharedobject.o build/opt/third_party/icu4c-57.1/source/common/ucmndata.o build/opt/third_party/icu4c-57.1/source/common/utrie.o build/opt/third_party/icu4c-57.1/source/common/ucnvbocu.o build/opt/third_party/icu4c-57.1/source/common/locavailable.o build/opt/third_party/icu4c-57.1/source/common/uset.o build/opt/third_party/icu4c-57.1/source/common/udataswp.o build/opt/third_party/icu4c-57.1/source/common/serv.o build/opt/third_party/icu4c-57.1/source/common/unistr_case.o build/opt/third_party/icu4c-57.1/source/common/ruleiter.o build/opt/third_party/icu4c-57.1/source/common/util.o build/opt/third_party/icu4c-57.1/source/common/ucharstrie.o build/opt/third_party/icu4c-57.1/source/common/messagepattern.o build/opt/third_party/icu4c-57.1/source/common/umutex.o build/opt/third_party/icu4c-57.1/source/common/ucasemap_titlecase_brkiter.o build/opt/third_party/icu4c-57.1/source/common/propname.o build/opt/third_party/icu4c-57.1/source/common/bytestrieiterator.o build/opt/third_party/icu4c-57.1/source/common/utrie2_builder.o build/opt/third_party/icu4c-57.1/source/common/uprops.o build/opt/third_party/icu4c-57.1/source/common/cstring.o build/opt/third_party/icu4c-57.1/source/common/ustrfmt.o build/opt/third_party/icu4c-57.1/source/common/ustack.o build/opt/third_party/icu4c-57.1/source/common/errorcode.o build/opt/third_party/icu4c-57.1/source/common/servlkf.o build/opt/third_party/icu4c-57.1/source/common/uiter.o build/opt/third_party/icu4c-57.1/source/common/caniter.o build/opt/third_party/icu4c-57.1/source/common/ubrk.o build/opt/third_party/icu4c-57.1/source/common/locid.o build/opt/third_party/icu4c-57.1/source/common/unistr_titlecase_brkiter.o build/opt/third_party/icu4c-57.1/source/common/ucnvsel.o build/opt/third_party/icu4c-57.1/source/common/uvectr32.o build/opt/third_party/icu4c-57.1/source/common/ubidiln.o build/opt/third_party/icu4c-57.1/source/common/uinit.o build/opt/third_party/icu4c-57.1/source/common/servrbf.o build/opt/third_party/icu4c-57.1/source/common/usc_impl.o build/opt/third_party/icu4c-57.1/source/common/uhash.o build/opt/third_party/icu4c-57.1/source/common/ucln_cmn.o build/opt/third_party/icu4c-57.1/source/common/unorm.o build/opt/third_party/icu4c-57.1/source/common/ustr_titlecase_brkiter.o build/opt/third_party/icu4c-57.1/source/common/utext.o build/opt/third_party/icu4c-57.1/source/common/ucnv_cnv.o build/opt/third_party/icu4c-57.1/source/common/resbund.o build/opt/third_party/icu4c-57.1/source/common/uidna.o build/opt/third_party/icu4c-57.1/source/common/servlk.o build/opt/third_party/icu4c-57.1/source/common/uniset_props.o build/opt/third_party/icu4c-57.1/source/common/udata.o build/opt/third_party/icu4c-57.1/source/common/usprep.o build/opt/third_party/icu4c-57.1/source/common/ucnvisci.o build/opt/third_party/icu4c-57.1/source/common/punycode.o build/opt/third_party/icu4c-57.1/source/common/rbbiscan.o build/opt/third_party/icu4c-57.1/source/common/appendable.o build/opt/third_party/icu4c-57.1/source/common/ubidiwrt.o build/opt/third_party/icu4c-57.1/source/common/ucharstrieiterator.o build/opt/third_party/icu4c-57.1/source/common/uscript.o build/opt/third_party/icu4c-57.1/source/common/unifilt.o build/opt/third_party/icu4c-57.1/source/common/uloc.o build/opt/third_party/icu4c-57.1/source/common/ustrcase.o build/opt/third_party/icu4c-57.1/source/common/stringtriebuilder.o build/opt/third_party/icu4c-57.1/source/common/locbased.o build/opt/third_party/icu4c-57.1/source/common/uobject.o build/opt/third_party/icu4c-57.1/source/common/servslkf.o build/opt/third_party/icu4c-57.1/source/common/rbbidata.o build/opt/third_party/icu4c-57.1/source/common/locdispnames.o build/opt/third_party/icu4c-57.1/source/common/rbbi.o build/opt/third_party/icu4c-57.1/source/common/putil.o build/opt/third_party/icu4c-57.1/source/common/ucnv_u16.o build/opt/third_party/icu4c-57.1/source/common/ucnv_ext.o build/opt/third_party/icu4c-57.1/source/common/ustrenum.o build/opt/third_party/icu4c-57.1/source/common/propsvec.o build/opt/third_party/icu4c-57.1/source/common/filteredbrk.o build/opt/third_party/icu4c-57.1/source/common/ucnv_cb.o build/opt/third_party/icu4c-57.1/source/common/uresbund.o build/opt/mongo/db/concurrency/lock_state.o build/opt/mongo/base/error_extra_info.o build/opt/mongo/bson/bsonobj.o build/opt/mongo/rpc/metadata/repl_set_metadata.o build/opt/third_party/shim_abseil.o build/opt/mongo/db/pipeline/expression_trigonometric.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_quantize.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_convert_data.o build/opt/mongo/transport/transport_layer.o build/opt/mongo/s/would_change_owning_shard_exception.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src20.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_floorf.o build/opt/mongo/db/matcher/rewrite_expr.o build/opt/mongo/db/catalog/index_key_validate.o build/opt/mongo/util/fail_point_server_parameter_gen.o build/opt/mongo/util/fail_point_registry.o build/opt/mongo/util/fail_point_service.o build/opt/mongo/util/net/cidr.o build/opt/mongo/util/net/socket_utils.o build/opt/mongo/util/net/hostname_canonicalization.o build/opt/mongo/util/net/socket_exception.o build/opt/mongo/util/net/hostandport.o build/opt/mongo/util/net/sockaddr.o build/opt/third_party/s2/util/math/mathutil.o build/opt/third_party/wiredtiger/src/checksum/software/checksum.o build/opt/mongo/scripting/mozjs/session.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_acosh.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src40.o build/opt/mongo/db/concurrency/replication_state_transition_lock_guard.o build/opt/mongo/base/initializer.o build/opt/mongo/idl/server_parameter.o build/opt/mongo/client/query.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_logb.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src11.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_next.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lround.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_rem.o build/opt/mongo/db/query/count_command_as_aggregation_command.o build/opt/mongo/rpc/metadata/config_server_metadata.o build/opt/mongo/db/multi_key_path_tracker.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_modf.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_frexp.o build/opt/mongo/db/auth/user_management_commands_parser.o build/opt/mongo/scripting/mozjs/numberint.o build/opt/mongo/db/auth/role_name.o build/opt/mongo/db/query/explain_options.o build/opt/mongo/scripting/mozjs/timestamp.o build/opt/mongo/scripting/mozjs/object.o build/opt/mongo/executor/network_interface_factory.o build/opt/mongo/util/options_parser/startup_options.o build/opt/mongo/shell/mongo-server.o build/opt/mongo/db/matcher/schema/expression_internal_schema_num_array_items.o build/opt/mongo/platform/stack_locator_linux.o build/opt/third_party/gperftools-2.7/dist/src/tcmalloc.o build/opt/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/cover.o build/opt/mongo/db/pipeline/expression_context.o build/opt/mongo/db/pipeline/variables.o build/opt/mongo/util/net/ssl_stream.o build/opt/mongo/db/traffic_reader.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_danish.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_compare.o build/opt/mongo/util/secure_zero_memory.o build/opt/third_party/shim_kms_message.o build/opt/mongo/db/repl/repl_settings.o build/opt/mongo/util/version_impl.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_expm1.o build/opt/mongo/db/geo/r2_region_coverer.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/portability.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sin.o build/opt/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_common.o build/opt/mongo/client/async_client.o build/opt/mongo/s/catalog/type_collection.o build/opt/mongo/executor/network_interface_thread_pool.o build/opt/third_party/pcre-8.42/pcre_fullinfo.o build/opt/mongo/util/version.o build/opt/mongo/util/icu_init.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_porter.o build/opt/mongo/db/commands.o build/opt/third_party/zstandard-1.3.7/zstd/lib/decompress/zstd_decompress.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_mul.o build/opt/mongo/bson/bson_depth.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/huf_compress.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int8.o build/opt/mongo/rpc/object_check.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atanh.o build/opt/mongo/shell/kms_shell.o build/opt/mongo/util/boost_assert_shim.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_powi.o build/opt/mongo/db/query/cursor_request.o build/opt/mongo/scripting/mozjs/numberdecimal.o build/opt/mongo/client/sasl_scram_client_conversation.o build/opt/mongo/util/assert_util.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_quantize.o build/opt/mongo/util/net/http_client_curl.o build/opt/mongo/logger/rotatable_file_manager.o build/opt/third_party/mozjs-60/extract/js/src/mfbt/Unified_cpp_mfbt0.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src45.o build/opt/mongo/db/pipeline/document_source_merge_modes_gen.o build/opt/third_party/mozjs-60/extract/mozglue/misc/Printf.o build/opt/third_party/mozjs-60/extract/mozglue/misc/ConditionVariable_posix.o build/opt/mongo/client/dbclient_base.o build/opt/mongo/s/request_types/migration_secondary_throttle_options.o build/opt/mongo/s/catalog/type_chunk.o build/opt/mongo/s/catalog/type_config_version.o build/opt/mongo/s/cannot_implicitly_create_collection_info.o build/opt/mongo/s/request_types/update_zone_key_range_request_type.o build/opt/mongo/s/catalog/type_tags.o build/opt/mongo/s/request_types/shard_collection_gen.o build/opt/mongo/s/database_version_helpers.o build/opt/mongo/s/request_types/flush_database_cache_updates_gen.o build/opt/mongo/s/request_types/merge_chunk_request_type.o build/opt/mongo/s/request_types/balance_chunk_request_type.o build/opt/mongo/s/request_types/split_chunk_request_type.o build/opt/mongo/s/catalog/mongo_version_range.o build/opt/mongo/s/catalog/type_shard.o build/opt/mongo/s/catalog/type_chunk_base_gen.o build/opt/mongo/s/request_types/clear_jumbo_flag_gen.o build/opt/mongo/s/request_types/set_shard_version_request.o build/opt/mongo/s/request_types/wait_for_fail_point_gen.o build/opt/mongo/s/request_types/get_database_version_gen.o build/opt/mongo/s/request_types/remove_shard_from_zone_request_type.o build/opt/mongo/s/catalog/type_mongos.o build/opt/mongo/s/request_types/clone_collection_options_from_primary_shard_gen.o build/opt/mongo/s/catalog/type_shard_database.o build/opt/mongo/s/request_types/add_shard_request_type.o build/opt/mongo/s/request_types/create_collection_gen.o build/opt/mongo/s/catalog/type_shard_collection.o build/opt/mongo/s/request_types/clone_catalog_data_gen.o build/opt/mongo/s/request_types/move_chunk_request.o build/opt/mongo/s/chunk_version_gen.o build/opt/mongo/s/request_types/flush_routing_table_cache_updates_gen.o build/opt/mongo/s/request_types/move_primary_gen.o build/opt/mongo/s/shard_id.o build/opt/mongo/db/repl/repl_client_info.o build/opt/mongo/logger/log_manager.o build/opt/mongo/rpc/metadata/impersonated_user_metadata_gen.o build/opt/mongo/rpc/metadata/impersonated_user_metadata.o build/opt/mongo/base/parse_number.o build/opt/mongo/scripting/mozjs/regexp.o build/opt/mongo/shell/shell_utils.o build/opt/mongo/base/initializer_dependency_graph.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp.o build/opt/third_party/mozjs-60/extract/js/src/vm/Interpreter.o build/opt/mongo/db/pipeline/parsed_aggregation_projection_node.o build/opt/mongo/db/pipeline/parsed_add_fields.o build/opt/mongo/db/pipeline/parsed_exclusion_projection.o build/opt/mongo/db/pipeline/parsed_inclusion_projection.o build/opt/mongo/transport/message_compressor_zlib.o build/opt/mongo/transport/message_compressor_zstd.o build/opt/mongo/transport/message_compressor_metrics.o build/opt/mongo/transport/message_compressor_snappy.o build/opt/mongo/db/ops/write_ops_parsers.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src27.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_rintf.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src1.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_atan.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_fabs.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/k_exp.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src31.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src5.o build/opt/third_party/mozjs-60/extract/js/src/perf/pm_stub.o build/opt/third_party/mozjs-60/extract/js/src/frontend/Parser.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src30.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src25.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src13.o build/opt/third_party/mozjs-60/extract/js/src/jsmath.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_ceilf.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src10.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src3.o build/opt/third_party/mozjs-60/extract/js/src/builtin/RegExp.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src23.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src26.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_atan2.o build/opt/third_party/mozjs-60/extract/js/src/jsarray.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src43.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src16.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_acos.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src29.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src8.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src12.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src28.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src2.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_log2.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_hypot.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src24.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src17.o build/opt/third_party/mozjs-60/extract/mozglue/misc/StackWalk.o build/opt/third_party/mozjs-60/mongo_sources/mongoErrorReportToString.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_nearbyint.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src41.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src6.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_scalbn.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src35.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src36.o build/opt/third_party/mozjs-60/extract/js/src/jit/x86-shared/Disassembler-x86-shared.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src22.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_log.o build/opt/third_party/mozjs-60/extract/mfbt/lz4.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src44.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src0.o build/opt/third_party/mozjs-60/extract/mozglue/misc/Mutex_posix.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src34.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_truncf.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src19.o build/opt/third_party/mozjs-60/extract/js/src/util/DoubleToString.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_pow.o build/opt/third_party/mozjs-60/mongo_sources/freeOpToJSContext.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_copysign.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_trunc.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_tanh.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_log10.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_asin.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_exp.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src9.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_cosh.o build/opt/third_party/mozjs-60/extract/mozglue/misc/TimeStamp_posix.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_floor.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_sinh.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src14.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_asinh.o build/opt/third_party/mozjs-60/extract/mozglue/misc/TimeStamp.o build/opt/third_party/mozjs-60/platform/x86_64/linux/build/Unified_cpp_js_src38.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_cbrt.o build/opt/mongo/db/fts/unicode/codepoints_casefold.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lgamma.o build/opt/third_party/boost-1.70.0/libs/program_options/src/positional_options.o build/opt/mongo/executor/remote_command_response.o build/opt/mongo/executor/remote_command_request.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_romanian.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_spanish.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_quantexpd.o build/opt/mongo/db/command_generic_argument.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/xxhash.o build/opt/mongo/db/fts/unicode/codepoints_delimiter_list.o build/opt/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.o build/opt/mongo/crypto/sha_block_openssl.o build/opt/third_party/gperftools-2.7/dist/src/maybe_threads.o build/opt/third_party/pcre-8.42/pcrecpp.o build/opt/mongo/scripting/mozjs/jsthread.o build/opt/third_party/pcre-8.42/pcre_globals.o build/opt/third_party/shim_icu.o build/opt/mongo/db/global_settings.o build/opt/mongo/db/field_ref.o build/opt/third_party/s2/s2edgeutil.o build/opt/third_party/shim_boost.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_italian.o build/opt/third_party/shim_yaml.o build/opt/third_party/zlib-1.2.11/zutil.o build/opt/mongo/shell/shell_options_gen.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_acosh.o build/opt/third_party/pcre-8.42/pcre_refcount.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fma.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_turkish.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint16.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp.o build/opt/mongo/db/operation_context_group.o build/opt/mongo/transport/transport_layer_manager.o build/opt/mongo/bson/bsonobjbuilder.o build/opt/mongo/transport/service_executor_synchronous.o build/opt/mongo/util/options_parser/constraints.o build/opt/third_party/gperftools-2.7/dist/src/common.o build/opt/mongo/db/catalog/index_catalog.o build/opt/mongo/db/fts/unicode/string.o build/opt/mongo/db/exec/projection_exec_agg.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_div.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atan.o build/opt/third_party/s2/s2cellunion.o build/opt/mongo/util/password_params_gen.o build/opt/mongo/util/password.o build/opt/mongo/db/query/tailable_mode.o build/opt/mongo/db/query/query_request.o build/opt/mongo/db/query/tailable_mode_gen.o build/opt/third_party/timelib-2018.01/dow.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sqrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod128.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_pow.o build/opt/third_party/shim_zstd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_scalbl.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_erfc.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int32.o build/opt/mongo/util/cmdline_utils/censor_cmdline.o build/opt/mongo/shell/fle_shell_options_gen.o build/opt/mongo/util/password_digest.o build/opt/mongo/db/concurrency/lock_stats.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod32.o build/opt/mongo/rpc/reply_builder_interface.o build/opt/third_party/s2/s2regionintersection.o build/opt/mongo/db/matcher/extensions_callback_noop.o build/opt/mongo/platform/stack_locator.o build/opt/mongo/util/options_parser/option_section.o build/opt/mongo/bson/bson_comparator_interface_base.o build/opt/mongo/bson/mutable/element.o build/opt/mongo/util/options_parser/option_description.o build/opt/mongo/util/signal_win32.o build/opt/mongo/util/signal_handlers.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_minmax.o build/opt/mongo/db/auth/action_set.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_string.o build/opt/mongo/rpc/metadata/tracking_metadata.o build/opt/mongo/db/pipeline/expression.o build/opt/third_party/gperftools-2.7/dist/src/emergency_malloc_for_stacktrace.o build/opt/mongo/rpc/object_check_gen.o build/opt/mongo/util/startup_test.o build/opt/mongo/db/fts/unicode/codepoints_diacritic_list.o build/opt/mongo/db/repl/replication_coordinator.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_string.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_compare.o build/opt/third_party/boost-1.70.0/libs/program_options/src/config_file.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint64.o build/opt/mongo/util/concurrency/thread_name.o build/opt/third_party/s2/s2cellid.o build/opt/third_party/shim_zlib.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint8.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/operations.o build/opt/third_party/pcre-8.42/pcre_string_utils.o build/opt/mongo/db/auth/authorization_session.o build/opt/third_party/zlib-1.2.11/inflate.o build/opt/mongo/db/service_context.o build/opt/mongo/platform/shared_library.o build/opt/mongo/db/cluster_auth_mode_option_gen.o build/opt/mongo/db/matcher/schema/expression_internal_schema_match_array_index.o build/opt/mongo/db/operation_context.o build/opt/mongo/util/net/ssl_options.o build/opt/mongo/shell/kms_local.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_llrintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint32.o build/opt/mongo/util/boost_assert_impl.o build/opt/mongo/base/string_data.o build/opt/mongo/util/duration.o build/opt/mongo/base/data_type.o build/opt/mongo/base/data_type_terminated.o build/opt/mongo/base/validate_locale.o build/opt/mongo/logger/redaction.o build/opt/mongo/util/time_support.o build/opt/mongo/util/stacktrace_posix.o build/opt/mongo/bson/simple_bsonobj_comparator.o build/opt/mongo/bson/timestamp.o build/opt/mongo/base/data_range.o build/opt/mongo/util/file.o build/opt/mongo/util/uuid.o build/opt/mongo/logger/log_severity.o build/opt/mongo/bson/bsonelement.o build/opt/mongo/platform/shared_library_posix.o build/opt/mongo/bson/oid.o build/opt/mongo/logger/rotatable_file_writer.o build/opt/mongo/base/data_range_cursor.o build/opt/mongo/logger/logger.o build/opt/mongo/base/transaction_error.o build/opt/mongo/base/shim.o build/opt/mongo/util/itoa.o build/opt/mongo/base/simple_string_data_comparator.o build/opt/mongo/base/error_codes.o build/opt/mongo/logger/log_component.o build/opt/mongo/platform/posix_fadvise.o build/opt/mongo/util/exit.o build/opt/mongo/bson/simple_bsonelement_comparator.o build/opt/mongo/util/stacktrace.o build/opt/mongo/platform/decimal128.o build/opt/mongo/platform/mutex.o build/opt/mongo/util/system_clock_source.o build/opt/mongo/util/hex.o build/opt/mongo/logger/console.o build/opt/mongo/bson/bsonmisc.o build/opt/mongo/logger/ramlog.o build/opt/mongo/util/base64.o build/opt/mongo/base/global_initializer.o build/opt/mongo/util/platform_init.o build/opt/mongo/base/global_initializer_registerer.o build/opt/mongo/util/str.o build/opt/mongo/base/init.o build/opt/mongo/platform/strcasestr.o build/opt/mongo/executor/network_interface_tl.o build/opt/mongo/scripting/mozjs/countdownlatch.o build/opt/mongo/scripting/mozjs/cursor_handle.o build/opt/mongo/scripting/mozjs/bindata.o build/opt/mongo/scripting/mozjs/error.o build/opt/mongo/scripting/mozjs/mongohelpers.o build/opt/mongo/scripting/mozjs/jscustomallocator.o build/opt/mongo/scripting/mozjs/internedstring.o build/opt/mongo/scripting/mozjs/numberlong.o build/opt/mongo/scripting/mozjs/code.o build/opt/mongo/scripting/mozjs/global.o build/opt/mongo/scripting/mozjs/objectwrapper.o build/opt/mongo/scripting/mozjs/idwrapper.o build/opt/mongo/scripting/mozjs/dbref.o build/opt/mongo/scripting/mozjs/maxkey.o build/opt/mongo/scripting/mozjs/minkey.o build/opt/mongo/scripting/mozjs/engine.o build/opt/mongo/scripting/mozjs/mongo.o build/opt/mongo/scripting/mozjs/PosixNSPR.o build/opt/mongo/scripting/mozjs/cursor.o build/opt/mongo/scripting/mozjs/oid.o build/opt/mongo/scripting/mozjs/exception.o build/opt/mongo/scripting/mozjs/engine_gen.o build/opt/mongo/scripting/mozjs/valuereader.o build/opt/mongo/scripting/mozjs/scripting_util_gen.o build/opt/mongo/scripting/mozjs/valuewriter.o build/opt/mongo/scripting/mozjs/implscope.o build/opt/mongo/scripting/mozjs/status.o build/opt/mongo/scripting/mozjs/jsstringwrapper.o build/opt/mongo/scripting/mozjs/nativefunction.o build/opt/mongo/scripting/mozjs/dbquery.o build/opt/mongo/util/net/private/ssl_expiration.o build/opt/mongo/util/net/sock.o build/opt/mongo/util/net/private/socket_poll.o build/opt/mongo/transport/transport_layer_asio.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_bid64.o build/opt/mongo/shell/kms.o build/opt/third_party/boost-1.70.0/libs/program_options/src/options_description.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp10.o build/opt/mongo/db/server_options_helpers.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_nearbyintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_nearbyintd.o build/opt/third_party/zlib-1.2.11/adler32.o build/opt/mongo/db/auth/impersonation_session.o build/opt/mongo/db/auth/privilege_parser.o build/opt/mongo/db/auth/privilege.o build/opt/third_party/murmurhash3/MurmurHash3.o build/opt/mongo/idl/server_parameter_with_storage.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_erf.o build/opt/third_party/gperftools-2.7/dist/src/stack_trace_table.o build/opt/mongo/db/repl/bson_extract_optime.o build/opt/mongo/db/baton.o build/opt/mongo/db/operation_time_tracker.o build/opt/mongo/db/logical_time.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/error_private.o build/opt/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/zdict.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sqrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_bid.o build/opt/mongo/db/default_baton.o build/opt/mongo/db/unclean_shutdown.o build/opt/mongo/db/client.o build/opt/mongo/db/server_recovery.o build/opt/mongo/db/matcher/expression.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_sqrt.o build/opt/mongo/db/keypattern.o build/opt/third_party/gperftools-2.7/dist/src/static_vars.o build/opt/third_party/gperftools-2.7/dist/src/malloc_extension.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log10.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log1p.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_frexp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fdimd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_quantexpd.o build/opt/mongo/db/fts/unicode/codepoints_diacritic_map.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/path_traits.o build/opt/mongo/db/pipeline/resume_token.o build/opt/mongo/idl/idl_parser.o build/opt/third_party/boost-1.70.0/libs/program_options/src/value_semantic.o build/opt/third_party/boost-1.70.0/libs/program_options/src/parsers.o build/opt/third_party/boost-1.70.0/libs/program_options/src/variables_map.o build/opt/third_party/boost-1.70.0/libs/program_options/src/cmdline.o build/opt/third_party/boost-1.70.0/libs/program_options/src/split.o build/opt/third_party/boost-1.70.0/libs/program_options/src/utf8_codecvt_facet.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int8.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_logb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_llrintd.o build/opt/mongo/db/pipeline/document_path_support.o build/opt/mongo/executor/network_interface.o build/opt/mongo/util/net/ssl_manager.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_acos.o build/opt/mongo/executor/connection_pool.o build/opt/mongo/db/pipeline/value.o build/opt/third_party/timelib-2018.01/parse_date.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tan.o build/opt/mongo/db/wire_version.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp.o build/opt/third_party/gperftools-2.7/dist/src/span.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_noncomp.o build/opt/third_party/fmt/dist/src/posix.o build/opt/mongo/db/namespace_string.o build/opt/third_party/gperftools-2.7/dist/src/base/spinlock.o build/opt/mongo/shell/kms_aws.o build/opt/mongo/db/query/killcursors_request.o build/opt/mongo/util/safe_num.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_mod.o build/opt/mongo/db/matcher/path.o build/opt/third_party/gperftools-2.7/dist/src/central_freelist.o build/opt/third_party/s2/s2polygonbuilder.o build/opt/third_party/s2/s2.o build/opt/third_party/s2/s2edgeindex.o build/opt/third_party/s2/s2latlngrect.o build/opt/third_party/s2/s1angle.o build/opt/third_party/s2/s2cell.o build/opt/third_party/s2/s2regionunion.o build/opt/third_party/s2/s2latlng.o build/opt/third_party/s2/s1interval.o build/opt/third_party/s2/s2region.o build/opt/third_party/s2/s2pointregion.o build/opt/third_party/s2/s2regioncoverer.o build/opt/third_party/s2/s2loop.o build/opt/mongo/rpc/metadata/sharding_metadata.o build/opt/third_party/gperftools-2.7/dist/src/sampler.o build/opt/third_party/gperftools-2.7/dist/src/internal_logging.o build/opt/third_party/gperftools-2.7/dist/src/symbolize.o build/opt/third_party/gperftools-2.7/dist/src/malloc_hook.o build/opt/third_party/gperftools-2.7/dist/src/base/vdso_support.o build/opt/third_party/gperftools-2.7/dist/src/base/sysinfo.o build/opt/third_party/gperftools-2.7/dist/src/stacktrace.o build/opt/third_party/gperftools-2.7/dist/src/base/spinlock_internal.o build/opt/third_party/gperftools-2.7/dist/src/base/elf_mem_image.o build/opt/third_party/gperftools-2.7/dist/src/page_heap.o build/opt/third_party/gperftools-2.7/dist/src/system-alloc.o build/opt/third_party/gperftools-2.7/dist/src/thread_cache.o build/opt/third_party/gperftools-2.7/dist/src/base/logging.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log2.o build/opt/mongo/db/query/query_knobs_gen.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_compare.o build/opt/mongo/util/icu.o build/opt/mongo/db/write_concern_options.o build/opt/mongo/db/commands/test_commands_enabled.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sin.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int16.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_decimal_data.o build/opt/mongo/util/secure_compare_memory.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lround.o build/opt/mongo/db/server_options_general_gen.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_feclearexcept.o build/opt/third_party/boost-1.70.0/libs/system/src/error_code.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cbrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_logbd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_nexttowardd.o build/opt/third_party/fmt/dist/src/format.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_swedish.o build/opt/mongo/db/index/wildcard_key_generator.o build/opt/mongo/bson/mutable/document.o build/opt/mongo/db/keyfile_option_gen.o build/opt/mongo/db/matcher/matchable.o build/opt/mongo/util/regex_util.o build/opt/mongo/util/options_parser/options_parser.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atan.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_scalbl.o build/opt/mongo/db/index/expression_keys_private.o build/opt/mongo/db/index/btree_key_generator.o build/opt/mongo/db/index/sort_key_generator.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int32.o build/opt/mongo/rpc/metadata.o build/opt/mongo/rpc/legacy_reply_builder.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_binarydecimal.o build/opt/mongo/shell/mongo.o build/opt/mongo/db/storage/storage_parameters_gen.o build/opt/third_party/shim_timelib.o build/opt/mongo/db/field_parser.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_feraiseexcept.o build/opt/mongo/rpc/metadata/logical_time_metadata.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/pool.o build/opt/mongo/client/sasl_client_authenticate.o build/opt/mongo/client/mongo_uri.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_expm1.o build/opt/mongo/shell/encrypted_dbclient_base.o build/opt/third_party/shim_snappy.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_quantize.o build/opt/mongo/db/catalog/collection_catalog.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sub.o build/opt/mongo/db/server_options.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_inv_hyper.o build/opt/mongo/db/server_options_base_gen.o build/opt/mongo/crypto/symmetric_crypto_openssl.o build/opt/mongo/db/server_options_nongeneral_gen.o build/opt/mongo/db/matcher/expression_text_base.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cos.o build/opt/mongo/shell/shell_utils_launcher.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log10.o build/opt/mongo/db/auth/user_name.o build/opt/mongo/bson/util/bson_extract.o build/opt/mongo/db/matcher/schema/encrypt_schema_types.o build/opt/third_party/pcre-8.42/pcre_config.o build/opt/mongo/db/matcher/schema/json_schema_parser.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp2.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstdmt_compress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/fse_compress.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lround.o build/opt/third_party/shim_stemmer.o build/opt/mongo/db/storage/storage_options.o build/opt/third_party/pcre-8.42/pcre_newline.o build/opt/mongo/util/options_parser/value.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cos.o build/opt/mongo/db/matcher/expression_parser.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/fse_decompress.o build/opt/mongo/db/storage/write_unit_of_work.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atan2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_exp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int16.o build/opt/mongo/db/pipeline/exchange_spec_gen.o build/opt/mongo/shell/mk_wcwidth.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_hungarian.o build/opt/third_party/zlib-1.2.11/compress.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_modf.o build/opt/mongo/executor/connection_pool_stats.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int16.o build/opt/mongo/db/commands/server_status.o build/opt/mongo/util/processinfo_linux.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_quantexpd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log2.o build/opt/mongo/db/pipeline/field_path.o build/opt/mongo/db/matcher/expression_internal_expr_eq.o build/opt/mongo/db/matcher/schema/expression_internal_schema_fmod.o build/opt/mongo/db/matcher/schema/expression_internal_schema_unique_items.o build/opt/mongo/db/matcher/schema/json_pointer.o build/opt/mongo/db/matcher/extensions_callback.o build/opt/mongo/db/matcher/expression_algo.o build/opt/mongo/db/matcher/expression_geo.o build/opt/mongo/db/matcher/expression_where_noop.o build/opt/mongo/db/matcher/expression_with_placeholder.o build/opt/mongo/db/matcher/schema/expression_internal_schema_num_properties.o build/opt/mongo/db/matcher/expression_tree.o build/opt/mongo/db/matcher/schema/expression_internal_schema_cond.o build/opt/mongo/db/matcher/expression_leaf.o build/opt/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.o build/opt/mongo/db/matcher/schema/expression_internal_schema_eq.o build/opt/mongo/db/matcher/match_details.o build/opt/mongo/db/matcher/expression_expr.o build/opt/mongo/rpc/message.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lrintd.o build/opt/mongo/executor/task_executor.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_erf.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_acosh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cosh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_acos.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_llrintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_noncomp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_add.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_acos.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_cbrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fesetexceptflag.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_erf.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fdimd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_logbd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod128.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sinh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_next.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sinh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cosh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_erfc.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_scalb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fmod.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_add.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_asinh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tanh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_round_integral.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_scalbl.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_scalb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cbrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lrintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_div.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_add.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_erfc.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atanh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atan2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lrintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_hypot.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp10.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_rem.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_expm1.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_round_integral.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_four_over_pi.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log1p.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp10.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cos.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_nexttowardd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_mul.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atan2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_logbd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint8.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log1p.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_pow.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_int.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fdimd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_round_integral.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_asin.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops_64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_flag_operations.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_bid128.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cbrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint16.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint16.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fmod.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_minmax.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_hypot.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_inv_trig.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log10.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_frexp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_pow.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_logb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/sqrt_tab_t.o build/opt/mongo/db/pipeline/value_comparator.o build/opt/mongo/util/net/ssl_parameters.o build/opt/mongo/util/net/ssl_parameters_gen.o build/opt/mongo/util/processinfo.o build/opt/mongo/base/system_error.o build/opt/mongo/rpc/metadata/client_metadata.o build/opt/mongo/rpc/metadata/client_metadata_ismaster.o build/opt/mongo/transport/service_executor_adaptive.o build/opt/mongo/transport/service_executor_gen.o build/opt/mongo/db/auth/auth_decorations.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/entropy_common.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_lazy.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/threading.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_fast.o build/opt/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/divsufsort.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_opt.o build/opt/third_party/zstandard-1.3.7/zstd/lib/decompress/huf_decompress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_compress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_decompress.o build/opt/mongo/db/auth/authorization_manager.o build/opt/mongo/crypto/sha256_block.o build/opt/third_party/shim_mozjs.o build/opt/mongo/client/sasl_client_session.o build/opt/mongo/client/sasl_client_conversation.o build/opt/mongo/client/sasl_client_authenticate_impl.o build/opt/mongo/shell/shell_options_init.o build/opt/mongo/db/query/view_response_formatter.o build/opt/mongo/db/index/expression_params.o build/opt/mongo/db/index/s2_common.o build/opt/mongo/db/query/find_and_modify_request.o build/opt/mongo/rpc/factory.o build/opt/mongo/rpc/legacy_reply.o build/opt/mongo/rpc/legacy_request_builder.o build/opt/mongo/util/winutil.o build/opt/mongo/util/periodic_runner.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/utf8_codecvt_facet.o build/opt/third_party/timelib-2018.01/parse_zoneinfo.o build/opt/third_party/timelib-2018.01/tm2unixtime.o build/opt/third_party/timelib-2018.01/unixtime2tm.o build/opt/third_party/timelib-2018.01/astro.o build/opt/third_party/timelib-2018.01/parse_tz.o build/opt/third_party/timelib-2018.01/interval.o build/opt/mongo/rpc/metadata/oplog_query_metadata.o build/opt/mongo/rpc/metadata/egress_metadata_hook_list.o build/opt/mongo/s/is_mongos.o build/opt/mongo/transport/service_entry_point_utils.o build/opt/mongo/db/time_proof_service.o build/opt/mongo/db/pipeline/document_source_list_sessions_gen.o build/opt/mongo/util/debugger.o build/opt/mongo/db/repl/rollback_gen.o build/opt/third_party/asio-master/asio/src/asio.o build/opt/third_party/zlib-1.2.11/inffast.o build/opt/third_party/zlib-1.2.11/inftrees.o build/opt/third_party/zlib-1.2.11/deflate.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_dutch.o build/opt/third_party/pcre-8.42/pcre_get.o build/opt/third_party/pcre-8.42/pcre_tables.o build/opt/third_party/pcre-8.42/pcre_byte_order.o build/opt/third_party/pcre-8.42/pcre_exec.o build/opt/third_party/pcre-8.42/pcre_study.o build/opt/third_party/pcre-8.42/pcre_xclass.o build/opt/third_party/pcre-8.42/pcre_chartables.o build/opt/mongo/executor/connection_pool_tl.o build/opt/third_party/s2/strings/split.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_russian.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_german.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_finnish.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_french.o build/opt/mongo/db/logical_clock_gen.o build/opt/mongo/crypto/aead_encryption.o build/opt/mongo/db/pipeline/document.o build/opt/mongo/crypto/symmetric_key.o build/opt/third_party/s2/strings/strutil.o build/opt/mongo/db/query/count_request.o build/opt/mongo/client/connection_string.o build/opt/mongo/db/query/killcursors_response.o build/opt/mongo/db/commands/test_commands_enabled_gen.o build/opt/mongo/db/audit.o build/opt/mongo/shell/mongodbcr.o build/opt/mongo/util/options_parser/options_parser_init.o build/opt/mongo/client/dbclient_cursor.o build/opt/mongo/db/storage/duplicate_key_error_info.o build/opt/mongo/db/commands/server_status_metric.o build/opt/mongo/db/repl/storage_interface.o build/opt/mongo/db/geo/shapes.o build/opt/mongo/db/geo/big_polygon.o build/opt/mongo/util/concurrency/spin_lock.o build/opt/mongo/transport/session.o build/opt/mongo/db/matcher/path_internal.o build/opt/mongo/executor/egress_tag_closer_manager.o build/opt/mongo/db/pipeline/document_source_change_stream_gen.o build/opt/mongo/db/pipeline/document_source_replace_root_gen.o build/opt/mongo/crypto/symmetric_crypto.o ================================================ FILE: cmake/mongodb/macosx-debug.objects ================================================ build/debug/third_party/boost-1.70.0/libs/system/src/error_code.o build/debug/third_party/zlib-1.2.11/inffast.o build/debug/third_party/zlib-1.2.11/deflate.o build/debug/third_party/zlib-1.2.11/crc32.o build/debug/mongo/client/sasl_client_authenticate_impl.o build/debug/mongo/util/signal_handlers.o build/debug/mongo/client/sasl_client_authenticate.o build/debug/mongo/util/clock_source.o build/debug/mongo/db/query/hint_parser.o build/debug/mongo/util/fast_clock_source_factory.o build/debug/mongo/util/background_thread_clock_source.o build/debug/mongo/util/signal_win32.o build/debug/mongo/client/sasl_client_session.o build/debug/mongo/client/sasl_scram_client_conversation.o build/debug/mongo/db/dbmessage.o build/debug/mongo/client/sasl_plain_client_conversation.o build/debug/mongo/client/native_sasl_client_session.o build/debug/mongo/db/query/hint_gen.o build/debug/mongo/client/sasl_client_conversation.o build/debug/third_party/zlib-1.2.11/inftrees.o build/debug/third_party/zlib-1.2.11/infback.o build/debug/third_party/zlib-1.2.11/adler32.o build/debug/third_party/zlib-1.2.11/compress.o build/debug/third_party/zlib-1.2.11/trees.o build/debug/third_party/zlib-1.2.11/zutil.o build/debug/third_party/zlib-1.2.11/uncompr.o build/debug/third_party/zlib-1.2.11/inflate.o build/debug/mongo/client/mongo_uri.o build/debug/mongo/client/connection_string.o build/debug/mongo/util/icu.o build/debug/mongo/db/hasher.o build/debug/mongo/util/cmdline_utils/censor_cmdline.o build/debug/mongo/idl/idl_parser.o build/debug/mongo/rpc/protocol.o build/debug/mongo/rpc/op_msg.o build/debug/mongo/rpc/message.o build/debug/mongo/util/md5.o build/debug/mongo/util/password_digest.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_russian.o build/debug/third_party/libstemmer_c/runtime/api.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_finnish.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_porter.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_french.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_english.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_portuguese.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_swedish.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_hungarian.o build/debug/third_party/libstemmer_c/libstemmer/libstemmer_utf8.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_danish.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_turkish.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_german.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_romanian.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_dutch.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_italian.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_spanish.o build/debug/third_party/libstemmer_c/src_c/stem_UTF_8_norwegian.o build/debug/mongo/util/quick_exit.o build/debug/mongo/crypto/sha1_block.o build/debug/mongo/db/bson/dotted_path_support.o build/debug/mongo/db/repl/repl_settings_gen.o build/debug/mongo/db/auth/authorization_session.o build/debug/mongo/db/auth/role_name.o build/debug/mongo/db/auth/user_name.o build/debug/mongo/db/auth/auth_decorations.o build/debug/mongo/db/repl/repl_settings.o build/debug/mongo/db/auth/authorization_manager.o build/debug/mongo/db/query/collation/collator_factory_interface.o build/debug/mongo/client/replica_set_monitor_manager.o build/debug/mongo/client/replica_set_change_notifier.o build/debug/mongo/client/connpool.o build/debug/mongo/client/dbclient_rs.o build/debug/mongo/client/connection_string_connect.o build/debug/mongo/client/global_conn_pool_gen.o build/debug/mongo/client/replica_set_monitor.o build/debug/mongo/client/global_conn_pool.o build/debug/mongo/client/mongo_uri_connect.o build/debug/mongo/client/dbclient_connection.o build/debug/mongo/executor/connection_pool_tl.o build/debug/mongo/executor/network_interface_tl.o build/debug/mongo/db/index_names.o build/debug/mongo/scripting/jsexception.o build/debug/mongo/scripting/engine.o build/debug/mongo/scripting/dbdirectclient_factory.o build/debug/mongo/scripting/deadline_monitor_gen.o build/debug/mongo/scripting/utils.o build/debug/mongo/scripting/deadline_monitor.o build/debug/mongo/db/catalog/index_key_validate.o build/debug/mongo/db/pipeline/variables.o build/debug/mongo/rpc/object_check.o build/debug/mongo/db/pipeline/expression_context.o build/debug/mongo/rpc/legacy_reply_builder.o build/debug/mongo/rpc/object_check_gen.o build/debug/mongo/rpc/reply_builder_interface.o build/debug/mongo/rpc/legacy_reply.o build/debug/mongo/rpc/factory.o build/debug/mongo/rpc/legacy_request.o build/debug/mongo/rpc/legacy_request_builder.o build/debug/mongo/transport/transport_layer_asio.o build/debug/mongo/s/is_mongos.o build/debug/mongo/db/query/query_knobs_gen.o build/debug/third_party/asio-master/asio/src/asio.o build/debug/mongo/db/signed_logical_time.o build/debug/mongo/db/repl/optime.o build/debug/mongo/db/repl/bson_extract_optime.o build/debug/mongo/db/matcher/path.o build/debug/mongo/db/matcher/path_internal.o build/debug/third_party/snappy-1.1.7/snappy-sinksource.o build/debug/third_party/snappy-1.1.7/snappy-c.o build/debug/third_party/snappy-1.1.7/snappy.o build/debug/mongo/db/keys_collection_client_sharded.o build/debug/mongo/executor/task_executor.o build/debug/mongo/db/catalog/collection_catalog.o build/debug/mongo/bson/mutable/element.o build/debug/mongo/bson/mutable/document.o build/debug/mongo/db/commands/server_status.o build/debug/mongo/db/query/explain_options.o build/debug/mongo/db/fts/fts_util.o build/debug/mongo/db/fts/fts_spec.o build/debug/mongo/db/fts/stop_words.o build/debug/mongo/db/fts/fts_spec_legacy.o build/debug/mongo/db/fts/fts_unicode_phrase_matcher.o build/debug/mongo/db/fts/fts_element_iterator.o build/debug/mongo/db/fts/fts_unicode_tokenizer.o build/debug/mongo/db/fts/fts_matcher.o build/debug/mongo/db/fts/fts_query_parser.o build/debug/mongo/db/fts/stop_words_list.o build/debug/mongo/db/fts/fts_index_format.o build/debug/mongo/db/fts/stemmer.o build/debug/mongo/db/fts/fts_query_impl.o build/debug/mongo/db/fts/fts_language.o build/debug/mongo/db/fts/tokenizer.o build/debug/mongo/db/fts/fts_basic_tokenizer.o build/debug/mongo/db/fts/fts_basic_phrase_matcher.o build/debug/mongo/db/query/datetime/date_time_support.o build/debug/third_party/shim_zstd.o build/debug/third_party/shim_pcrecpp.o build/debug/third_party/icu4c-57.1/source/common/uresdata.o build/debug/mongo/db/logical_session_id_helpers.o build/debug/third_party/icu4c-57.1/source/common/uniset.o build/debug/third_party/icu4c-57.1/source/common/unistr_cnv.o build/debug/third_party/icu4c-57.1/source/common/ubidi.o build/debug/third_party/icu4c-57.1/source/common/rbbitblb.o build/debug/third_party/icu4c-57.1/source/common/ucnv_cb.o build/debug/third_party/icu4c-57.1/source/common/ucasemap.o build/debug/third_party/icu4c-57.1/source/common/uresbund.o build/debug/third_party/icu4c-57.1/source/common/messagepattern.o build/debug/third_party/icu4c-57.1/source/common/dictbe.o build/debug/third_party/icu4c-57.1/source/common/uvectr64.o build/debug/third_party/icu4c-57.1/source/common/cwchar.o build/debug/third_party/icu4c-57.1/source/common/ushape.o build/debug/third_party/icu4c-57.1/source/common/errorcode.o build/debug/third_party/icu4c-57.1/source/common/patternprops.o build/debug/third_party/icu4c-57.1/source/common/ucmndata.o build/debug/third_party/icu4c-57.1/source/common/ucnv_set.o build/debug/third_party/icu4c-57.1/source/common/servslkf.o build/debug/third_party/icu4c-57.1/source/common/ucln_cmn.o build/debug/third_party/icu4c-57.1/source/common/cstring.o build/debug/third_party/icu4c-57.1/source/common/filteredbrk.o build/debug/third_party/icu4c-57.1/source/common/uidna.o build/debug/third_party/icu4c-57.1/source/common/rbbirb.o build/debug/third_party/icu4c-57.1/source/common/ucasemap_titlecase_brkiter.o build/debug/third_party/icu4c-57.1/source/common/listformatter.o build/debug/third_party/icu4c-57.1/source/common/unormcmp.o build/debug/third_party/icu4c-57.1/source/common/unames.o build/debug/third_party/icu4c-57.1/source/common/uset.o build/debug/third_party/icu4c-57.1/source/common/brkiter.o build/debug/third_party/icu4c-57.1/source/common/ustr_wcs.o build/debug/third_party/icu4c-57.1/source/common/ucnvisci.o build/debug/third_party/icu4c-57.1/source/common/locresdata.o build/debug/third_party/icu4c-57.1/source/common/bytestrie.o build/debug/third_party/icu4c-57.1/source/common/ucharstriebuilder.o build/debug/third_party/icu4c-57.1/source/common/wintz.o build/debug/third_party/icu4c-57.1/source/common/unistr_case.o build/debug/third_party/icu4c-57.1/source/common/ucnv_cnv.o build/debug/third_party/icu4c-57.1/source/common/rbbinode.o build/debug/third_party/icu4c-57.1/source/common/ucnv_u16.o build/debug/third_party/icu4c-57.1/source/common/ucat.o build/debug/third_party/icu4c-57.1/source/common/putil.o build/debug/third_party/icu4c-57.1/source/common/utrace.o build/debug/third_party/icu4c-57.1/source/common/uiter.o build/debug/third_party/icu4c-57.1/source/common/caniter.o build/debug/third_party/icu4c-57.1/source/common/utrie2_builder.o build/debug/third_party/icu4c-57.1/source/common/stringpiece.o build/debug/third_party/icu4c-57.1/source/common/servls.o build/debug/third_party/icu4c-57.1/source/common/normlzr.o build/debug/third_party/icu4c-57.1/source/common/ustr_cnv.o build/debug/third_party/icu4c-57.1/source/common/ucnvhz.o build/debug/third_party/icu4c-57.1/source/common/usc_impl.o build/debug/third_party/icu4c-57.1/source/common/umapfile.o build/debug/third_party/icu4c-57.1/source/common/unistr_case_locale.o build/debug/third_party/icu4c-57.1/source/common/ucol_swp.o build/debug/third_party/icu4c-57.1/source/common/dictionarydata.o build/debug/third_party/icu4c-57.1/source/common/ucnv_bld.o build/debug/third_party/icu4c-57.1/source/common/loadednormalizer2impl.o build/debug/third_party/icu4c-57.1/source/common/ucnv_err.o build/debug/third_party/icu4c-57.1/source/common/locdspnm.o build/debug/third_party/icu4c-57.1/source/common/locdispnames.o build/debug/third_party/icu4c-57.1/source/common/uloc_tag.o build/debug/third_party/icu4c-57.1/source/common/uchriter.o build/debug/third_party/icu4c-57.1/source/common/stringtriebuilder.o build/debug/third_party/icu4c-57.1/source/common/uscript_props.o build/debug/third_party/icu4c-57.1/source/common/parsepos.o build/debug/third_party/icu4c-57.1/source/common/dtintrv.o build/debug/third_party/icu4c-57.1/source/common/uchar.o build/debug/third_party/icu4c-57.1/source/common/rbbidata.o build/debug/third_party/icu4c-57.1/source/common/bytestriebuilder.o build/debug/third_party/icu4c-57.1/source/common/ucnv_ct.o build/debug/third_party/icu4c-57.1/source/common/ubidiwrt.o build/debug/third_party/icu4c-57.1/source/common/uloc.o build/debug/third_party/icu4c-57.1/source/common/bytestrieiterator.o build/debug/third_party/icu4c-57.1/source/common/ucnv_ext.o build/debug/third_party/icu4c-57.1/source/common/ucnv_lmb.o build/debug/third_party/icu4c-57.1/source/common/ustrfmt.o build/debug/third_party/icu4c-57.1/source/common/utf_impl.o build/debug/third_party/icu4c-57.1/source/common/cmemory.o build/debug/third_party/icu4c-57.1/source/common/uniset_props.o build/debug/third_party/icu4c-57.1/source/common/appendable.o build/debug/third_party/icu4c-57.1/source/common/ustrenum.o build/debug/third_party/icu4c-57.1/source/common/unifilt.o build/debug/third_party/icu4c-57.1/source/common/umath.o build/debug/third_party/icu4c-57.1/source/common/loclikely.o build/debug/third_party/icu4c-57.1/source/common/cstr.o build/debug/third_party/icu4c-57.1/source/common/udataswp.o build/debug/third_party/icu4c-57.1/source/common/normalizer2impl.o build/debug/third_party/icu4c-57.1/source/common/bytestream.o build/debug/third_party/icu4c-57.1/source/common/ucnv_io.o build/debug/third_party/icu4c-57.1/source/common/ubidi_props.o build/debug/third_party/icu4c-57.1/source/common/util.o build/debug/third_party/icu4c-57.1/source/common/locmap.o build/debug/third_party/icu4c-57.1/source/common/simpleformatter.o build/debug/third_party/icu4c-57.1/source/common/charstr.o build/debug/third_party/icu4c-57.1/source/common/locavailable.o build/debug/third_party/icu4c-57.1/source/common/utrie.o build/debug/third_party/icu4c-57.1/source/common/ucnvsel.o build/debug/third_party/icu4c-57.1/source/common/utypes.o build/debug/third_party/icu4c-57.1/source/common/ubrk.o build/debug/third_party/icu4c-57.1/source/common/uarrsort.o build/debug/third_party/icu4c-57.1/source/common/rbbistbl.o build/debug/third_party/icu4c-57.1/source/common/ucase.o build/debug/third_party/icu4c-57.1/source/common/serv.o build/debug/third_party/icu4c-57.1/source/common/usprep.o build/debug/third_party/icu4c-57.1/source/common/chariter.o build/debug/third_party/icu4c-57.1/source/common/unifunct.o build/debug/third_party/icu4c-57.1/source/common/ustrcase.o build/debug/third_party/icu4c-57.1/source/common/uprops.o build/debug/third_party/icu4c-57.1/source/common/uset_props.o build/debug/third_party/icu4c-57.1/source/common/usetiter.o build/debug/third_party/icu4c-57.1/source/common/schriter.o build/debug/third_party/icu4c-57.1/source/common/brkeng.o build/debug/third_party/icu4c-57.1/source/common/ruleiter.o build/debug/third_party/icu4c-57.1/source/common/udatamem.o build/debug/third_party/icu4c-57.1/source/common/umutex.o build/debug/third_party/icu4c-57.1/source/common/unisetspan.o build/debug/third_party/icu4c-57.1/source/common/icuplug.o build/debug/third_party/icu4c-57.1/source/common/ucnv2022.o build/debug/third_party/icu4c-57.1/source/common/locutil.o build/debug/third_party/icu4c-57.1/source/common/ustrtrns.o build/debug/third_party/icu4c-57.1/source/common/rbbi.o build/debug/third_party/icu4c-57.1/source/common/servlk.o build/debug/third_party/icu4c-57.1/source/common/ulist.o build/debug/third_party/icu4c-57.1/source/common/uhash_us.o build/debug/third_party/icu4c-57.1/source/common/propname.o build/debug/third_party/icu4c-57.1/source/common/servnotf.o build/debug/third_party/icu4c-57.1/source/common/ucnvmbcs.o build/debug/third_party/icu4c-57.1/source/common/uvector.o build/debug/third_party/icu4c-57.1/source/common/servlkf.o build/debug/third_party/icu4c-57.1/source/common/icudataver.o build/debug/third_party/icu4c-57.1/source/common/unistr_props.o build/debug/third_party/icu4c-57.1/source/common/uloc_keytype.o build/debug/third_party/icu4c-57.1/source/common/ucnv_u7.o build/debug/third_party/icu4c-57.1/source/common/ucnvbocu.o build/debug/third_party/icu4c-57.1/source/common/uinvchar.o build/debug/third_party/icu4c-57.1/source/common/ustring.o build/debug/third_party/icu4c-57.1/source/common/ustrcase_locale.o build/debug/third_party/icu4c-57.1/source/common/ustack.o build/debug/third_party/icu4c-57.1/source/common/ucnvscsu.o build/debug/third_party/icu4c-57.1/source/common/pluralmap.o build/debug/third_party/icu4c-57.1/source/common/uts46.o build/debug/third_party/icu4c-57.1/source/common/ucnv.o build/debug/third_party/icu4c-57.1/source/common/ubidiln.o build/debug/third_party/icu4c-57.1/source/common/filterednormalizer2.o build/debug/third_party/icu4c-57.1/source/common/bmpset.o build/debug/third_party/icu4c-57.1/source/common/ulistformatter.o build/debug/third_party/icu4c-57.1/source/common/ucnvlat1.o build/debug/third_party/icu4c-57.1/source/common/rbbiscan.o build/debug/third_party/icu4c-57.1/source/common/punycode.o build/debug/third_party/icu4c-57.1/source/common/propsvec.o build/debug/third_party/icu4c-57.1/source/common/utext.o build/debug/third_party/icu4c-57.1/source/common/udata.o build/debug/third_party/icu4c-57.1/source/common/ucurr.o build/debug/third_party/icu4c-57.1/source/common/unistr_titlecase_brkiter.o build/debug/third_party/icu4c-57.1/source/common/ucnvdisp.o build/debug/third_party/icu4c-57.1/source/common/unistr.o build/debug/third_party/icu4c-57.1/source/common/locid.o build/debug/third_party/icu4c-57.1/source/common/resbund.o build/debug/third_party/icu4c-57.1/source/common/uinit.o build/debug/third_party/icu4c-57.1/source/common/utrie2.o build/debug/third_party/icu4c-57.1/source/common/uniset_closure.o build/debug/third_party/icu4c-57.1/source/common/ustr_titlecase_brkiter.o build/debug/third_party/icu4c-57.1/source/common/uenum.o build/debug/third_party/icu4c-57.1/source/common/rbbisetb.o build/debug/third_party/icu4c-57.1/source/common/util_props.o build/debug/third_party/icu4c-57.1/source/common/resbund_cnv.o build/debug/third_party/icu4c-57.1/source/common/sharedobject.o build/debug/third_party/icu4c-57.1/source/common/ucnv_u32.o build/debug/third_party/icu4c-57.1/source/common/uvectr32.o build/debug/third_party/icu4c-57.1/source/common/normalizer2.o build/debug/third_party/icu4c-57.1/source/common/ucharstrie.o build/debug/third_party/icu4c-57.1/source/common/unifiedcache.o build/debug/third_party/icu4c-57.1/source/common/locbased.o build/debug/third_party/icu4c-57.1/source/common/resource.o build/debug/third_party/icu4c-57.1/source/common/ures_cnv.o build/debug/third_party/icu4c-57.1/source/common/ucharstrieiterator.o build/debug/third_party/icu4c-57.1/source/common/unorm.o build/debug/third_party/icu4c-57.1/source/common/uscript.o build/debug/third_party/icu4c-57.1/source/common/uobject.o build/debug/third_party/icu4c-57.1/source/common/ucnv_u8.o build/debug/third_party/icu4c-57.1/source/common/servrbf.o build/debug/third_party/icu4c-57.1/source/common/uhash.o build/debug/mongo/db/concurrency/flow_control_ticketholder.o build/debug/third_party/s2/strings/split.o build/debug/third_party/s2/strings/stringprintf.o build/debug/third_party/s2/strings/strutil.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/codecvt_error_category.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/operations.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/path.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/utf8_codecvt_facet.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/path_traits.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/portability.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/windows_file_codecvt.o build/debug/third_party/boost-1.70.0/libs/filesystem/src/unique_path.o build/debug/mongo/util/version_impl.o build/debug/mongo/crypto/sha256_block.o build/debug/mongo/db/pipeline/document_path_support.o build/debug/mongo/db/pipeline/value.o build/debug/mongo/db/pipeline/document_comparator.o build/debug/mongo/db/pipeline/value_comparator.o build/debug/mongo/db/pipeline/document.o build/debug/mongo/shell/shell_options_gen.o build/debug/third_party/abseil-cpp-master/abseil-cpp/absl/hash/internal/hash.o build/debug/third_party/abseil-cpp-master/abseil-cpp/absl/hash/internal/city.o build/debug/mongo/db/storage/write_unit_of_work.o build/debug/mongo/client/index_spec.o build/debug/mongo/client/dbclient_cursor.o build/debug/mongo/client/dbclient_base.o build/debug/mongo/db/pipeline/runtime_constants_gen.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_tanh.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src15.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src5.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src26.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src17.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src30.o build/debug/third_party/mozjs-60/extract/js/src/perf/pm_stub.o build/debug/third_party/mozjs-60/extract/mfbt/Compression.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src40.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_asin.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src25.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src1.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_rint.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src32.o build/debug/third_party/mozjs-60/extract/mozglue/misc/ConditionVariable_posix.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src28.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src16.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src45.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_expm1.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_exp.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src35.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src37.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_asinh.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_scalbn.o build/debug/third_party/mozjs-60/extract/mozglue/misc/TimeStamp.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_truncf.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_log1p.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/k_exp.o build/debug/third_party/mozjs-60/extract/js/src/vm/JSAtom.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src29.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src38.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_trunc.o build/debug/third_party/mozjs-60/extract/mfbt/double-conversion/double-conversion/strtod.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src41.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src43.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_copysign.o build/debug/third_party/mozjs-60/extract/js/src/util/DoubleToString.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src24.o build/debug/third_party/mozjs-60/extract/mfbt/lz4.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src10.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src6.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src0.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_fabs.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src20.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_atan.o build/debug/third_party/mozjs-60/extract/mozglue/misc/StackWalk.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src9.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src31.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src12.o build/debug/third_party/mozjs-60/extract/js/src/jsarray.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_ceil.o build/debug/third_party/mozjs-60/extract/mozglue/misc/Mutex_posix.o build/debug/third_party/mozjs-60/extract/js/src/vm/Interpreter.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src34.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src39.o build/debug/third_party/mozjs-60/extract/js/src/builtin/RegExp.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src3.o build/debug/third_party/mozjs-60/extract/js/src/jsmath.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_cosh.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_log2.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_log10.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src13.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_log.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_hypot.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_atanh.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_pow.o build/debug/third_party/mozjs-60/extract/js/src/gc/StoreBuffer.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src22.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_acos.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_ceilf.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src4.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src8.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src42.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src7.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src23.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src36.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src11.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_acosh.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_nearbyint.o build/debug/third_party/mozjs-60/mongo_sources/mongoErrorReportToString.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_cbrt.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_sqrt.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src27.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src33.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_floor.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src44.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src21.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_rintf.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/s_floorf.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src18.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src19.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src2.o build/debug/third_party/mozjs-60/extract/js/src/jit/x86-shared/Disassembler-x86-shared.o build/debug/third_party/mozjs-60/extract/js/src/mfbt/Unified_cpp_mfbt0.o build/debug/third_party/mozjs-60/mongo_sources/freeOpToJSContext.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_sinh.o build/debug/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src14.o build/debug/third_party/mozjs-60/extract/js/src/frontend/Parser.o build/debug/third_party/mozjs-60/extract/modules/fdlibm/e_atan2.o build/debug/third_party/mozjs-60/extract/mozglue/misc/Printf.o build/debug/third_party/mozjs-60/extract/mozglue/misc/TimeStamp_posix.o build/debug/mongo/db/repl/read_concern_args.o build/debug/mongo/util/fail_point_service.o build/debug/mongo/util/fail_point_registry.o build/debug/mongo/util/fail_point_server_parameter_gen.o build/debug/mongo/util/fail_point.o build/debug/third_party/boost-1.70.0/libs/iostreams/src/file_descriptor.o build/debug/third_party/boost-1.70.0/libs/iostreams/src/mapped_file.o build/debug/mongo/util/background.o build/debug/mongo/bson/util/bson_extract.o build/debug/mongo/db/commands/test_commands_enabled.o build/debug/mongo/db/commands/test_commands_enabled_gen.o build/debug/mongo/util/net/ssl_options_client.o build/debug/mongo/util/net/ssl_options_client_gen.o build/debug/mongo/db/server_options_base_gen.o build/debug/mongo/db/server_options_base.o build/debug/mongo/db/keyfile_option_gen.o build/debug/mongo/db/server_options_general_gen.o build/debug/mongo/db/cluster_auth_mode_option_gen.o build/debug/mongo/db/server_options_nongeneral_gen.o build/debug/third_party/shim_stemmer.o build/debug/mongo/executor/connection_pool.o build/debug/mongo/db/operation_context.o build/debug/mongo/db/repl_set_member_in_standalone_mode.o build/debug/mongo/db/unclean_shutdown.o build/debug/mongo/db/server_recovery.o build/debug/mongo/db/service_context.o build/debug/mongo/db/default_baton.o build/debug/mongo/db/client.o build/debug/mongo/db/operation_context_group.o build/debug/mongo/db/baton.o build/debug/mongo/util/winutil.o build/debug/mongo/db/server_options.o build/debug/mongo/shell/kms_shell.o build/debug/mongo/executor/network_interface_thread_pool.o build/debug/mongo/db/index/expression_params.o build/debug/mongo/db/index/s2_common.o build/debug/mongo/s/request_types/split_chunk_request_type.o build/debug/mongo/s/request_types/clone_catalog_data_gen.o build/debug/mongo/s/chunk_version_gen.o build/debug/mongo/s/request_types/migration_secondary_throttle_options.o build/debug/mongo/s/shard_id.o build/debug/mongo/s/catalog/type_lockpings.o build/debug/mongo/s/cannot_implicitly_create_collection_info.o build/debug/mongo/s/request_types/merge_chunk_request_type.o build/debug/mongo/s/catalog/type_shard.o build/debug/mongo/s/request_types/shard_collection_gen.o build/debug/mongo/s/request_types/flush_routing_table_cache_updates_gen.o build/debug/mongo/s/stale_exception.o build/debug/mongo/s/catalog/type_locks.o build/debug/mongo/s/request_types/wait_for_fail_point_gen.o build/debug/mongo/s/database_version_gen.o build/debug/mongo/s/catalog/type_changelog.o build/debug/mongo/s/catalog/type_tags.o build/debug/mongo/s/request_types/flush_database_cache_updates_gen.o build/debug/mongo/s/request_types/create_database_gen.o build/debug/mongo/s/request_types/add_shard_to_zone_request_type.o build/debug/mongo/s/catalog/type_mongos.o build/debug/mongo/s/catalog/type_database.o build/debug/mongo/s/request_types/move_chunk_request.o build/debug/mongo/s/catalog/type_shard_collection.o build/debug/mongo/s/request_types/create_collection_gen.o build/debug/mongo/s/catalog/type_collection.o build/debug/mongo/s/catalog/type_chunk.o build/debug/mongo/s/request_types/update_zone_key_range_request_type.o build/debug/mongo/s/request_types/add_shard_request_type.o build/debug/mongo/s/request_types/commit_chunk_migration_request_type.o build/debug/mongo/s/request_types/move_primary_gen.o build/debug/mongo/s/catalog/type_config_version.o build/debug/mongo/s/request_types/remove_shard_from_zone_request_type.o build/debug/mongo/s/request_types/balance_chunk_request_type.o build/debug/mongo/s/catalog/type_chunk_base_gen.o build/debug/mongo/s/chunk_version.o build/debug/mongo/s/would_change_owning_shard_exception.o build/debug/mongo/s/request_types/clear_jumbo_flag_gen.o build/debug/mongo/s/catalog/mongo_version_range.o build/debug/mongo/s/database_version_helpers.o build/debug/mongo/s/catalog/type_shard_database.o build/debug/mongo/s/request_types/set_shard_version_request.o build/debug/mongo/s/request_types/get_database_version_gen.o build/debug/mongo/s/request_types/clone_collection_options_from_primary_shard_gen.o build/debug/mongo/crypto/symmetric_key.o build/debug/mongo/crypto/symmetric_crypto.o build/debug/mongo/transport/message_compressor_options_client_gen.o build/debug/mongo/crypto/symmetric_crypto_apple.o build/debug/mongo/db/server_options_server_helpers.o build/debug/mongo/base/data_type.o build/debug/mongo/bson/bsonobjbuilder.o build/debug/mongo/bson/simple_bsonobj_comparator.o build/debug/mongo/platform/decimal128.o build/debug/mongo/util/uuid.o build/debug/mongo/util/assert_util.o build/debug/mongo/base/init.o build/debug/mongo/base/validate_locale.o build/debug/mongo/logger/log_severity.o build/debug/mongo/bson/bson_comparator_interface_base.o build/debug/mongo/util/str.o build/debug/mongo/bson/bsonmisc.o build/debug/mongo/util/exception_filter_win32.o build/debug/mongo/base/global_initializer_registerer.o build/debug/mongo/base/error_codes.o build/debug/mongo/bson/bson_validate.o build/debug/mongo/logger/log_component_settings.o build/debug/mongo/logger/logger.o build/debug/mongo/util/hex.o build/debug/mongo/base/string_data.o build/debug/mongo/base/status.o build/debug/mongo/platform/strcasestr.o build/debug/mongo/bson/bsonobj.o build/debug/mongo/util/stacktrace_posix.o build/debug/mongo/util/allocator.o build/debug/mongo/platform/shared_library.o build/debug/mongo/base/make_string_vector.o build/debug/mongo/util/log.o build/debug/mongo/logger/console.o build/debug/mongo/base/shim.o build/debug/mongo/util/boost_assert_impl.o build/debug/mongo/bson/json.o build/debug/mongo/util/itoa.o build/debug/mongo/util/stacktrace.o build/debug/mongo/logger/message_log_domain.o build/debug/mongo/util/duration.o build/debug/mongo/base/simple_string_data_comparator.o build/debug/mongo/platform/stack_locator.o build/debug/mongo/bson/simple_bsonelement_comparator.o build/debug/mongo/logger/rotatable_file_writer.o build/debug/mongo/logger/redaction.o build/debug/mongo/util/file.o build/debug/mongo/logger/ramlog.o build/debug/mongo/bson/bsontypes.o build/debug/mongo/base/data_type_string_data.o build/debug/mongo/util/time_support.o build/debug/mongo/util/system_tick_source.o build/debug/mongo/base/data_range_cursor.o build/debug/mongo/platform/stack_locator_macOS.o build/debug/mongo/util/errno_util.o build/debug/mongo/platform/posix_fadvise.o build/debug/mongo/logger/logstream_builder.o build/debug/mongo/base/initializer_dependency_graph.o build/debug/mongo/base/transaction_error.o build/debug/mongo/platform/random.o build/debug/mongo/base/data_range.o build/debug/mongo/util/startup_test.o build/debug/mongo/logger/log_manager.o build/debug/mongo/platform/strnlen.o build/debug/mongo/util/timer.o build/debug/mongo/bson/bsonelement.o build/debug/mongo/base/global_initializer.o build/debug/mongo/bson/timestamp.o build/debug/mongo/platform/shared_library_posix.o build/debug/mongo/util/text.o build/debug/mongo/util/version.o build/debug/mongo/util/signal_handlers_synchronous.o build/debug/mongo/logger/message_event_utf8_encoder.o build/debug/mongo/base/data_type_terminated.o build/debug/mongo/base/error_extra_info.o build/debug/mongo/util/exit.o build/debug/mongo/util/shell_exec.o build/debug/mongo/base/parse_number.o build/debug/mongo/util/concurrency/idle_thread_block.o build/debug/mongo/util/base64.o build/debug/mongo/logger/component_message_log_domain.o build/debug/mongo/platform/process_id.o build/debug/mongo/logger/rotatable_file_manager.o build/debug/mongo/bson/oid.o build/debug/mongo/util/concurrency/thread_name.o build/debug/mongo/logger/log_component.o build/debug/mongo/util/platform_init.o build/debug/mongo/bson/bson_depth.o build/debug/mongo/util/system_clock_source.o build/debug/mongo/base/initializer.o build/debug/mongo/platform/mutex.o build/debug/mongo/db/pipeline/expression_trigonometric.o build/debug/mongo/db/pipeline/expression.o build/debug/mongo/db/traffic_reader.o build/debug/mongo/shell/shell_options_init.o build/debug/mongo/executor/connection_pool_stats.o build/debug/mongo/rpc/metadata/impersonated_user_metadata.o build/debug/mongo/db/storage/storage_parameters_gen.o build/debug/mongo/rpc/metadata/impersonated_user_metadata_gen.o build/debug/mongo/db/storage/storage_options.o build/debug/mongo/util/dns_query.o build/debug/mongo/db/catalog/index_catalog.o build/debug/third_party/boost-1.70.0/libs/program_options/src/split.o build/debug/third_party/boost-1.70.0/libs/program_options/src/cmdline.o build/debug/third_party/boost-1.70.0/libs/program_options/src/positional_options.o build/debug/third_party/boost-1.70.0/libs/program_options/src/value_semantic.o build/debug/third_party/boost-1.70.0/libs/program_options/src/parsers.o build/debug/third_party/boost-1.70.0/libs/program_options/src/winmain.o build/debug/third_party/boost-1.70.0/libs/program_options/src/config_file.o build/debug/third_party/boost-1.70.0/libs/program_options/src/utf8_codecvt_facet.o build/debug/third_party/boost-1.70.0/libs/program_options/src/variables_map.o build/debug/third_party/boost-1.70.0/libs/program_options/src/options_description.o build/debug/third_party/boost-1.70.0/libs/program_options/src/convert.o build/debug/mongo/shell/shell_options_storage.o build/debug/third_party/shim_icu.o build/debug/mongo/db/geo/geoparser.o build/debug/mongo/db/geo/geometry_container.o build/debug/mongo/db/index/index_descriptor.o build/debug/mongo/util/options_parser/startup_option_init.o build/debug/mongo/util/options_parser/option_section.o build/debug/mongo/util/options_parser/constraints.o build/debug/mongo/util/options_parser/environment.o build/debug/mongo/util/options_parser/startup_options.o build/debug/mongo/util/options_parser/value.o build/debug/mongo/util/options_parser/option_description.o build/debug/mongo/util/options_parser/options_parser.o build/debug/third_party/yaml-cpp-0.6.2/src/nodeevents.o build/debug/third_party/yaml-cpp-0.6.2/src/exceptions.o build/debug/third_party/yaml-cpp-0.6.2/src/scanscalar.o build/debug/third_party/yaml-cpp-0.6.2/src/node.o build/debug/third_party/yaml-cpp-0.6.2/src/scantag.o build/debug/third_party/yaml-cpp-0.6.2/src/scantoken.o build/debug/third_party/yaml-cpp-0.6.2/src/emitfromevents.o build/debug/third_party/yaml-cpp-0.6.2/src/emitterstate.o build/debug/third_party/yaml-cpp-0.6.2/src/exp.o build/debug/third_party/yaml-cpp-0.6.2/src/emit.o build/debug/third_party/yaml-cpp-0.6.2/src/simplekey.o build/debug/third_party/yaml-cpp-0.6.2/src/regex_yaml.o build/debug/third_party/yaml-cpp-0.6.2/src/binary.o build/debug/third_party/yaml-cpp-0.6.2/src/emitter.o build/debug/third_party/yaml-cpp-0.6.2/src/contrib/graphbuilderadapter.o build/debug/third_party/yaml-cpp-0.6.2/src/stream.o build/debug/third_party/yaml-cpp-0.6.2/src/node_data.o build/debug/third_party/yaml-cpp-0.6.2/src/parser.o build/debug/third_party/yaml-cpp-0.6.2/src/scanner.o build/debug/third_party/yaml-cpp-0.6.2/src/nodebuilder.o build/debug/third_party/yaml-cpp-0.6.2/src/parse.o build/debug/third_party/yaml-cpp-0.6.2/src/memory.o build/debug/third_party/yaml-cpp-0.6.2/src/null.o build/debug/third_party/yaml-cpp-0.6.2/src/ostream_wrapper.o build/debug/third_party/yaml-cpp-0.6.2/src/singledocparser.o build/debug/third_party/yaml-cpp-0.6.2/src/tag.o build/debug/third_party/yaml-cpp-0.6.2/src/contrib/graphbuilder.o build/debug/third_party/yaml-cpp-0.6.2/src/directives.o build/debug/third_party/yaml-cpp-0.6.2/src/convert.o build/debug/third_party/yaml-cpp-0.6.2/src/emitterutils.o build/debug/mongo/db/log_process_details.o build/debug/mongo/db/repl/replication_consistency_markers.o build/debug/mongo/executor/remote_command_request.o build/debug/mongo/executor/remote_command_response.o build/debug/mongo/db/repl/replication_process.o build/debug/mongo/db/auth/restriction_environment.o build/debug/third_party/wiredtiger/src/checksum/x86/crc32-x86-alt.o build/debug/third_party/wiredtiger/src/checksum/x86/crc32-x86.o build/debug/third_party/wiredtiger/src/checksum/software/checksum.o build/debug/mongo/db/wire_version.o build/debug/mongo/db/commands/server_status_internal.o build/debug/mongo/db/commands/server_status_metric.o build/debug/third_party/pcre-8.42/pcre_newline.o build/debug/third_party/pcre-8.42/pcre_scanner.o build/debug/third_party/pcre-8.42/pcre_tables.o build/debug/third_party/pcre-8.42/pcre_valid_utf8.o build/debug/third_party/pcre-8.42/pcre_stringpiece.o build/debug/third_party/pcre-8.42/pcre_string_utils.o build/debug/third_party/pcre-8.42/pcre_ucd.o build/debug/third_party/pcre-8.42/pcre_exec.o build/debug/third_party/pcre-8.42/pcre_ord2utf8.o build/debug/third_party/pcre-8.42/pcre_version.o build/debug/third_party/pcre-8.42/pcre_compile.o build/debug/third_party/pcre-8.42/pcre_get.o build/debug/third_party/pcre-8.42/pcre_byte_order.o build/debug/third_party/pcre-8.42/pcre_study.o build/debug/third_party/pcre-8.42/pcre_fullinfo.o build/debug/third_party/pcre-8.42/pcre_refcount.o build/debug/third_party/pcre-8.42/pcre_xclass.o build/debug/third_party/pcre-8.42/pcre_globals.o build/debug/third_party/pcre-8.42/pcre_maketables.o build/debug/third_party/pcre-8.42/pcre_dfa_exec.o build/debug/third_party/pcre-8.42/pcre_chartables.o build/debug/third_party/pcre-8.42/pcre_config.o build/debug/third_party/pcre-8.42/pcrecpp.o build/debug/third_party/shim_timelib.o build/debug/mongo/db/server_options_helpers.o build/debug/mongo/db/server_options_helpers_gen.o build/debug/mongo/db/audit.o build/debug/mongo/db/write_concern_options.o build/debug/mongo/rpc/metadata/logical_time_metadata.o build/debug/mongo/shell/encrypted_dbclient_base.o build/debug/mongo/shell/fle_shell_options_gen.o build/debug/mongo/rpc/metadata/tracking_metadata.o build/debug/mongo/rpc/metadata/config_server_metadata.o build/debug/mongo/rpc/metadata.o build/debug/mongo/rpc/metadata/repl_set_metadata.o build/debug/mongo/rpc/metadata/egress_metadata_hook_list.o build/debug/mongo/rpc/metadata/sharding_metadata.o build/debug/mongo/rpc/metadata/oplog_query_metadata.o build/debug/mongo/db/field_ref.o build/debug/mongo/db/field_parser.o build/debug/mongo/db/field_ref_set.o build/debug/mongo/db/keypattern.o build/debug/third_party/icu4c-57.1/source/i18n/smallintformatter.o build/debug/third_party/icu4c-57.1/source/i18n/remtrans.o build/debug/third_party/icu4c-57.1/source/i18n/dayperiodrules.o build/debug/third_party/icu4c-57.1/source/i18n/standardplural.o build/debug/third_party/icu4c-57.1/source/i18n/windtfmt.o build/debug/third_party/icu4c-57.1/source/i18n/selfmt.o build/debug/third_party/icu4c-57.1/source/i18n/casetrn.o build/debug/third_party/icu4c-57.1/source/i18n/strrepl.o build/debug/third_party/icu4c-57.1/source/i18n/vzone.o build/debug/third_party/icu4c-57.1/source/i18n/ztrans.o build/debug/third_party/icu4c-57.1/source/i18n/calendar.o build/debug/third_party/icu4c-57.1/source/i18n/tzfmt.o build/debug/third_party/icu4c-57.1/source/i18n/ethpccal.o build/debug/third_party/icu4c-57.1/source/i18n/utf16collationiterator.o build/debug/third_party/icu4c-57.1/source/i18n/rbtz.o build/debug/third_party/icu4c-57.1/source/i18n/translit.o build/debug/third_party/icu4c-57.1/source/i18n/compactdecimalformat.o build/debug/third_party/icu4c-57.1/source/i18n/rulebasedcollator.o build/debug/third_party/icu4c-57.1/source/i18n/uregion.o build/debug/third_party/icu4c-57.1/source/i18n/digitinterval.o build/debug/third_party/icu4c-57.1/source/i18n/collationdatareader.o build/debug/third_party/icu4c-57.1/source/i18n/anytrans.o build/debug/third_party/icu4c-57.1/source/i18n/tznames.o build/debug/third_party/icu4c-57.1/source/i18n/rematch.o build/debug/third_party/icu4c-57.1/source/i18n/simpletz.o build/debug/third_party/icu4c-57.1/source/i18n/ucln_in.o build/debug/third_party/icu4c-57.1/source/i18n/uspoof_impl.o build/debug/third_party/icu4c-57.1/source/i18n/vtzone.o build/debug/third_party/icu4c-57.1/source/i18n/indiancal.o build/debug/third_party/icu4c-57.1/source/i18n/nultrans.o build/debug/third_party/icu4c-57.1/source/i18n/regextxt.o build/debug/third_party/icu4c-57.1/source/i18n/smpdtfst.o build/debug/third_party/icu4c-57.1/source/i18n/collationbuilder.o build/debug/third_party/icu4c-57.1/source/i18n/rbt.o build/debug/third_party/icu4c-57.1/source/i18n/smpdtfmt.o build/debug/third_party/icu4c-57.1/source/i18n/nfrule.o build/debug/third_party/icu4c-57.1/source/i18n/decContext.o build/debug/third_party/icu4c-57.1/source/i18n/udat.o build/debug/third_party/icu4c-57.1/source/i18n/alphaindex.o build/debug/third_party/icu4c-57.1/source/i18n/dtfmtsym.o build/debug/third_party/icu4c-57.1/source/i18n/usearch.o build/debug/third_party/icu4c-57.1/source/i18n/digitformatter.o build/debug/third_party/icu4c-57.1/source/i18n/uspoof_conf.o build/debug/third_party/icu4c-57.1/source/i18n/nortrans.o build/debug/third_party/icu4c-57.1/source/i18n/nfrs.o build/debug/third_party/icu4c-57.1/source/i18n/digitgrouping.o build/debug/third_party/icu4c-57.1/source/i18n/rbt_set.o build/debug/third_party/icu4c-57.1/source/i18n/plurrule.o build/debug/third_party/icu4c-57.1/source/i18n/fmtable.o build/debug/third_party/icu4c-57.1/source/i18n/identifier_info.o build/debug/third_party/icu4c-57.1/source/i18n/pluralaffix.o build/debug/third_party/icu4c-57.1/source/i18n/collationsettings.o build/debug/third_party/icu4c-57.1/source/i18n/uspoof_build.o build/debug/third_party/icu4c-57.1/source/i18n/valueformatter.o build/debug/third_party/icu4c-57.1/source/i18n/measure.o build/debug/third_party/icu4c-57.1/source/i18n/winnmfmt.o build/debug/third_party/icu4c-57.1/source/i18n/wintzimpl.o build/debug/third_party/icu4c-57.1/source/i18n/dangical.o build/debug/third_party/icu4c-57.1/source/i18n/bocsu.o build/debug/third_party/icu4c-57.1/source/i18n/udateintervalformat.o build/debug/third_party/icu4c-57.1/source/i18n/ucoleitr.o build/debug/third_party/icu4c-57.1/source/i18n/dtitvfmt.o build/debug/third_party/icu4c-57.1/source/i18n/currpinf.o build/debug/third_party/icu4c-57.1/source/i18n/msgfmt.o build/debug/third_party/icu4c-57.1/source/i18n/curramt.o build/debug/third_party/icu4c-57.1/source/i18n/format.o build/debug/third_party/icu4c-57.1/source/i18n/decNumber.o build/debug/third_party/icu4c-57.1/source/i18n/sharedbreakiterator.o build/debug/third_party/icu4c-57.1/source/i18n/collationkeys.o build/debug/third_party/icu4c-57.1/source/i18n/titletrn.o build/debug/third_party/icu4c-57.1/source/i18n/ucol.o build/debug/third_party/icu4c-57.1/source/i18n/umsg.o build/debug/third_party/icu4c-57.1/source/i18n/collationrootelements.o build/debug/third_party/icu4c-57.1/source/i18n/collation.o build/debug/third_party/icu4c-57.1/source/i18n/dtrule.o build/debug/third_party/icu4c-57.1/source/i18n/chnsecal.o build/debug/third_party/icu4c-57.1/source/i18n/unesctrn.o build/debug/third_party/icu4c-57.1/source/i18n/measunit.o build/debug/third_party/icu4c-57.1/source/i18n/sortkey.o build/debug/third_party/icu4c-57.1/source/i18n/collationsets.o build/debug/third_party/icu4c-57.1/source/i18n/unumsys.o build/debug/third_party/icu4c-57.1/source/i18n/affixpatternparser.o build/debug/third_party/icu4c-57.1/source/i18n/uspoof.o build/debug/third_party/icu4c-57.1/source/i18n/taiwncal.o build/debug/third_party/icu4c-57.1/source/i18n/reldtfmt.o build/debug/third_party/icu4c-57.1/source/i18n/cecal.o build/debug/third_party/icu4c-57.1/source/i18n/regeximp.o build/debug/third_party/icu4c-57.1/source/i18n/csrucode.o build/debug/third_party/icu4c-57.1/source/i18n/esctrn.o build/debug/third_party/icu4c-57.1/source/i18n/collationroot.o build/debug/third_party/icu4c-57.1/source/i18n/tolowtrn.o build/debug/third_party/icu4c-57.1/source/i18n/collationweights.o build/debug/third_party/icu4c-57.1/source/i18n/ufieldpositer.o build/debug/third_party/icu4c-57.1/source/i18n/ucal.o build/debug/third_party/icu4c-57.1/source/i18n/csrutf8.o build/debug/third_party/icu4c-57.1/source/i18n/tmunit.o build/debug/third_party/icu4c-57.1/source/i18n/decimfmtimpl.o build/debug/third_party/icu4c-57.1/source/i18n/zrule.o build/debug/third_party/icu4c-57.1/source/i18n/collationdatawriter.o build/debug/third_party/icu4c-57.1/source/i18n/tztrans.o build/debug/third_party/icu4c-57.1/source/i18n/precision.o build/debug/third_party/icu4c-57.1/source/i18n/stsearch.o build/debug/third_party/icu4c-57.1/source/i18n/choicfmt.o build/debug/third_party/icu4c-57.1/source/i18n/plurfmt.o build/debug/third_party/icu4c-57.1/source/i18n/ulocdata.o build/debug/third_party/icu4c-57.1/source/i18n/buddhcal.o build/debug/third_party/icu4c-57.1/source/i18n/udatpg.o build/debug/third_party/icu4c-57.1/source/i18n/olsontz.o build/debug/third_party/icu4c-57.1/source/i18n/datefmt.o build/debug/third_party/icu4c-57.1/source/i18n/nfsubs.o build/debug/third_party/icu4c-57.1/source/i18n/coleitr.o build/debug/third_party/icu4c-57.1/source/i18n/tzrule.o build/debug/third_party/icu4c-57.1/source/i18n/utmscale.o build/debug/third_party/icu4c-57.1/source/i18n/strmatch.o build/debug/third_party/icu4c-57.1/source/i18n/region.o build/debug/third_party/icu4c-57.1/source/i18n/rbt_rule.o build/debug/third_party/icu4c-57.1/source/i18n/uregexc.o build/debug/third_party/icu4c-57.1/source/i18n/cpdtrans.o build/debug/third_party/icu4c-57.1/source/i18n/gregocal.o build/debug/third_party/icu4c-57.1/source/i18n/name2uni.o build/debug/third_party/icu4c-57.1/source/i18n/rbt_data.o build/debug/third_party/icu4c-57.1/source/i18n/decfmtst.o build/debug/third_party/icu4c-57.1/source/i18n/collationtailoring.o build/debug/third_party/icu4c-57.1/source/i18n/currfmt.o build/debug/third_party/icu4c-57.1/source/i18n/digitlst.o build/debug/third_party/icu4c-57.1/source/i18n/dtitvinf.o build/debug/third_party/icu4c-57.1/source/i18n/ucol_res.o build/debug/third_party/icu4c-57.1/source/i18n/dtptngen.o build/debug/third_party/icu4c-57.1/source/i18n/hebrwcal.o build/debug/third_party/icu4c-57.1/source/i18n/measfmt.o build/debug/third_party/icu4c-57.1/source/i18n/islamcal.o build/debug/third_party/icu4c-57.1/source/i18n/coptccal.o build/debug/third_party/icu4c-57.1/source/i18n/brktrans.o build/debug/third_party/icu4c-57.1/source/i18n/scriptset.o build/debug/third_party/icu4c-57.1/source/i18n/japancal.o build/debug/third_party/icu4c-57.1/source/i18n/dcfmtsym.o build/debug/third_party/icu4c-57.1/source/i18n/quantityformatter.o build/debug/third_party/icu4c-57.1/source/i18n/digitaffixesandpadding.o build/debug/third_party/icu4c-57.1/source/i18n/fpositer.o build/debug/third_party/icu4c-57.1/source/i18n/toupptrn.o build/debug/third_party/icu4c-57.1/source/i18n/collationdatabuilder.o build/debug/third_party/icu4c-57.1/source/i18n/csdetect.o build/debug/third_party/icu4c-57.1/source/i18n/basictz.o build/debug/third_party/icu4c-57.1/source/i18n/zonemeta.o build/debug/third_party/icu4c-57.1/source/i18n/transreg.o build/debug/third_party/icu4c-57.1/source/i18n/decimfmt.o build/debug/third_party/icu4c-57.1/source/i18n/ucsdet.o build/debug/third_party/icu4c-57.1/source/i18n/utrans.o build/debug/third_party/icu4c-57.1/source/i18n/tzgnames.o build/debug/third_party/icu4c-57.1/source/i18n/upluralrules.o build/debug/third_party/icu4c-57.1/source/i18n/astro.o build/debug/third_party/icu4c-57.1/source/i18n/csrsbcs.o build/debug/third_party/icu4c-57.1/source/i18n/funcrepl.o build/debug/third_party/icu4c-57.1/source/i18n/search.o build/debug/third_party/icu4c-57.1/source/i18n/currunit.o build/debug/third_party/icu4c-57.1/source/i18n/fmtable_cnv.o build/debug/third_party/icu4c-57.1/source/i18n/numfmt.o build/debug/third_party/icu4c-57.1/source/i18n/tmutamt.o build/debug/third_party/icu4c-57.1/source/i18n/rbt_pars.o build/debug/third_party/icu4c-57.1/source/i18n/uregex.o build/debug/third_party/icu4c-57.1/source/i18n/inputext.o build/debug/third_party/icu4c-57.1/source/i18n/collationcompare.o build/debug/third_party/icu4c-57.1/source/i18n/ucol_sit.o build/debug/third_party/icu4c-57.1/source/i18n/collationfastlatinbuilder.o build/debug/third_party/icu4c-57.1/source/i18n/collationiterator.o build/debug/third_party/icu4c-57.1/source/i18n/numsys.o build/debug/third_party/icu4c-57.1/source/i18n/tznames_impl.o build/debug/third_party/icu4c-57.1/source/i18n/csrecog.o build/debug/third_party/icu4c-57.1/source/i18n/csrmbcs.o build/debug/third_party/icu4c-57.1/source/i18n/collationdata.o build/debug/third_party/icu4c-57.1/source/i18n/collationfcd.o build/debug/third_party/icu4c-57.1/source/i18n/scientificnumberformatter.o build/debug/third_party/icu4c-57.1/source/i18n/collationfastlatin.o build/debug/third_party/icu4c-57.1/source/i18n/gregoimp.o build/debug/third_party/icu4c-57.1/source/i18n/regexcmp.o build/debug/third_party/icu4c-57.1/source/i18n/reldatefmt.o build/debug/third_party/icu4c-57.1/source/i18n/collationruleparser.o build/debug/third_party/icu4c-57.1/source/i18n/gender.o build/debug/third_party/icu4c-57.1/source/i18n/coll.o build/debug/third_party/icu4c-57.1/source/i18n/uspoof_wsconf.o build/debug/third_party/icu4c-57.1/source/i18n/quant.o build/debug/third_party/icu4c-57.1/source/i18n/tmutfmt.o build/debug/third_party/icu4c-57.1/source/i18n/csmatch.o build/debug/third_party/icu4c-57.1/source/i18n/uitercollationiterator.o build/debug/third_party/icu4c-57.1/source/i18n/regexst.o build/debug/third_party/icu4c-57.1/source/i18n/unum.o build/debug/third_party/icu4c-57.1/source/i18n/csr2022.o build/debug/third_party/icu4c-57.1/source/i18n/timezone.o build/debug/third_party/icu4c-57.1/source/i18n/digitaffix.o build/debug/third_party/icu4c-57.1/source/i18n/uni2name.o build/debug/third_party/icu4c-57.1/source/i18n/utf8collationiterator.o build/debug/third_party/icu4c-57.1/source/i18n/tridpars.o build/debug/third_party/icu4c-57.1/source/i18n/repattrn.o build/debug/third_party/icu4c-57.1/source/i18n/rbnf.o build/debug/third_party/icu4c-57.1/source/i18n/decimalformatpattern.o build/debug/third_party/icu4c-57.1/source/i18n/fphdlimp.o build/debug/third_party/icu4c-57.1/source/i18n/visibledigits.o build/debug/third_party/icu4c-57.1/source/i18n/persncal.o build/debug/mongo/db/keys_collection_document.o build/debug/mongo/db/keys_collection_cache.o build/debug/mongo/db/key_generator.o build/debug/mongo/db/catalog/collection.o build/debug/mongo/util/secure_zero_memory.o build/debug/third_party/s2/util/math/mathutil.o build/debug/mongo/db/keys_collection_manager_gen.o build/debug/mongo/db/keys_collection_manager.o build/debug/mongo/db/global_settings.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_minmax.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_bid64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log10.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp2.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fma.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int16.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_flag_operations.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp10.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_mul.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sqrt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_add.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_scalb.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fegetexceptflag.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lrintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cos.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_scalb.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sinh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tgamma.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atan.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint8.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lgamma.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fdimd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fma.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_quantize.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int16.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_bid128.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_scalbl.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_quantexpd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_frexp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_cbrt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_frexp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_quantexpd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_pow.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tan.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_acos.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_erf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_lgamma.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_ldexp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log1p.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_expm1.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_scalb.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_compare.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_acosh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int16.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_inv_trig.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_four_over_pi.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fdimd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_asin.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atanh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_llrintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_int.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_from_int.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_round.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_erfc.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_expm1.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_feraiseexcept.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_div.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log1p.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_mul.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tgamma.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_logbd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_sqrt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_trig.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_logb.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cosh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_modf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sqrt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp2.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int8.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_acosh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tgamma.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sinh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_acosh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int8.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint8.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cosh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_add.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_noncomp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tanh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_scalbl.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_exception.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fetestexcept.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log10.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cos.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cbrt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_quantize.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atan2.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fdimd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atanh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_ldexp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_llrintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_mod.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_string.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_nearbyintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_bid.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cosh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint8.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fma.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sqrt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_logbd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_noncomp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_llrintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint16.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_div.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log2.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sin.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log1p.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_round_integral.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lgamma.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lround.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_quantize.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tan.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atan2.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_nexttowardd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_inv_hyper.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_powi.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log2.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_binarydecimal.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_decimal_data.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_erfc.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_next.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fmod.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_erf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fesetexceptflag.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/sqrt_tab_t.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log10.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atan.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lgamma.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_nearbyintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_erf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_asinh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_asin.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint16.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint16.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sin.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_asinh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_convert_data.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_div.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_erf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cbrt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_rem.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_expm1.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_asinh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_exp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_log.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_nexttowardd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_frexp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_next.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_logb.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int8.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lrintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log2.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_quantexpd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_pow.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_ldexp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_asin.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_logbd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_bid128.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_round_integral.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp2.o build/debug/third_party/IntelRDFPMathLib20U1/dpml_log1p_t.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_minmax.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_next.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_erfc.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sub.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_string.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tan.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_mul.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lround.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_hypot.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_pow.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lround.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lrintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atan.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_compare.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp10.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_scalbl.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_2_str_tables.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_nexttowardd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cbrt.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_minmax.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atan2.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_rem.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_feclearexcept.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_round_integral.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_add.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_rem.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_noncomp.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int32.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_logb.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_hypot.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_string.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_hypot.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_acos.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_acos.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_bessel.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_nearbyintd.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops_64.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp10.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cos.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_compare.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tanh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod128.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_modf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_modf.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atanh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_pow.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fmod.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fmod.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sinh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod128.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sin.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_decimal_globals.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tanh.o build/debug/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_dpd.o build/debug/mongo/db/storage/duplicate_key_error_info.o build/debug/third_party/shim_intel_decimal128.o build/debug/mongo/util/net/sockaddr.o build/debug/mongo/util/net/cidr.o build/debug/mongo/util/net/hostandport.o build/debug/mongo/util/net/socket_exception.o build/debug/mongo/util/net/hostname_canonicalization.o build/debug/mongo/util/net/hostandport_gen.o build/debug/mongo/util/net/socket_utils.o build/debug/mongo/db/repl/rollback_gen.o build/debug/mongo/shell/shell_utils_extended.o build/debug/mongo/shell/shell_utils.o build/debug/mongo/shell/linenoise.o build/debug/mongo/shell/shell_options.o build/debug/mongo/shell/shell_utils_launcher.o build/debug/mongo/shell/mongo-server.o build/debug/mongo/shell/mk_wcwidth.o build/debug/mongo/db/catalog/disable_index_spec_namespace_generation_gen.o build/debug/mongo/db/repl/split_horizon.o build/debug/mongo/util/options_parser/options_parser_init.o build/debug/mongo/executor/network_interface.o build/debug/mongo/db/auth/address_restriction_gen.o build/debug/mongo/db/auth/address_restriction.o build/debug/mongo/executor/network_interface_factory.o build/debug/mongo/db/index/btree_key_generator.o build/debug/mongo/db/index/sort_key_generator.o build/debug/mongo/db/index/expression_keys_private.o build/debug/mongo/db/index/wildcard_key_generator.o build/debug/mongo/util/periodic_runner.o build/debug/third_party/kms-message/src/kms_request_str.o build/debug/third_party/kms-message/src/kms_message.o build/debug/third_party/kms-message/src/kms_kv_list.o build/debug/third_party/kms-message/src/kms_request_opt.o build/debug/third_party/kms-message/src/kms_encrypt_request.o build/debug/third_party/kms-message/src/kms_response.o build/debug/third_party/kms-message/src/kms_b64.o build/debug/third_party/kms-message/src/sort.o build/debug/third_party/kms-message/src/kms_response_parser.o build/debug/third_party/kms-message/src/kms_crypto_apple.o build/debug/third_party/kms-message/src/kms_request.o build/debug/third_party/kms-message/src/hexlify.o build/debug/third_party/kms-message/src/kms_decrypt_request.o build/debug/mongo/db/exec/projection_exec_agg.o build/debug/mongo/db/repl/member_config.o build/debug/mongo/db/repl/update_position_args.o build/debug/mongo/db/repl/repl_set_request_votes_args.o build/debug/mongo/db/repl/repl_set_config_gen.o build/debug/mongo/db/repl/is_master_response.o build/debug/mongo/db/repl/last_vote.o build/debug/mongo/db/repl/repl_set_heartbeat_args_v1.o build/debug/mongo/db/repl/repl_set_heartbeat_response.o build/debug/mongo/db/repl/repl_set_tag.o build/debug/mongo/db/repl/repl_set_config.o build/debug/mongo/db/pipeline/dependencies.o build/debug/third_party/s2/base/strtoint.o build/debug/third_party/s2/base/int128.o build/debug/third_party/s2/base/stringprintf.o build/debug/third_party/s2/base/logging.o build/debug/mongo/transport/transport_layer.o build/debug/mongo/transport/service_entry_point_utils.o build/debug/mongo/transport/session.o build/debug/mongo/util/intrusive_counter.o build/debug/mongo/db/multi_key_path_tracker.o build/debug/mongo/base/system_error.o build/debug/third_party/timelib-2018.01/interval.o build/debug/third_party/timelib-2018.01/parse_iso_intervals.o build/debug/third_party/timelib-2018.01/parse_date.o build/debug/third_party/timelib-2018.01/parse_zoneinfo.o build/debug/third_party/timelib-2018.01/timelib.o build/debug/third_party/timelib-2018.01/tm2unixtime.o build/debug/third_party/timelib-2018.01/astro.o build/debug/third_party/timelib-2018.01/parse_tz.o build/debug/third_party/timelib-2018.01/unixtime2tm.o build/debug/third_party/timelib-2018.01/dow.o build/debug/mongo/executor/thread_pool_task_executor.o build/debug/third_party/s2/util/coding/varint.o build/debug/third_party/s2/util/coding/coder.o build/debug/mongo/rpc/metadata/client_metadata_ismaster.o build/debug/mongo/rpc/metadata/client_metadata.o build/debug/mongo/shell/kms.o build/debug/mongo/shell/kms_local.o build/debug/mongo/shell/kms_gen.o build/debug/mongo/shell/kms_aws.o build/debug/mongo/util/regex_util.o build/debug/mongo/rpc/get_status_from_command_result.o build/debug/mongo/rpc/write_concern_error_detail.o build/debug/mongo/db/concurrency/lock_manager.o build/debug/mongo/db/concurrency/d_concurrency.o build/debug/mongo/db/concurrency/lock_state.o build/debug/mongo/db/concurrency/replication_state_transition_lock_guard.o build/debug/mongo/db/concurrency/lock_stats.o build/debug/mongo/db/command_generic_argument.o build/debug/mongo/db/logical_session_id.o build/debug/mongo/db/logical_session_id_gen.o build/debug/mongo/shell/mongodbcr.o build/debug/third_party/shim_abseil.o build/debug/third_party/s2/s2pointregion.o build/debug/third_party/s2/s2regionintersection.o build/debug/third_party/s2/s2regionunion.o build/debug/third_party/s2/s1angle.o build/debug/third_party/s2/s2region.o build/debug/third_party/s2/s2cap.o build/debug/third_party/s2/s2polyline.o build/debug/third_party/s2/s2latlngrect.o build/debug/third_party/s2/s1interval.o build/debug/third_party/s2/s2loop.o build/debug/third_party/s2/s2edgeindex.o build/debug/third_party/s2/s2edgeutil.o build/debug/third_party/s2/s2cellid.o build/debug/third_party/s2/s2polygonbuilder.o build/debug/third_party/s2/s2.o build/debug/third_party/s2/s2regioncoverer.o build/debug/third_party/s2/s2cellunion.o build/debug/third_party/s2/s2polygon.o build/debug/third_party/s2/s2latlng.o build/debug/third_party/s2/s2r2rect.o build/debug/third_party/s2/s2cell.o build/debug/mongo/util/concurrency/ticketholder.o build/debug/mongo/client/query.o build/debug/mongo/util/debugger.o build/debug/mongo/db/query/collation/collation_index_key.o build/debug/mongo/db/query/collation/collation_spec.o build/debug/mongo/db/logical_clock.o build/debug/mongo/db/logical_clock_gen.o build/debug/mongo/db/query/collation/collator_interface.o build/debug/mongo/db/pipeline/resume_token.o build/debug/mongo/db/pipeline/exchange_spec_gen.o build/debug/mongo/db/pipeline/document_source_change_stream_gen.o build/debug/mongo/db/pipeline/value_gen.o build/debug/mongo/db/pipeline/document_source_list_sessions_gen.o build/debug/mongo/db/pipeline/document_source_merge_gen.o build/debug/mongo/db/pipeline/document_source_replace_root_gen.o build/debug/mongo/db/pipeline/document_source_merge_spec.o build/debug/mongo/db/pipeline/document_source_merge_modes_gen.o build/debug/mongo/db/geo/hash.o build/debug/mongo/db/geo/shapes.o build/debug/mongo/db/geo/big_polygon.o build/debug/mongo/db/geo/r2_region_coverer.o build/debug/mongo/db/fts/unicode/string.o build/debug/mongo/db/fts/unicode/codepoints_diacritic_list.o build/debug/mongo/db/fts/unicode/codepoints_casefold.o build/debug/mongo/db/fts/unicode/codepoints_delimiter_list.o build/debug/mongo/db/fts/unicode/codepoints_diacritic_map.o build/debug/mongo/db/repl/storage_interface.o build/debug/mongo/base/secure_allocator.o build/debug/mongo/db/ops/write_ops_parsers.o build/debug/mongo/db/ops/write_ops_gen.o build/debug/mongo/db/stats/counters.o build/debug/third_party/shim_allocator.o build/debug/mongo/util/net/ssl_types.o build/debug/mongo/db/logical_time_validator.o build/debug/mongo/util/secure_compare_memory.o build/debug/mongo/util/safe_num.o build/debug/mongo/db/storage/key_string.o build/debug/mongo/util/net/ssl_manager_apple.o build/debug/mongo/util/net/ssl_manager.o build/debug/mongo/util/net/ssl_stream.o build/debug/mongo/util/net/ssl_parameters_gen.o build/debug/mongo/util/net/ssl_parameters.o build/debug/mongo/util/net/private/ssl_expiration.o build/debug/mongo/util/net/private/socket_poll.o build/debug/mongo/util/net/sock.o build/debug/mongo/transport/service_executor_gen.o build/debug/mongo/transport/service_executor_reserved.o build/debug/mongo/transport/service_executor_adaptive.o build/debug/mongo/transport/service_executor_synchronous.o build/debug/mongo/db/query/count_command_gen.o build/debug/mongo/db/query/killcursors_request.o build/debug/mongo/db/query/killcursors_response.o build/debug/mongo/db/query/find_and_modify_request.o build/debug/mongo/db/query/view_response_formatter.o build/debug/mongo/db/query/cursor_response.o build/debug/mongo/db/query/count_command_as_aggregation_command.o build/debug/mongo/db/query/count_request.o build/debug/mongo/db/query/cursor_request.o build/debug/mongo/db/query/getmore_request.o build/debug/mongo/client/authenticate.o build/debug/mongo/db/namespace_string.o build/debug/mongo/util/summation.o build/debug/mongo/util/processinfo_macOS.o build/debug/mongo/util/processinfo.o build/debug/third_party/shim_kms_message.o build/debug/third_party/shim_zlib.o build/debug/mongo/db/logical_time.o build/debug/mongo/db/operation_time_tracker.o build/debug/third_party/shim_asio.o build/debug/mongo/scripting/bson_template_evaluator.o build/debug/mongo/shell/bench.o build/debug/third_party/shim_mozjs.o build/debug/third_party/abseil-cpp-master/abseil-cpp/absl/container/internal/raw_hash_set.o build/debug/third_party/icu4c-57.1/source/stubdata/stubdata.o build/debug/mongo/client/read_preference.o build/debug/mongo/db/time_proof_service.o build/debug/mongo/crypto/sha_block_apple.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_lazy.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/huf_compress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/hist.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/fse_decompress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_common.o build/debug/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_compress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/decompress/huf_decompress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/zdict.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_fast.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstdmt_compress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/xxhash.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/pool.o build/debug/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/cover.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/zstd_common.o build/debug/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/divsufsort.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_opt.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_double_fast.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_compress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/decompress/zstd_decompress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/entropy_common.o build/debug/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_decompress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/fastcover.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/error_private.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_ldm.o build/debug/third_party/zstandard-1.3.7/zstd/lib/compress/fse_compress.o build/debug/third_party/zstandard-1.3.7/zstd/lib/common/threading.o build/debug/third_party/murmurhash3/MurmurHash3.o build/debug/mongo/db/pipeline/aggregation_request.o build/debug/third_party/shim_yaml.o build/debug/mongo/util/net/http_client_curl.o build/debug/mongo/util/net/ssl_options.o build/debug/mongo/db/views/resolved_view.o build/debug/mongo/s/catalog/sharding_catalog_client.o build/debug/mongo/util/concurrency/spin_lock.o build/debug/mongo/util/password_params_gen.o build/debug/mongo/util/password.o build/debug/mongo/db/matcher/expression_where_base.o build/debug/mongo/db/matcher/expression.o build/debug/mongo/db/matcher/expression_array.o build/debug/mongo/db/matcher/schema/json_schema_parser.o build/debug/mongo/db/matcher/schema/expression_internal_schema_fmod.o build/debug/mongo/db/matcher/schema/expression_internal_schema_num_array_items.o build/debug/mongo/db/matcher/rewrite_expr.o build/debug/mongo/db/matcher/schema/expression_internal_schema_eq.o build/debug/mongo/db/matcher/extensions_callback.o build/debug/mongo/db/matcher/expression_text_base.o build/debug/mongo/db/matcher/expression_internal_expr_eq.o build/debug/mongo/db/matcher/schema/expression_internal_schema_cond.o build/debug/mongo/db/matcher/schema/expression_internal_schema_unique_items.o build/debug/mongo/db/matcher/expression_algo.o build/debug/mongo/db/matcher/expression_text_noop.o build/debug/mongo/db/matcher/expression_tree.o build/debug/mongo/db/matcher/expression_geo.o build/debug/mongo/db/matcher/extensions_callback_noop.o build/debug/mongo/db/matcher/schema/encrypt_schema_types.o build/debug/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.o build/debug/mongo/db/matcher/expression_expr.o build/debug/mongo/db/matcher/expression_leaf.o build/debug/mongo/db/matcher/expression_parser.o build/debug/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.o build/debug/mongo/db/matcher/expression_where_noop.o build/debug/mongo/db/matcher/schema/expression_internal_schema_object_match.o build/debug/mongo/db/matcher/schema/expression_internal_schema_xor.o build/debug/mongo/db/matcher/matchable.o build/debug/mongo/db/matcher/schema/expression_internal_schema_str_length.o build/debug/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.o build/debug/mongo/db/matcher/expression_with_placeholder.o build/debug/mongo/db/matcher/matcher.o build/debug/mongo/db/matcher/schema/expression_internal_schema_match_array_index.o build/debug/mongo/db/matcher/matcher_type_set.o build/debug/mongo/db/matcher/schema/encrypt_schema_gen.o build/debug/mongo/db/matcher/match_details.o build/debug/mongo/db/matcher/schema/json_pointer.o build/debug/mongo/db/matcher/schema/expression_internal_schema_num_properties.o build/debug/mongo/db/query/tailable_mode_gen.o build/debug/mongo/db/query/query_request.o build/debug/mongo/db/query/tailable_mode.o build/debug/mongo/util/icu_init.o build/debug/mongo/client/async_client.o build/debug/mongo/idl/server_parameter_with_storage.o build/debug/mongo/idl/server_parameter.o build/debug/mongo/db/pipeline/parsed_add_fields.o build/debug/mongo/db/pipeline/parsed_exclusion_projection.o build/debug/mongo/db/pipeline/parsed_inclusion_projection.o build/debug/mongo/db/pipeline/parsed_aggregation_projection.o build/debug/mongo/db/pipeline/parsed_aggregation_projection_node.o build/debug/third_party/shim_snappy.o build/debug/mongo/db/auth/user_management_commands_parser.o build/debug/mongo/db/auth/action_set.o build/debug/mongo/db/auth/resource_pattern.o build/debug/mongo/db/auth/action_type.o build/debug/mongo/db/auth/privilege_parser.o build/debug/mongo/db/auth/impersonation_session.o build/debug/mongo/db/auth/privilege.o build/debug/mongo/util/boost_assert_shim.o build/debug/third_party/shim_boost.o build/debug/mongo/db/commands.o build/debug/mongo/transport/message_compressor_manager.o build/debug/mongo/transport/message_compressor_zstd.o build/debug/mongo/transport/message_compressor_metrics.o build/debug/mongo/transport/message_compressor_snappy.o build/debug/mongo/transport/message_compressor_zlib.o build/debug/mongo/transport/message_compressor_registry.o build/debug/mongo/crypto/aead_encryption.o build/debug/mongo/db/pipeline/field_path.o build/debug/mongo/db/repl/repl_client_info.o build/debug/mongo/db/repl/replication_coordinator_noop.o build/debug/mongo/db/repl/replication_coordinator.o build/debug/mongo/scripting/mozjs/cursor.o build/debug/mongo/scripting/mozjs/countdownlatch.o build/debug/mongo/scripting/mozjs/engine.o build/debug/mongo/scripting/mozjs/code.o build/debug/mongo/scripting/mozjs/regexp.o build/debug/mongo/scripting/mozjs/timestamp.o build/debug/mongo/scripting/mozjs/internedstring.o build/debug/mongo/scripting/mozjs/numberdecimal.o build/debug/mongo/scripting/mozjs/cursor_handle.o build/debug/mongo/scripting/mozjs/session.o build/debug/mongo/scripting/mozjs/nativefunction.o build/debug/mongo/scripting/mozjs/error.o build/debug/mongo/scripting/mozjs/scripting_util_gen.o build/debug/mongo/scripting/mozjs/dbquery.o build/debug/mongo/scripting/mozjs/global.o build/debug/mongo/scripting/mozjs/valuewriter.o build/debug/mongo/scripting/mozjs/db.o build/debug/mongo/scripting/mozjs/idwrapper.o build/debug/mongo/scripting/mozjs/base.o build/debug/mongo/scripting/mozjs/jsstringwrapper.o build/debug/mongo/scripting/mozjs/mongohelpers.o build/debug/mongo/scripting/mozjs/proxyscope.o build/debug/mongo/scripting/mozjs/bson.o build/debug/mongo/scripting/mozjs/implscope.o build/debug/mongo/scripting/mozjs/jsthread.o build/debug/mongo/scripting/mozjs/PosixNSPR.o build/debug/mongo/scripting/mozjs/oid.o build/debug/mongo/scripting/mozjs/uri.o build/debug/mongo/scripting/mozjs/engine_gen.o build/debug/mongo/scripting/mozjs/mongo.o build/debug/mongo/scripting/mozjs/maxkey.o build/debug/mongo/scripting/mozjs/status.o build/debug/mongo/scripting/mozjs/object.o build/debug/mongo/scripting/mozjs/numberint.o build/debug/mongo/scripting/mozjs/dbcollection.o build/debug/mongo/scripting/mozjs/jscustomallocator.o build/debug/mongo/scripting/mozjs/bindata.o build/debug/mongo/scripting/mozjs/dbref.o build/debug/mongo/scripting/mozjs/valuereader.o build/debug/mongo/scripting/mozjs/objectwrapper.o build/debug/mongo/scripting/mozjs/exception.o build/debug/mongo/scripting/mozjs/numberlong.o build/debug/mongo/scripting/mozjs/dbpointer.o build/debug/mongo/scripting/mozjs/mongohelpers_js.o build/debug/mongo/scripting/mozjs/minkey.o build/debug/third_party/fmt/dist/src/posix.o build/debug/third_party/fmt/dist/src/format.o build/debug/mongo/shell/mongo.o build/debug/mongo/db/fts/fts_query_noop.o build/debug/third_party/shim_fmt.o build/debug/mongo/executor/egress_tag_closer_manager.o build/debug/mongo/transport/transport_layer_manager.o build/debug/mongo/shell/linenoise_utf8.o build/debug/mongo/db/catalog/index_catalog_entry.o ================================================ FILE: cmake/mongodb/macosx-release.objects ================================================ build/opt/third_party/shim_asio.o build/opt/mongo/db/keys_collection_manager.o build/opt/mongo/crypto/aead_encryption.o build/opt/mongo/crypto/sha256_block.o build/opt/mongo/db/keys_collection_cache.o build/opt/mongo/db/key_generator.o build/opt/mongo/shell/mongodbcr.o build/opt/mongo/db/operation_time_tracker.o build/opt/mongo/db/keys_collection_manager_gen.o build/opt/mongo/db/logical_time.o build/opt/mongo/db/logical_clock.o build/opt/mongo/db/logical_clock_gen.o build/opt/mongo/db/query/tailable_mode_gen.o build/opt/mongo/db/query/tailable_mode.o build/opt/mongo/db/query/query_request.o build/opt/mongo/db/server_options.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_romanian.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_portuguese.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_italian.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_norwegian.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_english.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_german.o build/opt/third_party/libstemmer_c/runtime/api.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_hungarian.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_dutch.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_porter.o build/opt/third_party/libstemmer_c/libstemmer/libstemmer_utf8.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_russian.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_spanish.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_danish.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_finnish.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_swedish.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_turkish.o build/opt/third_party/libstemmer_c/src_c/stem_UTF_8_french.o build/opt/mongo/db/field_ref.o build/opt/mongo/db/field_parser.o build/opt/mongo/db/field_ref_set.o build/opt/mongo/db/keypattern.o build/opt/mongo/util/concurrency/ticketholder.o build/opt/mongo/db/dbmessage.o build/opt/third_party/boost-1.70.0/libs/iostreams/src/file_descriptor.o build/opt/third_party/boost-1.70.0/libs/iostreams/src/mapped_file.o build/opt/mongo/db/auth/restriction_environment.o build/opt/mongo/shell/kms_shell.o build/opt/mongo/rpc/metadata/oplog_query_metadata.o build/opt/mongo/rpc/metadata/logical_time_metadata.o build/opt/mongo/rpc/metadata/sharding_metadata.o build/opt/mongo/rpc/metadata/repl_set_metadata.o build/opt/mongo/rpc/metadata/egress_metadata_hook_list.o build/opt/mongo/rpc/metadata/tracking_metadata.o build/opt/mongo/rpc/metadata/config_server_metadata.o build/opt/mongo/rpc/metadata.o build/opt/mongo/util/net/private/socket_poll.o build/opt/mongo/db/logical_session_id_helpers.o build/opt/mongo/util/net/sock.o build/opt/mongo/db/pipeline/aggregation_request.o build/opt/mongo/db/query/query_knobs_gen.o build/opt/mongo/bson/mutable/element.o build/opt/mongo/bson/mutable/document.o build/opt/mongo/shell/encrypted_dbclient_base.o build/opt/mongo/db/matcher/expression.o build/opt/mongo/db/matcher/matchable.o build/opt/mongo/shell/fle_shell_options_gen.o build/opt/mongo/db/matcher/schema/expression_internal_schema_cond.o build/opt/mongo/db/matcher/schema/expression_internal_schema_eq.o build/opt/mongo/db/matcher/expression_geo.o build/opt/mongo/db/matcher/schema/encrypt_schema_types.o build/opt/mongo/db/matcher/expression_algo.o build/opt/mongo/db/matcher/expression_with_placeholder.o build/opt/mongo/db/matcher/expression_where_base.o build/opt/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.o build/opt/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.o build/opt/mongo/db/matcher/expression_tree.o build/opt/mongo/db/matcher/schema/encrypt_schema_gen.o build/opt/mongo/db/matcher/extensions_callback_noop.o build/opt/mongo/db/matcher/schema/expression_internal_schema_str_length.o build/opt/mongo/db/matcher/schema/expression_internal_schema_match_array_index.o build/opt/mongo/db/matcher/schema/json_schema_parser.o build/opt/mongo/db/matcher/schema/expression_internal_schema_xor.o build/opt/mongo/db/matcher/schema/expression_internal_schema_fmod.o build/opt/mongo/db/matcher/schema/json_pointer.o build/opt/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.o build/opt/mongo/db/matcher/matcher.o build/opt/mongo/db/matcher/schema/expression_internal_schema_object_match.o build/opt/mongo/db/matcher/expression_array.o build/opt/mongo/db/matcher/extensions_callback.o build/opt/mongo/db/matcher/schema/expression_internal_schema_unique_items.o build/opt/mongo/db/matcher/rewrite_expr.o build/opt/mongo/db/matcher/schema/expression_internal_schema_num_array_items.o build/opt/mongo/db/matcher/expression_internal_expr_eq.o build/opt/mongo/db/matcher/expression_text_noop.o build/opt/mongo/db/matcher/expression_expr.o build/opt/mongo/db/matcher/schema/expression_internal_schema_num_properties.o build/opt/mongo/db/matcher/expression_text_base.o build/opt/mongo/db/matcher/matcher_type_set.o build/opt/mongo/db/matcher/expression_leaf.o build/opt/mongo/db/matcher/expression_parser.o build/opt/mongo/db/matcher/expression_where_noop.o build/opt/mongo/db/matcher/match_details.o build/opt/third_party/shim_icu.o build/opt/mongo/client/mongo_uri_connect.o build/opt/mongo/client/global_conn_pool.o build/opt/mongo/client/replica_set_monitor.o build/opt/mongo/client/replica_set_monitor_manager.o build/opt/mongo/client/replica_set_change_notifier.o build/opt/mongo/client/global_conn_pool_gen.o build/opt/mongo/client/connection_string_connect.o build/opt/mongo/client/dbclient_connection.o build/opt/mongo/client/connpool.o build/opt/mongo/client/dbclient_rs.o build/opt/mongo/db/wire_version.o build/opt/third_party/yaml-cpp-0.6.2/src/ostream_wrapper.o build/opt/third_party/yaml-cpp-0.6.2/src/emitter.o build/opt/third_party/yaml-cpp-0.6.2/src/emitterstate.o build/opt/third_party/yaml-cpp-0.6.2/src/scanner.o build/opt/third_party/yaml-cpp-0.6.2/src/directives.o build/opt/third_party/yaml-cpp-0.6.2/src/singledocparser.o build/opt/third_party/yaml-cpp-0.6.2/src/stream.o build/opt/third_party/yaml-cpp-0.6.2/src/contrib/graphbuilderadapter.o build/opt/third_party/yaml-cpp-0.6.2/src/regex_yaml.o build/opt/third_party/yaml-cpp-0.6.2/src/exceptions.o build/opt/third_party/yaml-cpp-0.6.2/src/exp.o build/opt/third_party/yaml-cpp-0.6.2/src/convert.o build/opt/third_party/yaml-cpp-0.6.2/src/emitterutils.o build/opt/third_party/yaml-cpp-0.6.2/src/simplekey.o build/opt/third_party/yaml-cpp-0.6.2/src/contrib/graphbuilder.o build/opt/third_party/yaml-cpp-0.6.2/src/scanscalar.o build/opt/third_party/yaml-cpp-0.6.2/src/nodeevents.o build/opt/third_party/yaml-cpp-0.6.2/src/scantag.o build/opt/third_party/yaml-cpp-0.6.2/src/node_data.o build/opt/third_party/yaml-cpp-0.6.2/src/scantoken.o build/opt/third_party/yaml-cpp-0.6.2/src/binary.o build/opt/third_party/yaml-cpp-0.6.2/src/parser.o build/opt/third_party/yaml-cpp-0.6.2/src/emit.o build/opt/third_party/yaml-cpp-0.6.2/src/memory.o build/opt/third_party/yaml-cpp-0.6.2/src/parse.o build/opt/third_party/yaml-cpp-0.6.2/src/node.o build/opt/third_party/yaml-cpp-0.6.2/src/null.o build/opt/third_party/yaml-cpp-0.6.2/src/nodebuilder.o build/opt/third_party/yaml-cpp-0.6.2/src/emitfromevents.o build/opt/third_party/yaml-cpp-0.6.2/src/tag.o build/opt/mongo/db/repl/replication_consistency_markers.o build/opt/mongo/db/fts/unicode/codepoints_delimiter_list.o build/opt/mongo/db/fts/unicode/codepoints_diacritic_map.o build/opt/mongo/db/repl/replication_process.o build/opt/mongo/db/fts/unicode/codepoints_diacritic_list.o build/opt/mongo/db/fts/unicode/codepoints_casefold.o build/opt/mongo/db/fts/unicode/string.o build/opt/mongo/util/clock_source.o build/opt/mongo/util/background_thread_clock_source.o build/opt/mongo/util/fast_clock_source_factory.o build/opt/third_party/boost-1.70.0/libs/system/src/error_code.o build/opt/mongo/client/read_preference.o build/opt/third_party/s2/util/math/mathutil.o build/opt/mongo/db/traffic_reader.o build/opt/mongo/util/regex_util.o build/opt/mongo/db/concurrency/lock_state.o build/opt/mongo/db/concurrency/lock_stats.o build/opt/mongo/db/concurrency/lock_manager.o build/opt/mongo/db/concurrency/replication_state_transition_lock_guard.o build/opt/mongo/db/concurrency/d_concurrency.o build/opt/mongo/crypto/sha_block_apple.o build/opt/mongo/util/winutil.o build/opt/mongo/db/catalog/collection_catalog.o build/opt/mongo/util/password.o build/opt/mongo/util/password_params_gen.o build/opt/third_party/s2/util/coding/coder.o build/opt/third_party/s2/util/coding/varint.o build/opt/mongo/db/repl/read_concern_args.o build/opt/mongo/executor/task_executor.o build/opt/mongo/transport/transport_layer_asio.o build/opt/mongo/db/audit.o build/opt/mongo/rpc/protocol.o build/opt/mongo/rpc/op_msg.o build/opt/mongo/rpc/message.o build/opt/mongo/util/signal_win32.o build/opt/mongo/util/signal_handlers.o build/opt/third_party/murmurhash3/MurmurHash3.o build/opt/mongo/executor/network_interface_factory.o build/opt/mongo/db/pipeline/expression_trigonometric.o build/opt/mongo/db/pipeline/expression.o build/opt/mongo/db/global_settings.o build/opt/mongo/db/geo/geometry_container.o build/opt/mongo/db/geo/geoparser.o build/opt/mongo/executor/connection_pool_stats.o build/opt/mongo/util/background.o build/opt/mongo/db/namespace_string.o build/opt/mongo/db/repl/replication_coordinator.o build/opt/mongo/db/repl/repl_client_info.o build/opt/mongo/db/repl/replication_coordinator_noop.o build/opt/mongo/util/secure_compare_memory.o build/opt/third_party/shim_abseil.o build/opt/mongo/util/options_parser/environment.o build/opt/mongo/util/options_parser/constraints.o build/opt/mongo/util/options_parser/value.o build/opt/mongo/util/options_parser/options_parser.o build/opt/mongo/util/options_parser/option_description.o build/opt/mongo/util/options_parser/startup_option_init.o build/opt/mongo/util/options_parser/startup_options.o build/opt/mongo/util/options_parser/option_section.o build/opt/third_party/zlib-1.2.11/inftrees.o build/opt/third_party/zlib-1.2.11/compress.o build/opt/third_party/zlib-1.2.11/deflate.o build/opt/third_party/zlib-1.2.11/crc32.o build/opt/third_party/zlib-1.2.11/infback.o build/opt/third_party/zlib-1.2.11/inflate.o build/opt/third_party/zlib-1.2.11/inffast.o build/opt/third_party/zlib-1.2.11/zutil.o build/opt/third_party/zlib-1.2.11/adler32.o build/opt/third_party/zlib-1.2.11/trees.o build/opt/third_party/zlib-1.2.11/uncompr.o build/opt/mongo/db/pipeline/field_path.o build/opt/third_party/shim_zstd.o build/opt/mongo/base/system_error.o build/opt/mongo/transport/message_compressor_options_client_gen.o build/opt/mongo/shell/mongo.o build/opt/mongo/shell/shell_options_init.o build/opt/mongo/util/safe_num.o build/opt/mongo/util/secure_zero_memory.o build/opt/mongo/db/query/collation/collator_factory_interface.o build/opt/third_party/s2/s2cap.o build/opt/third_party/s2/s2cellid.o build/opt/third_party/s2/s2cell.o build/opt/third_party/s2/s2regioncoverer.o build/opt/third_party/s2/s1interval.o build/opt/third_party/s2/s2edgeindex.o build/opt/third_party/s2/s2.o build/opt/third_party/s2/s2latlng.o build/opt/third_party/s2/s2loop.o build/opt/third_party/s2/s2pointregion.o build/opt/third_party/s2/s2latlngrect.o build/opt/third_party/s2/s2r2rect.o build/opt/third_party/s2/s2region.o build/opt/third_party/s2/s2cellunion.o build/opt/third_party/s2/s1angle.o build/opt/third_party/s2/s2polyline.o build/opt/third_party/s2/s2regionintersection.o build/opt/third_party/s2/s2edgeutil.o build/opt/third_party/s2/s2polygonbuilder.o build/opt/third_party/s2/s2regionunion.o build/opt/third_party/s2/s2polygon.o build/opt/mongo/rpc/object_check_gen.o build/opt/mongo/rpc/factory.o build/opt/mongo/rpc/legacy_request_builder.o build/opt/mongo/rpc/legacy_reply_builder.o build/opt/mongo/rpc/legacy_reply.o build/opt/mongo/rpc/reply_builder_interface.o build/opt/mongo/rpc/legacy_request.o build/opt/mongo/rpc/object_check.o build/opt/mongo/db/commands/server_status_metric.o build/opt/mongo/db/commands/server_status_internal.o build/opt/mongo/client/sasl_client_authenticate.o build/opt/mongo/client/sasl_client_session.o build/opt/mongo/client/sasl_scram_client_conversation.o build/opt/mongo/client/sasl_client_authenticate_impl.o build/opt/mongo/client/sasl_plain_client_conversation.o build/opt/mongo/client/sasl_client_conversation.o build/opt/mongo/client/native_sasl_client_session.o build/opt/mongo/db/unclean_shutdown.o build/opt/mongo/db/operation_context_group.o build/opt/mongo/db/repl_set_member_in_standalone_mode.o build/opt/mongo/db/baton.o build/opt/mongo/db/client.o build/opt/mongo/db/server_recovery.o build/opt/mongo/db/operation_context.o build/opt/mongo/db/service_context.o build/opt/mongo/db/default_baton.o build/opt/mongo/db/pipeline/document_path_support.o build/opt/mongo/db/pipeline/value_comparator.o build/opt/mongo/db/pipeline/value.o build/opt/mongo/db/pipeline/document.o build/opt/mongo/db/pipeline/document_comparator.o build/opt/third_party/shim_timelib.o build/opt/mongo/client/dbclient_cursor.o build/opt/mongo/client/index_spec.o build/opt/mongo/client/dbclient_base.o build/opt/mongo/idl/idl_parser.o build/opt/mongo/db/repl/optime.o build/opt/mongo/db/repl/bson_extract_optime.o build/opt/third_party/s2/strings/strutil.o build/opt/third_party/s2/strings/stringprintf.o build/opt/third_party/s2/strings/split.o build/opt/mongo/bson/util/bson_extract.o build/opt/mongo/db/matcher/path_internal.o build/opt/mongo/db/matcher/path.o build/opt/mongo/shell/linenoise_utf8.o build/opt/third_party/shim_kms_message.o build/opt/mongo/db/repl/member_config.o build/opt/mongo/db/repl/repl_set_heartbeat_args_v1.o build/opt/mongo/db/repl/last_vote.o build/opt/mongo/db/repl/repl_set_heartbeat_response.o build/opt/mongo/db/repl/update_position_args.o build/opt/mongo/db/repl/repl_set_request_votes_args.o build/opt/mongo/db/repl/repl_set_config.o build/opt/mongo/db/repl/repl_set_config_gen.o build/opt/mongo/db/repl/is_master_response.o build/opt/mongo/db/repl/repl_set_tag.o build/opt/mongo/db/repl/split_horizon.o build/opt/mongo/db/query/collation/collator_interface.o build/opt/mongo/db/query/collation/collation_spec.o build/opt/mongo/db/query/collation/collation_index_key.o build/opt/mongo/executor/connection_pool.o build/opt/mongo/util/summation.o build/opt/mongo/db/index/s2_common.o build/opt/mongo/db/index/expression_params.o build/opt/mongo/util/processinfo.o build/opt/mongo/util/processinfo_macOS.o build/opt/mongo/db/commands/test_commands_enabled_gen.o build/opt/mongo/db/commands/test_commands_enabled.o build/opt/mongo/util/dns_query.o build/opt/mongo/util/boost_assert_shim.o build/opt/mongo/db/catalog/disable_index_spec_namespace_generation_gen.o build/opt/mongo/crypto/sha1_block.o build/opt/mongo/db/commands/server_status.o build/opt/mongo/db/ops/write_ops_parsers.o build/opt/mongo/db/ops/write_ops_gen.o build/opt/third_party/asio-master/asio/src/asio.o build/opt/mongo/db/repl/rollback_gen.o build/opt/mongo/db/auth/authorization_manager.o build/opt/mongo/db/auth/user_name.o build/opt/mongo/db/auth/authorization_session.o build/opt/mongo/db/auth/role_name.o build/opt/mongo/db/auth/auth_decorations.o build/opt/third_party/shim_intel_decimal128.o build/opt/mongo/db/multi_key_path_tracker.o build/opt/mongo/util/version_impl.o build/opt/mongo/executor/thread_pool_task_executor.o build/opt/mongo/util/net/ssl_types.o build/opt/mongo/util/net/ssl_manager_apple.o build/opt/mongo/util/net/private/ssl_expiration.o build/opt/mongo/util/net/ssl_stream.o build/opt/mongo/util/net/ssl_parameters_gen.o build/opt/mongo/util/net/ssl_parameters.o build/opt/mongo/util/net/ssl_manager.o build/opt/third_party/shim_yaml.o build/opt/mongo/executor/remote_command_request.o build/opt/mongo/executor/remote_command_response.o build/opt/mongo/db/server_options_server_helpers.o build/opt/mongo/client/async_client.o build/opt/third_party/kms-message/src/kms_encrypt_request.o build/opt/third_party/kms-message/src/kms_message.o build/opt/third_party/kms-message/src/sort.o build/opt/third_party/kms-message/src/kms_request_str.o build/opt/third_party/kms-message/src/kms_response.o build/opt/third_party/kms-message/src/kms_b64.o build/opt/third_party/kms-message/src/kms_crypto_apple.o build/opt/third_party/kms-message/src/kms_decrypt_request.o build/opt/third_party/kms-message/src/kms_request_opt.o build/opt/third_party/kms-message/src/hexlify.o build/opt/third_party/kms-message/src/kms_request.o build/opt/third_party/kms-message/src/kms_response_parser.o build/opt/third_party/kms-message/src/kms_kv_list.o build/opt/third_party/icu4c-57.1/source/i18n/zrule.o build/opt/third_party/icu4c-57.1/source/i18n/rematch.o build/opt/third_party/icu4c-57.1/source/i18n/affixpatternparser.o build/opt/third_party/icu4c-57.1/source/i18n/quant.o build/opt/third_party/icu4c-57.1/source/i18n/format.o build/opt/third_party/icu4c-57.1/source/i18n/olsontz.o build/opt/third_party/icu4c-57.1/source/i18n/ucol_res.o build/opt/third_party/icu4c-57.1/source/i18n/bocsu.o build/opt/third_party/icu4c-57.1/source/i18n/regextxt.o build/opt/third_party/icu4c-57.1/source/i18n/region.o build/opt/third_party/icu4c-57.1/source/i18n/rbt_rule.o build/opt/third_party/icu4c-57.1/source/i18n/scientificnumberformatter.o build/opt/third_party/icu4c-57.1/source/i18n/sortkey.o build/opt/third_party/icu4c-57.1/source/i18n/tznames_impl.o build/opt/third_party/icu4c-57.1/source/i18n/dangical.o build/opt/third_party/icu4c-57.1/source/i18n/digitaffix.o build/opt/third_party/icu4c-57.1/source/i18n/funcrepl.o build/opt/third_party/icu4c-57.1/source/i18n/strmatch.o build/opt/third_party/icu4c-57.1/source/i18n/rulebasedcollator.o build/opt/third_party/icu4c-57.1/source/i18n/precision.o build/opt/third_party/icu4c-57.1/source/i18n/ethpccal.o build/opt/third_party/icu4c-57.1/source/i18n/indiancal.o build/opt/third_party/icu4c-57.1/source/i18n/tridpars.o build/opt/third_party/icu4c-57.1/source/i18n/rbt.o build/opt/third_party/icu4c-57.1/source/i18n/udateintervalformat.o build/opt/third_party/icu4c-57.1/source/i18n/collationruleparser.o build/opt/third_party/icu4c-57.1/source/i18n/tmutfmt.o build/opt/third_party/icu4c-57.1/source/i18n/decNumber.o build/opt/third_party/icu4c-57.1/source/i18n/rbtz.o build/opt/third_party/icu4c-57.1/source/i18n/hebrwcal.o build/opt/third_party/icu4c-57.1/source/i18n/ulocdata.o build/opt/third_party/icu4c-57.1/source/i18n/identifier_info.o build/opt/third_party/icu4c-57.1/source/i18n/timezone.o build/opt/third_party/icu4c-57.1/source/i18n/nultrans.o build/opt/third_party/icu4c-57.1/source/i18n/vtzone.o build/opt/third_party/icu4c-57.1/source/i18n/curramt.o build/opt/third_party/icu4c-57.1/source/i18n/stsearch.o build/opt/third_party/icu4c-57.1/source/i18n/collationdatabuilder.o build/opt/third_party/icu4c-57.1/source/i18n/fmtable.o build/opt/third_party/icu4c-57.1/source/i18n/tmunit.o build/opt/third_party/icu4c-57.1/source/i18n/tzfmt.o build/opt/third_party/icu4c-57.1/source/i18n/measfmt.o build/opt/third_party/icu4c-57.1/source/i18n/fmtable_cnv.o build/opt/third_party/icu4c-57.1/source/i18n/casetrn.o build/opt/third_party/icu4c-57.1/source/i18n/msgfmt.o build/opt/third_party/icu4c-57.1/source/i18n/translit.o build/opt/third_party/icu4c-57.1/source/i18n/persncal.o build/opt/third_party/icu4c-57.1/source/i18n/rbt_pars.o build/opt/third_party/icu4c-57.1/source/i18n/dcfmtsym.o build/opt/third_party/icu4c-57.1/source/i18n/collation.o build/opt/third_party/icu4c-57.1/source/i18n/calendar.o build/opt/third_party/icu4c-57.1/source/i18n/decimalformatpattern.o build/opt/third_party/icu4c-57.1/source/i18n/rbnf.o build/opt/third_party/icu4c-57.1/source/i18n/taiwncal.o build/opt/third_party/icu4c-57.1/source/i18n/collationfcd.o build/opt/third_party/icu4c-57.1/source/i18n/choicfmt.o build/opt/third_party/icu4c-57.1/source/i18n/smpdtfst.o build/opt/third_party/icu4c-57.1/source/i18n/gender.o build/opt/third_party/icu4c-57.1/source/i18n/zonemeta.o build/opt/third_party/icu4c-57.1/source/i18n/standardplural.o build/opt/third_party/icu4c-57.1/source/i18n/ucln_in.o build/opt/third_party/icu4c-57.1/source/i18n/collationbuilder.o build/opt/third_party/icu4c-57.1/source/i18n/collationroot.o build/opt/third_party/icu4c-57.1/source/i18n/usearch.o build/opt/third_party/icu4c-57.1/source/i18n/uitercollationiterator.o build/opt/third_party/icu4c-57.1/source/i18n/dtptngen.o build/opt/third_party/icu4c-57.1/source/i18n/japancal.o build/opt/third_party/icu4c-57.1/source/i18n/decimfmt.o build/opt/third_party/icu4c-57.1/source/i18n/regexst.o build/opt/third_party/icu4c-57.1/source/i18n/uni2name.o build/opt/third_party/icu4c-57.1/source/i18n/utmscale.o build/opt/third_party/icu4c-57.1/source/i18n/udat.o build/opt/third_party/icu4c-57.1/source/i18n/compactdecimalformat.o build/opt/third_party/icu4c-57.1/source/i18n/digitgrouping.o build/opt/third_party/icu4c-57.1/source/i18n/collationiterator.o build/opt/third_party/icu4c-57.1/source/i18n/selfmt.o build/opt/third_party/icu4c-57.1/source/i18n/basictz.o build/opt/third_party/icu4c-57.1/source/i18n/collationrootelements.o build/opt/third_party/icu4c-57.1/source/i18n/dayperiodrules.o build/opt/third_party/icu4c-57.1/source/i18n/decimfmtimpl.o build/opt/third_party/icu4c-57.1/source/i18n/tztrans.o build/opt/third_party/icu4c-57.1/source/i18n/upluralrules.o build/opt/third_party/icu4c-57.1/source/i18n/collationdatareader.o build/opt/third_party/icu4c-57.1/source/i18n/smpdtfmt.o build/opt/third_party/icu4c-57.1/source/i18n/csrucode.o build/opt/third_party/icu4c-57.1/source/i18n/plurrule.o build/opt/third_party/icu4c-57.1/source/i18n/csrsbcs.o build/opt/third_party/icu4c-57.1/source/i18n/windtfmt.o build/opt/third_party/icu4c-57.1/source/i18n/vzone.o build/opt/third_party/icu4c-57.1/source/i18n/regexcmp.o build/opt/third_party/icu4c-57.1/source/i18n/numsys.o build/opt/third_party/icu4c-57.1/source/i18n/tznames.o build/opt/third_party/icu4c-57.1/source/i18n/tzrule.o build/opt/third_party/icu4c-57.1/source/i18n/dtitvfmt.o build/opt/third_party/icu4c-57.1/source/i18n/utf8collationiterator.o build/opt/third_party/icu4c-57.1/source/i18n/collationdatawriter.o build/opt/third_party/icu4c-57.1/source/i18n/collationweights.o build/opt/third_party/icu4c-57.1/source/i18n/brktrans.o build/opt/third_party/icu4c-57.1/source/i18n/toupptrn.o build/opt/third_party/icu4c-57.1/source/i18n/transreg.o build/opt/third_party/icu4c-57.1/source/i18n/visibledigits.o build/opt/third_party/icu4c-57.1/source/i18n/ucal.o build/opt/third_party/icu4c-57.1/source/i18n/smallintformatter.o build/opt/third_party/icu4c-57.1/source/i18n/search.o build/opt/third_party/icu4c-57.1/source/i18n/islamcal.o build/opt/third_party/icu4c-57.1/source/i18n/coll.o build/opt/third_party/icu4c-57.1/source/i18n/datefmt.o build/opt/third_party/icu4c-57.1/source/i18n/regeximp.o build/opt/third_party/icu4c-57.1/source/i18n/coleitr.o build/opt/third_party/icu4c-57.1/source/i18n/titletrn.o build/opt/third_party/icu4c-57.1/source/i18n/dtfmtsym.o build/opt/third_party/icu4c-57.1/source/i18n/inputext.o build/opt/third_party/icu4c-57.1/source/i18n/valueformatter.o build/opt/third_party/icu4c-57.1/source/i18n/decContext.o build/opt/third_party/icu4c-57.1/source/i18n/currpinf.o build/opt/third_party/icu4c-57.1/source/i18n/gregoimp.o build/opt/third_party/icu4c-57.1/source/i18n/anytrans.o build/opt/third_party/icu4c-57.1/source/i18n/tzgnames.o build/opt/third_party/icu4c-57.1/source/i18n/ucoleitr.o build/opt/third_party/icu4c-57.1/source/i18n/nfrule.o build/opt/third_party/icu4c-57.1/source/i18n/sharedbreakiterator.o build/opt/third_party/icu4c-57.1/source/i18n/cecal.o build/opt/third_party/icu4c-57.1/source/i18n/quantityformatter.o build/opt/third_party/icu4c-57.1/source/i18n/collationfastlatinbuilder.o build/opt/third_party/icu4c-57.1/source/i18n/unumsys.o build/opt/third_party/icu4c-57.1/source/i18n/ufieldpositer.o build/opt/third_party/icu4c-57.1/source/i18n/uspoof_build.o build/opt/third_party/icu4c-57.1/source/i18n/dtitvinf.o build/opt/third_party/icu4c-57.1/source/i18n/decfmtst.o build/opt/third_party/icu4c-57.1/source/i18n/nfsubs.o build/opt/third_party/icu4c-57.1/source/i18n/umsg.o build/opt/third_party/icu4c-57.1/source/i18n/collationsets.o build/opt/third_party/icu4c-57.1/source/i18n/strrepl.o build/opt/third_party/icu4c-57.1/source/i18n/csrmbcs.o build/opt/third_party/icu4c-57.1/source/i18n/ucol.o build/opt/third_party/icu4c-57.1/source/i18n/nfrs.o build/opt/third_party/icu4c-57.1/source/i18n/measure.o build/opt/third_party/icu4c-57.1/source/i18n/uspoof.o build/opt/third_party/icu4c-57.1/source/i18n/rbt_set.o build/opt/third_party/icu4c-57.1/source/i18n/csrecog.o build/opt/third_party/icu4c-57.1/source/i18n/reldatefmt.o build/opt/third_party/icu4c-57.1/source/i18n/utrans.o build/opt/third_party/icu4c-57.1/source/i18n/reldtfmt.o build/opt/third_party/icu4c-57.1/source/i18n/digitformatter.o build/opt/third_party/icu4c-57.1/source/i18n/buddhcal.o build/opt/third_party/icu4c-57.1/source/i18n/chnsecal.o build/opt/third_party/icu4c-57.1/source/i18n/numfmt.o build/opt/third_party/icu4c-57.1/source/i18n/collationsettings.o build/opt/third_party/icu4c-57.1/source/i18n/udatpg.o build/opt/third_party/icu4c-57.1/source/i18n/ucol_sit.o build/opt/third_party/icu4c-57.1/source/i18n/uspoof_impl.o build/opt/third_party/icu4c-57.1/source/i18n/digitinterval.o build/opt/third_party/icu4c-57.1/source/i18n/csmatch.o build/opt/third_party/icu4c-57.1/source/i18n/gregocal.o build/opt/third_party/icu4c-57.1/source/i18n/collationkeys.o build/opt/third_party/icu4c-57.1/source/i18n/rbt_data.o build/opt/third_party/icu4c-57.1/source/i18n/collationtailoring.o build/opt/third_party/icu4c-57.1/source/i18n/uregexc.o build/opt/third_party/icu4c-57.1/source/i18n/dtrule.o build/opt/third_party/icu4c-57.1/source/i18n/coptccal.o build/opt/third_party/icu4c-57.1/source/i18n/csdetect.o build/opt/third_party/icu4c-57.1/source/i18n/plurfmt.o build/opt/third_party/icu4c-57.1/source/i18n/astro.o build/opt/third_party/icu4c-57.1/source/i18n/fpositer.o build/opt/third_party/icu4c-57.1/source/i18n/simpletz.o build/opt/third_party/icu4c-57.1/source/i18n/measunit.o build/opt/third_party/icu4c-57.1/source/i18n/uregion.o build/opt/third_party/icu4c-57.1/source/i18n/uspoof_conf.o build/opt/third_party/icu4c-57.1/source/i18n/uspoof_wsconf.o build/opt/third_party/icu4c-57.1/source/i18n/currfmt.o build/opt/third_party/icu4c-57.1/source/i18n/pluralaffix.o build/opt/third_party/icu4c-57.1/source/i18n/collationcompare.o build/opt/third_party/icu4c-57.1/source/i18n/csrutf8.o build/opt/third_party/icu4c-57.1/source/i18n/collationfastlatin.o build/opt/third_party/icu4c-57.1/source/i18n/name2uni.o build/opt/third_party/icu4c-57.1/source/i18n/nortrans.o build/opt/third_party/icu4c-57.1/source/i18n/unum.o build/opt/third_party/icu4c-57.1/source/i18n/digitlst.o build/opt/third_party/icu4c-57.1/source/i18n/fphdlimp.o build/opt/third_party/icu4c-57.1/source/i18n/collationdata.o build/opt/third_party/icu4c-57.1/source/i18n/csr2022.o build/opt/third_party/icu4c-57.1/source/i18n/ztrans.o build/opt/third_party/icu4c-57.1/source/i18n/currunit.o build/opt/third_party/icu4c-57.1/source/i18n/ucsdet.o build/opt/third_party/icu4c-57.1/source/i18n/utf16collationiterator.o build/opt/third_party/icu4c-57.1/source/i18n/digitaffixesandpadding.o build/opt/third_party/icu4c-57.1/source/i18n/tmutamt.o build/opt/third_party/icu4c-57.1/source/i18n/esctrn.o build/opt/third_party/icu4c-57.1/source/i18n/tolowtrn.o build/opt/third_party/icu4c-57.1/source/i18n/repattrn.o build/opt/third_party/icu4c-57.1/source/i18n/wintzimpl.o build/opt/third_party/icu4c-57.1/source/i18n/alphaindex.o build/opt/third_party/icu4c-57.1/source/i18n/cpdtrans.o build/opt/third_party/icu4c-57.1/source/i18n/uregex.o build/opt/third_party/icu4c-57.1/source/i18n/unesctrn.o build/opt/third_party/icu4c-57.1/source/i18n/winnmfmt.o build/opt/third_party/icu4c-57.1/source/i18n/remtrans.o build/opt/third_party/icu4c-57.1/source/i18n/scriptset.o build/opt/mongo/util/periodic_runner.o build/opt/third_party/shim_stemmer.o build/opt/mongo/db/keys_collection_client_sharded.o build/opt/mongo/db/repl/storage_interface.o build/opt/mongo/db/auth/privilege.o build/opt/mongo/db/auth/action_type.o build/opt/mongo/db/auth/privilege_parser.o build/opt/mongo/db/auth/address_restriction_gen.o build/opt/mongo/db/auth/address_restriction.o build/opt/mongo/db/auth/action_set.o build/opt/mongo/db/auth/impersonation_session.o build/opt/mongo/db/auth/resource_pattern.o build/opt/mongo/db/auth/user_management_commands_parser.o build/opt/third_party/fmt/dist/src/posix.o build/opt/third_party/fmt/dist/src/format.o build/opt/mongo/db/fts/fts_query_noop.o build/opt/third_party/pcre-8.42/pcre_string_utils.o build/opt/third_party/pcre-8.42/pcre_byte_order.o build/opt/third_party/pcre-8.42/pcre_chartables.o build/opt/third_party/pcre-8.42/pcre_ord2utf8.o build/opt/third_party/pcre-8.42/pcre_tables.o build/opt/third_party/pcre-8.42/pcre_newline.o build/opt/third_party/pcre-8.42/pcre_globals.o build/opt/third_party/pcre-8.42/pcre_compile.o build/opt/third_party/pcre-8.42/pcre_dfa_exec.o build/opt/third_party/pcre-8.42/pcre_ucd.o build/opt/third_party/pcre-8.42/pcre_scanner.o build/opt/third_party/pcre-8.42/pcre_exec.o build/opt/third_party/pcre-8.42/pcre_valid_utf8.o build/opt/third_party/pcre-8.42/pcre_stringpiece.o build/opt/third_party/pcre-8.42/pcre_get.o build/opt/third_party/pcre-8.42/pcre_refcount.o build/opt/third_party/pcre-8.42/pcre_version.o build/opt/third_party/pcre-8.42/pcre_maketables.o build/opt/third_party/pcre-8.42/pcrecpp.o build/opt/third_party/pcre-8.42/pcre_xclass.o build/opt/third_party/pcre-8.42/pcre_study.o build/opt/third_party/pcre-8.42/pcre_config.o build/opt/third_party/pcre-8.42/pcre_fullinfo.o build/opt/mongo/idl/server_parameter_with_storage.o build/opt/mongo/idl/server_parameter.o build/opt/mongo/executor/network_interface_thread_pool.o build/opt/third_party/shim_pcrecpp.o build/opt/third_party/shim_allocator.o build/opt/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_decompress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/divsufsort.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/pool.o build/opt/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/fastcover.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/error_private.o build/opt/third_party/zstandard-1.3.7/zstd/lib/decompress/huf_decompress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_common.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/hist.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_compress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/huf_compress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/cover.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_ldm.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_opt.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/fse_decompress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_fast.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/fse_compress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/entropy_common.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_double_fast.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/zstd_common.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstdmt_compress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/decompress/zstd_decompress.o build/opt/third_party/zstandard-1.3.7/zstd/lib/dictBuilder/zdict.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/threading.o build/opt/third_party/zstandard-1.3.7/zstd/lib/compress/zstd_lazy.o build/opt/third_party/zstandard-1.3.7/zstd/lib/common/xxhash.o build/opt/third_party/zstandard-1.3.7/zstd/lib/deprecated/zbuff_compress.o build/opt/mongo/db/pipeline/exchange_spec_gen.o build/opt/mongo/db/pipeline/document_source_merge_modes_gen.o build/opt/mongo/db/pipeline/resume_token.o build/opt/mongo/db/pipeline/document_source_list_sessions_gen.o build/opt/mongo/db/pipeline/document_source_merge_gen.o build/opt/mongo/db/pipeline/document_source_replace_root_gen.o build/opt/mongo/db/pipeline/value_gen.o build/opt/mongo/db/pipeline/document_source_change_stream_gen.o build/opt/third_party/boost-1.70.0/libs/program_options/src/value_semantic.o build/opt/mongo/db/pipeline/document_source_merge_spec.o build/opt/third_party/boost-1.70.0/libs/program_options/src/config_file.o build/opt/third_party/boost-1.70.0/libs/program_options/src/winmain.o build/opt/third_party/boost-1.70.0/libs/program_options/src/parsers.o build/opt/third_party/boost-1.70.0/libs/program_options/src/positional_options.o build/opt/third_party/boost-1.70.0/libs/program_options/src/utf8_codecvt_facet.o build/opt/third_party/boost-1.70.0/libs/program_options/src/options_description.o build/opt/third_party/boost-1.70.0/libs/program_options/src/variables_map.o build/opt/third_party/boost-1.70.0/libs/program_options/src/cmdline.o build/opt/third_party/boost-1.70.0/libs/program_options/src/split.o build/opt/third_party/boost-1.70.0/libs/program_options/src/convert.o build/opt/mongo/shell/bench.o build/opt/mongo/shell/kms_aws.o build/opt/mongo/shell/kms_gen.o build/opt/mongo/shell/kms.o build/opt/mongo/shell/kms_local.o build/opt/mongo/client/connection_string.o build/opt/mongo/client/mongo_uri.o build/opt/mongo/util/intrusive_counter.o build/opt/mongo/s/catalog/sharding_catalog_client.o build/opt/mongo/shell/shell_utils_launcher.o build/opt/mongo/shell/shell_options.o build/opt/mongo/shell/shell_utils.o build/opt/mongo/shell/mk_wcwidth.o build/opt/mongo/shell/linenoise.o build/opt/mongo/shell/shell_utils_extended.o build/opt/mongo/shell/mongo-server.o build/opt/mongo/db/catalog/index_catalog.o build/opt/mongo/util/debugger.o build/opt/mongo/db/geo/big_polygon.o build/opt/mongo/db/geo/hash.o build/opt/mongo/db/geo/shapes.o build/opt/mongo/db/geo/r2_region_coverer.o build/opt/mongo/db/query/explain_options.o build/opt/mongo/scripting/bson_template_evaluator.o build/opt/third_party/s2/base/strtoint.o build/opt/third_party/s2/base/logging.o build/opt/third_party/s2/base/int128.o build/opt/third_party/s2/base/stringprintf.o build/opt/mongo/db/signed_logical_time.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lround.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sinh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_modf.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_nearbyintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_modf.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_nexttowardd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_mod.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_erf.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log10.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_frexp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log1p.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_next.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_powi.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_next.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_pow.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_log.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_string.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_rem.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_round.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_logb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_expm1.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sin.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_acos.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod128.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_noncomp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_bid.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_scalb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cos.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int8.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_exp10.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fetestexcept.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tan.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cos.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atan.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_acosh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_binarydecimal.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_logbd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_compare.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_erfc.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_logb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_llrintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lround.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_scalb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_expm1.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_scalb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_minmax.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_mul.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tanh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_string.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atan2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_trig.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_dpd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint16.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_round_integral.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_lrintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_erf.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_acosh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_logbd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lround.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sqrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_quantexpd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_2_str_tables.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_lrintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_asinh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_add.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int8.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_asinh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_logbd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fdimd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_erf.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tan.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_next.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sinh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_sqrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log10.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_cbrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint16.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_noncomp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atanh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cbrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_sqrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_rem.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_round_integral.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fmod.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_erfc.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_pow.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fesetexceptflag.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atanh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_feraiseexcept.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fdimd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_minmax.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_decimal_data.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_add.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_exception.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sub.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_string.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_lrintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_bessel.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_inv_hyper.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_convert_data.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_ldexp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atan2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_asin.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_erfc.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cosh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_frexp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sin.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_scalbl.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_llrintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_hypot.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_fdimd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_hypot.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atan.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_div.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_div.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_modf.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_int.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_logb.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_fmod.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_rem.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_frexp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sinh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_quantize.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_ldexp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log1p.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_lgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_inv_trig.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_asin.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tgamma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops_64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_feclearexcept.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_exp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_pow.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cos.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log10.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_quantexpd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_scalbl.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_tanh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_log.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_mul.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_int16.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_sqrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_nexttowardd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/sqrt_tab_t.o build/opt/third_party/IntelRDFPMathLib20U1/dpml_log1p_t.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_atan.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_nexttowardd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_div.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint16.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_asin.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_uint8.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_acosh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_round_integral.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_flag_operations.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_cosh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_expm1.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp10.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_add.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_atanh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cbrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_minmax.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_int16.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fma.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint8.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_nearbyintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_asinh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_cosh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_acos.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_uint32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_mul.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_exp2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_hypot.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_quantexpd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int8.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_quantize.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_scalbl.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_fmod.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_four_over_pi.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_tan.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_log1p.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_compare.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_acos.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_noncomp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_from_int.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_bid128.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_erf.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_decimal_globals.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_uint8.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_exp10.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_ldexp.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_to_bid64.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_cbrt.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/strtod32.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_to_bid128.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_log2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_fegetexceptflag.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_tanh.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_quantize.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_nearbyintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_llrintd.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/wcstod128.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid64_pow.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_to_int16.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid32_sin.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_atan2.o build/opt/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid128_compare.o build/opt/mongo/db/log_process_details.o build/opt/mongo/db/cluster_auth_mode_option_gen.o build/opt/mongo/db/keyfile_option_gen.o build/opt/mongo/db/server_options_base.o build/opt/mongo/db/server_options_general_gen.o build/opt/mongo/db/server_options_base_gen.o build/opt/mongo/db/server_options_nongeneral_gen.o build/opt/mongo/db/write_concern_options.o build/opt/mongo/base/secure_allocator.o build/opt/mongo/client/authenticate.o build/opt/mongo/db/index_names.o build/opt/mongo/util/options_parser/options_parser_init.o build/opt/mongo/db/query/datetime/date_time_support.o build/opt/mongo/client/query.o build/opt/mongo/db/views/resolved_view.o build/opt/mongo/util/icu.o build/opt/mongo/db/pipeline/variables.o build/opt/mongo/db/pipeline/expression_context.o build/opt/mongo/db/stats/counters.o build/opt/third_party/shim_zlib.o build/opt/mongo/util/fail_point.o build/opt/mongo/util/fail_point_service.o build/opt/mongo/util/icu_init.o build/opt/mongo/scripting/jsexception.o build/opt/mongo/util/fail_point_registry.o build/opt/mongo/util/fail_point_server_parameter_gen.o build/opt/mongo/scripting/utils.o build/opt/mongo/scripting/engine.o build/opt/mongo/scripting/deadline_monitor.o build/opt/mongo/scripting/deadline_monitor_gen.o build/opt/mongo/scripting/dbdirectclient_factory.o build/opt/mongo/rpc/get_status_from_command_result.o build/opt/mongo/rpc/write_concern_error_detail.o build/opt/mongo/executor/egress_tag_closer_manager.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_acosh.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_atan2.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_sinh.o build/opt/third_party/mozjs-60/extract/mozglue/misc/StackWalk.o build/opt/third_party/mozjs-60/mongo_sources/freeOpToJSContext.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_asinh.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_pow.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src32.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_copysign.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src36.o build/opt/third_party/mozjs-60/extract/js/src/gc/StoreBuffer.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_log2.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src21.o build/opt/third_party/mozjs-60/extract/mozglue/misc/TimeStamp_posix.o build/opt/third_party/mozjs-60/extract/js/src/util/DoubleToString.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src7.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_asin.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_cosh.o build/opt/third_party/mozjs-60/extract/js/src/jsarray.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_truncf.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src22.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_ceil.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_acos.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src30.o build/opt/third_party/mozjs-60/extract/mfbt/lz4.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_floor.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src42.o build/opt/third_party/mozjs-60/extract/js/src/jsmath.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src6.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src23.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src39.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src13.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_rintf.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_fabs.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src9.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src40.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src2.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src20.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src27.o build/opt/third_party/mozjs-60/extract/mozglue/misc/ConditionVariable_posix.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src15.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_log.o build/opt/third_party/mozjs-60/mongo_sources/mongoErrorReportToString.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src14.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src3.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src25.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src8.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_log1p.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src10.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_expm1.o build/opt/third_party/mozjs-60/extract/js/src/perf/pm_stub.o build/opt/third_party/mozjs-60/extract/js/src/builtin/RegExp.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_cbrt.o build/opt/third_party/mozjs-60/extract/js/src/vm/JSAtom.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_floorf.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_log10.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src17.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src44.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src38.o build/opt/third_party/mozjs-60/extract/mfbt/Compression.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src16.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src35.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src28.o build/opt/third_party/mozjs-60/extract/js/src/jit/x86-shared/Disassembler-x86-shared.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src12.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_hypot.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_ceilf.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_atan.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src24.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src37.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_trunc.o build/opt/third_party/mozjs-60/extract/mozglue/misc/Printf.o build/opt/third_party/mozjs-60/extract/mfbt/double-conversion/double-conversion/strtod.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_tanh.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/k_exp.o build/opt/third_party/mozjs-60/extract/js/src/mfbt/Unified_cpp_mfbt0.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_atanh.o build/opt/third_party/mozjs-60/extract/mozglue/misc/TimeStamp.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src0.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src29.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src18.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src45.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_exp.o build/opt/third_party/mozjs-60/extract/js/src/vm/Interpreter.o build/opt/third_party/mozjs-60/extract/mozglue/misc/Mutex_posix.o build/opt/third_party/mozjs-60/extract/js/src/frontend/Parser.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src31.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src33.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_rint.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/e_sqrt.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src11.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src41.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_scalbn.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src1.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src34.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src43.o build/opt/third_party/mozjs-60/extract/modules/fdlibm/s_nearbyint.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src4.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src19.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src5.o build/opt/third_party/mozjs-60/platform/x86_64/macOS/build/Unified_cpp_js_src26.o build/opt/mongo/db/fts/stop_words_list.o build/opt/mongo/db/fts/fts_basic_tokenizer.o build/opt/mongo/db/fts/fts_language.o build/opt/mongo/db/fts/stop_words.o build/opt/mongo/db/fts/fts_query_parser.o build/opt/mongo/db/fts/fts_unicode_phrase_matcher.o build/opt/mongo/db/fts/fts_matcher.o build/opt/mongo/db/fts/stemmer.o build/opt/mongo/db/fts/tokenizer.o build/opt/mongo/db/fts/fts_unicode_tokenizer.o build/opt/mongo/db/fts/fts_util.o build/opt/mongo/db/fts/fts_element_iterator.o build/opt/mongo/db/fts/fts_index_format.o build/opt/mongo/db/fts/fts_spec.o build/opt/mongo/db/fts/fts_query_impl.o build/opt/mongo/db/fts/fts_spec_legacy.o build/opt/mongo/db/fts/fts_basic_phrase_matcher.o build/opt/third_party/timelib-2018.01/parse_zoneinfo.o build/opt/third_party/timelib-2018.01/timelib.o build/opt/third_party/timelib-2018.01/parse_iso_intervals.o build/opt/third_party/timelib-2018.01/unixtime2tm.o build/opt/third_party/timelib-2018.01/parse_date.o build/opt/third_party/timelib-2018.01/tm2unixtime.o build/opt/third_party/timelib-2018.01/parse_tz.o build/opt/third_party/timelib-2018.01/astro.o build/opt/third_party/timelib-2018.01/dow.o build/opt/third_party/timelib-2018.01/interval.o build/opt/mongo/db/command_generic_argument.o build/opt/third_party/shim_boost.o build/opt/mongo/db/concurrency/flow_control_ticketholder.o build/opt/mongo/db/time_proof_service.o build/opt/mongo/db/storage/duplicate_key_error_info.o build/opt/third_party/abseil-cpp-master/abseil-cpp/absl/hash/internal/hash.o build/opt/third_party/abseil-cpp-master/abseil-cpp/absl/hash/internal/city.o build/opt/mongo/util/quick_exit.o build/opt/third_party/icu4c-57.1/source/stubdata/stubdata.o build/opt/mongo/scripting/mozjs/dbquery.o build/opt/mongo/scripting/mozjs/jscustomallocator.o build/opt/mongo/scripting/mozjs/db.o build/opt/mongo/scripting/mozjs/bindata.o build/opt/mongo/scripting/mozjs/engine.o build/opt/mongo/scripting/mozjs/numberlong.o build/opt/mongo/scripting/mozjs/timestamp.o build/opt/mongo/scripting/mozjs/object.o build/opt/mongo/scripting/mozjs/objectwrapper.o build/opt/mongo/scripting/mozjs/global.o build/opt/mongo/scripting/mozjs/dbcollection.o build/opt/mongo/scripting/mozjs/cursor.o build/opt/mongo/scripting/mozjs/PosixNSPR.o build/opt/mongo/scripting/mozjs/uri.o build/opt/mongo/scripting/mozjs/internedstring.o build/opt/mongo/scripting/mozjs/session.o build/opt/mongo/scripting/mozjs/base.o build/opt/mongo/scripting/mozjs/exception.o build/opt/mongo/scripting/mozjs/proxyscope.o build/opt/mongo/scripting/mozjs/jsthread.o build/opt/mongo/scripting/mozjs/mongohelpers_js.o build/opt/mongo/scripting/mozjs/error.o build/opt/mongo/scripting/mozjs/numberdecimal.o build/opt/mongo/scripting/mozjs/code.o build/opt/mongo/scripting/mozjs/mongo.o build/opt/mongo/scripting/mozjs/nativefunction.o build/opt/mongo/scripting/mozjs/jsstringwrapper.o build/opt/mongo/scripting/mozjs/engine_gen.o build/opt/mongo/scripting/mozjs/numberint.o build/opt/mongo/scripting/mozjs/maxkey.o build/opt/mongo/scripting/mozjs/dbref.o build/opt/mongo/scripting/mozjs/mongohelpers.o build/opt/mongo/scripting/mozjs/bson.o build/opt/mongo/scripting/mozjs/regexp.o build/opt/mongo/scripting/mozjs/idwrapper.o build/opt/mongo/scripting/mozjs/status.o build/opt/mongo/scripting/mozjs/cursor_handle.o build/opt/mongo/scripting/mozjs/oid.o build/opt/mongo/scripting/mozjs/valuewriter.o build/opt/mongo/scripting/mozjs/minkey.o build/opt/mongo/scripting/mozjs/dbpointer.o build/opt/mongo/scripting/mozjs/implscope.o build/opt/mongo/scripting/mozjs/scripting_util_gen.o build/opt/mongo/scripting/mozjs/valuereader.o build/opt/mongo/scripting/mozjs/countdownlatch.o build/opt/mongo/util/concurrency/spin_lock.o build/opt/third_party/snappy-1.1.7/snappy.o build/opt/third_party/snappy-1.1.7/snappy-sinksource.o build/opt/third_party/snappy-1.1.7/snappy-c.o build/opt/mongo/db/bson/dotted_path_support.o build/opt/third_party/icu4c-57.1/source/common/resbund_cnv.o build/opt/third_party/icu4c-57.1/source/common/ucase.o build/opt/third_party/icu4c-57.1/source/common/uprops.o build/opt/third_party/icu4c-57.1/source/common/punycode.o build/opt/third_party/icu4c-57.1/source/common/uvectr32.o build/opt/third_party/icu4c-57.1/source/common/uinit.o build/opt/third_party/icu4c-57.1/source/common/ustrcase.o build/opt/third_party/icu4c-57.1/source/common/unormcmp.o build/opt/third_party/icu4c-57.1/source/common/utrie2_builder.o build/opt/third_party/icu4c-57.1/source/common/unames.o build/opt/third_party/icu4c-57.1/source/common/uts46.o build/opt/third_party/icu4c-57.1/source/common/uloc_keytype.o build/opt/third_party/icu4c-57.1/source/common/uhash.o build/opt/third_party/icu4c-57.1/source/common/ucnv_ext.o build/opt/third_party/icu4c-57.1/source/common/bytestriebuilder.o build/opt/third_party/icu4c-57.1/source/common/cstring.o build/opt/third_party/icu4c-57.1/source/common/ures_cnv.o build/opt/third_party/icu4c-57.1/source/common/filterednormalizer2.o build/opt/third_party/icu4c-57.1/source/common/wintz.o build/opt/third_party/icu4c-57.1/source/common/ucnv_u7.o build/opt/third_party/icu4c-57.1/source/common/cmemory.o build/opt/third_party/icu4c-57.1/source/common/utrace.o build/opt/third_party/icu4c-57.1/source/common/parsepos.o build/opt/third_party/icu4c-57.1/source/common/charstr.o build/opt/third_party/icu4c-57.1/source/common/locdispnames.o build/opt/third_party/icu4c-57.1/source/common/unifunct.o build/opt/third_party/icu4c-57.1/source/common/uarrsort.o build/opt/third_party/icu4c-57.1/source/common/ustack.o build/opt/third_party/icu4c-57.1/source/common/ucnv.o build/opt/third_party/icu4c-57.1/source/common/locid.o build/opt/third_party/icu4c-57.1/source/common/normlzr.o build/opt/third_party/icu4c-57.1/source/common/umath.o build/opt/third_party/icu4c-57.1/source/common/brkiter.o build/opt/third_party/icu4c-57.1/source/common/uscript.o build/opt/third_party/icu4c-57.1/source/common/icudataver.o build/opt/third_party/icu4c-57.1/source/common/ucnv_cb.o build/opt/third_party/icu4c-57.1/source/common/ubidi_props.o build/opt/third_party/icu4c-57.1/source/common/unistr_props.o build/opt/third_party/icu4c-57.1/source/common/ubidiln.o build/opt/third_party/icu4c-57.1/source/common/rbbiscan.o build/opt/third_party/icu4c-57.1/source/common/ucnv_err.o build/opt/third_party/icu4c-57.1/source/common/stringtriebuilder.o build/opt/third_party/icu4c-57.1/source/common/util.o build/opt/third_party/icu4c-57.1/source/common/ucnvsel.o build/opt/third_party/icu4c-57.1/source/common/uresbund.o build/opt/third_party/icu4c-57.1/source/common/ulist.o build/opt/third_party/icu4c-57.1/source/common/servls.o build/opt/third_party/icu4c-57.1/source/common/udatamem.o build/opt/third_party/icu4c-57.1/source/common/stringpiece.o build/opt/third_party/icu4c-57.1/source/common/ubrk.o build/opt/third_party/icu4c-57.1/source/common/messagepattern.o build/opt/third_party/icu4c-57.1/source/common/ucasemap.o build/opt/third_party/icu4c-57.1/source/common/uloc.o build/opt/third_party/icu4c-57.1/source/common/ucnvhz.o build/opt/third_party/icu4c-57.1/source/common/ustr_titlecase_brkiter.o build/opt/third_party/icu4c-57.1/source/common/ushape.o build/opt/third_party/icu4c-57.1/source/common/bytestrie.o build/opt/third_party/icu4c-57.1/source/common/ucnv_io.o build/opt/third_party/icu4c-57.1/source/common/ucnv_u32.o build/opt/third_party/icu4c-57.1/source/common/serv.o build/opt/third_party/icu4c-57.1/source/common/sharedobject.o build/opt/third_party/icu4c-57.1/source/common/uniset.o build/opt/third_party/icu4c-57.1/source/common/unistr_titlecase_brkiter.o build/opt/third_party/icu4c-57.1/source/common/normalizer2.o build/opt/third_party/icu4c-57.1/source/common/uvector.o build/opt/third_party/icu4c-57.1/source/common/unistr_case_locale.o build/opt/third_party/icu4c-57.1/source/common/brkeng.o build/opt/third_party/icu4c-57.1/source/common/ustr_cnv.o build/opt/third_party/icu4c-57.1/source/common/ucnv2022.o build/opt/third_party/icu4c-57.1/source/common/ucharstrieiterator.o build/opt/third_party/icu4c-57.1/source/common/servlkf.o build/opt/third_party/icu4c-57.1/source/common/umutex.o build/opt/third_party/icu4c-57.1/source/common/servnotf.o build/opt/third_party/icu4c-57.1/source/common/rbbi.o build/opt/third_party/icu4c-57.1/source/common/ucharstriebuilder.o build/opt/third_party/icu4c-57.1/source/common/dtintrv.o build/opt/third_party/icu4c-57.1/source/common/ucol_swp.o build/opt/third_party/icu4c-57.1/source/common/cstr.o build/opt/third_party/icu4c-57.1/source/common/unistr_cnv.o build/opt/third_party/icu4c-57.1/source/common/rbbisetb.o build/opt/third_party/icu4c-57.1/source/common/uhash_us.o build/opt/third_party/icu4c-57.1/source/common/ucnvlat1.o build/opt/third_party/icu4c-57.1/source/common/ustrfmt.o build/opt/third_party/icu4c-57.1/source/common/listformatter.o build/opt/third_party/icu4c-57.1/source/common/usc_impl.o build/opt/third_party/icu4c-57.1/source/common/uenum.o build/opt/third_party/icu4c-57.1/source/common/propsvec.o build/opt/third_party/icu4c-57.1/source/common/unifilt.o build/opt/third_party/icu4c-57.1/source/common/uobject.o build/opt/third_party/icu4c-57.1/source/common/uniset_props.o build/opt/third_party/icu4c-57.1/source/common/uniset_closure.o build/opt/third_party/icu4c-57.1/source/common/uresdata.o build/opt/third_party/icu4c-57.1/source/common/servrbf.o build/opt/third_party/icu4c-57.1/source/common/ruleiter.o build/opt/third_party/icu4c-57.1/source/common/resbund.o build/opt/third_party/icu4c-57.1/source/common/ucnv_u16.o build/opt/third_party/icu4c-57.1/source/common/pluralmap.o build/opt/third_party/icu4c-57.1/source/common/ucnv_set.o build/opt/third_party/icu4c-57.1/source/common/dictionarydata.o build/opt/third_party/icu4c-57.1/source/common/cwchar.o build/opt/third_party/icu4c-57.1/source/common/locresdata.o build/opt/third_party/icu4c-57.1/source/common/simpleformatter.o build/opt/third_party/icu4c-57.1/source/common/unistr.o build/opt/third_party/icu4c-57.1/source/common/ustrcase_locale.o build/opt/third_party/icu4c-57.1/source/common/umapfile.o build/opt/third_party/icu4c-57.1/source/common/usetiter.o build/opt/third_party/icu4c-57.1/source/common/rbbitblb.o build/opt/third_party/icu4c-57.1/source/common/ulistformatter.o build/opt/third_party/icu4c-57.1/source/common/ucat.o build/opt/third_party/icu4c-57.1/source/common/ucharstrie.o build/opt/third_party/icu4c-57.1/source/common/uset.o build/opt/third_party/icu4c-57.1/source/common/locutil.o build/opt/third_party/icu4c-57.1/source/common/ubidi.o build/opt/third_party/icu4c-57.1/source/common/ustrtrns.o build/opt/third_party/icu4c-57.1/source/common/ucnvbocu.o build/opt/third_party/icu4c-57.1/source/common/udataswp.o build/opt/third_party/icu4c-57.1/source/common/ucnvscsu.o build/opt/third_party/icu4c-57.1/source/common/utypes.o build/opt/third_party/icu4c-57.1/source/common/caniter.o build/opt/third_party/icu4c-57.1/source/common/filteredbrk.o build/opt/third_party/icu4c-57.1/source/common/bytestream.o build/opt/third_party/icu4c-57.1/source/common/udata.o build/opt/third_party/icu4c-57.1/source/common/usprep.o build/opt/third_party/icu4c-57.1/source/common/ucnv_ct.o build/opt/third_party/icu4c-57.1/source/common/uchar.o build/opt/third_party/icu4c-57.1/source/common/bmpset.o build/opt/third_party/icu4c-57.1/source/common/bytestrieiterator.o build/opt/third_party/icu4c-57.1/source/common/util_props.o build/opt/third_party/icu4c-57.1/source/common/patternprops.o build/opt/third_party/icu4c-57.1/source/common/unisetspan.o build/opt/third_party/icu4c-57.1/source/common/ucnv_bld.o build/opt/third_party/icu4c-57.1/source/common/ucnv_u8.o build/opt/third_party/icu4c-57.1/source/common/utrie.o build/opt/third_party/icu4c-57.1/source/common/uidna.o build/opt/third_party/icu4c-57.1/source/common/ucnvisci.o build/opt/third_party/icu4c-57.1/source/common/schriter.o build/opt/third_party/icu4c-57.1/source/common/unorm.o build/opt/third_party/icu4c-57.1/source/common/utrie2.o build/opt/third_party/icu4c-57.1/source/common/ucnvmbcs.o build/opt/third_party/icu4c-57.1/source/common/ustr_wcs.o build/opt/third_party/icu4c-57.1/source/common/errorcode.o build/opt/third_party/icu4c-57.1/source/common/ucnv_cnv.o build/opt/third_party/icu4c-57.1/source/common/ustrenum.o build/opt/third_party/icu4c-57.1/source/common/unistr_case.o build/opt/third_party/icu4c-57.1/source/common/propname.o build/opt/third_party/icu4c-57.1/source/common/ucasemap_titlecase_brkiter.o build/opt/third_party/icu4c-57.1/source/common/locdspnm.o build/opt/third_party/icu4c-57.1/source/common/putil.o build/opt/third_party/icu4c-57.1/source/common/ubidiwrt.o build/opt/third_party/icu4c-57.1/source/common/dictbe.o build/opt/third_party/icu4c-57.1/source/common/utf_impl.o build/opt/third_party/icu4c-57.1/source/common/locavailable.o build/opt/third_party/icu4c-57.1/source/common/uset_props.o build/opt/third_party/icu4c-57.1/source/common/loadednormalizer2impl.o build/opt/third_party/icu4c-57.1/source/common/uinvchar.o build/opt/third_party/icu4c-57.1/source/common/icuplug.o build/opt/third_party/icu4c-57.1/source/common/uvectr64.o build/opt/third_party/icu4c-57.1/source/common/ucln_cmn.o build/opt/third_party/icu4c-57.1/source/common/ustring.o build/opt/third_party/icu4c-57.1/source/common/unifiedcache.o build/opt/third_party/icu4c-57.1/source/common/servlk.o build/opt/third_party/icu4c-57.1/source/common/ucnv_lmb.o build/opt/third_party/icu4c-57.1/source/common/uscript_props.o build/opt/third_party/icu4c-57.1/source/common/ucnvdisp.o build/opt/third_party/icu4c-57.1/source/common/uiter.o build/opt/third_party/icu4c-57.1/source/common/uchriter.o build/opt/third_party/icu4c-57.1/source/common/appendable.o build/opt/third_party/icu4c-57.1/source/common/utext.o build/opt/third_party/icu4c-57.1/source/common/locbased.o build/opt/third_party/icu4c-57.1/source/common/ucmndata.o build/opt/third_party/icu4c-57.1/source/common/uloc_tag.o build/opt/third_party/icu4c-57.1/source/common/rbbirb.o build/opt/third_party/icu4c-57.1/source/common/rbbistbl.o build/opt/third_party/icu4c-57.1/source/common/servslkf.o build/opt/third_party/icu4c-57.1/source/common/chariter.o build/opt/third_party/icu4c-57.1/source/common/locmap.o build/opt/third_party/icu4c-57.1/source/common/resource.o build/opt/third_party/icu4c-57.1/source/common/normalizer2impl.o build/opt/third_party/icu4c-57.1/source/common/rbbinode.o build/opt/third_party/icu4c-57.1/source/common/loclikely.o build/opt/third_party/icu4c-57.1/source/common/ucurr.o build/opt/third_party/icu4c-57.1/source/common/rbbidata.o build/opt/mongo/transport/message_compressor_zstd.o build/opt/mongo/transport/message_compressor_registry.o build/opt/mongo/transport/message_compressor_metrics.o build/opt/mongo/transport/message_compressor_manager.o build/opt/mongo/transport/message_compressor_zlib.o build/opt/mongo/transport/message_compressor_snappy.o build/opt/mongo/db/catalog/index_catalog_entry.o build/opt/mongo/util/net/http_client_curl.o build/opt/mongo/db/keys_collection_document.o build/opt/mongo/db/pipeline/parsed_aggregation_projection_node.o build/opt/mongo/db/pipeline/parsed_inclusion_projection.o build/opt/mongo/db/pipeline/parsed_aggregation_projection.o build/opt/mongo/db/pipeline/parsed_exclusion_projection.o build/opt/mongo/db/pipeline/parsed_add_fields.o build/opt/mongo/crypto/symmetric_crypto.o build/opt/mongo/crypto/symmetric_crypto_apple.o build/opt/mongo/crypto/symmetric_key.o build/opt/mongo/db/logical_session_id_gen.o build/opt/mongo/db/logical_session_id.o build/opt/mongo/util/net/ssl_options.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/path_traits.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/utf8_codecvt_facet.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/portability.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/unique_path.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/windows_file_codecvt.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/operations.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/codecvt_error_category.o build/opt/third_party/boost-1.70.0/libs/filesystem/src/path.o build/opt/mongo/util/concurrency/thread_name.o build/opt/mongo/util/exception_filter_win32.o build/opt/mongo/platform/stack_locator_macOS.o build/opt/mongo/base/error_codes.o build/opt/mongo/platform/shared_library_posix.o build/opt/mongo/bson/simple_bsonobj_comparator.o build/opt/mongo/util/base64.o build/opt/mongo/base/string_data.o build/opt/mongo/bson/bson_depth.o build/opt/mongo/platform/stack_locator.o build/opt/mongo/logger/logger.o build/opt/mongo/base/transaction_error.o build/opt/mongo/base/shim.o build/opt/mongo/logger/ramlog.o build/opt/mongo/base/data_type.o build/opt/mongo/base/initializer.o build/opt/mongo/util/time_support.o build/opt/mongo/base/global_initializer.o build/opt/mongo/util/file.o build/opt/mongo/util/boost_assert_impl.o build/opt/mongo/base/init.o build/opt/mongo/logger/message_event_utf8_encoder.o build/opt/mongo/base/data_type_string_data.o build/opt/mongo/bson/bsonmisc.o build/opt/mongo/util/startup_test.o build/opt/mongo/logger/rotatable_file_writer.o build/opt/mongo/util/errno_util.o build/opt/mongo/platform/shared_library.o build/opt/mongo/util/duration.o build/opt/mongo/logger/component_message_log_domain.o build/opt/mongo/platform/random.o build/opt/mongo/util/hex.o build/opt/mongo/bson/bson_validate.o build/opt/mongo/platform/strcasestr.o build/opt/mongo/util/signal_handlers_synchronous.o build/opt/mongo/platform/posix_fadvise.o build/opt/mongo/logger/console.o build/opt/mongo/bson/json.o build/opt/mongo/logger/message_log_domain.o build/opt/mongo/platform/process_id.o build/opt/mongo/util/stacktrace.o build/opt/mongo/util/log.o build/opt/mongo/logger/log_component_settings.o build/opt/mongo/base/validate_locale.o build/opt/mongo/base/initializer_dependency_graph.o build/opt/mongo/bson/bsontypes.o build/opt/mongo/logger/rotatable_file_manager.o build/opt/mongo/bson/bsonobj.o build/opt/mongo/bson/simple_bsonelement_comparator.o build/opt/mongo/util/system_tick_source.o build/opt/mongo/base/error_extra_info.o build/opt/mongo/util/itoa.o build/opt/mongo/base/global_initializer_registerer.o build/opt/mongo/platform/strnlen.o build/opt/mongo/platform/mutex.o build/opt/mongo/base/data_range_cursor.o build/opt/mongo/base/make_string_vector.o build/opt/mongo/util/stacktrace_posix.o build/opt/mongo/base/parse_number.o build/opt/mongo/util/timer.o build/opt/mongo/bson/timestamp.o build/opt/mongo/logger/logstream_builder.o build/opt/mongo/util/allocator.o build/opt/mongo/util/str.o build/opt/mongo/logger/log_severity.o build/opt/mongo/util/uuid.o build/opt/mongo/base/data_range.o build/opt/mongo/util/platform_init.o build/opt/mongo/util/system_clock_source.o build/opt/mongo/logger/redaction.o build/opt/mongo/util/concurrency/idle_thread_block.o build/opt/mongo/logger/log_component.o build/opt/mongo/util/exit.o build/opt/mongo/util/assert_util.o build/opt/mongo/bson/oid.o build/opt/mongo/base/data_type_terminated.o build/opt/mongo/platform/decimal128.o build/opt/mongo/util/text.o build/opt/mongo/bson/bson_comparator_interface_base.o build/opt/mongo/logger/log_manager.o build/opt/mongo/bson/bsonelement.o build/opt/mongo/util/version.o build/opt/mongo/util/shell_exec.o build/opt/mongo/base/simple_string_data_comparator.o build/opt/mongo/bson/bsonobjbuilder.o build/opt/mongo/base/status.o build/opt/mongo/rpc/metadata/client_metadata_ismaster.o build/opt/mongo/rpc/metadata/client_metadata.o build/opt/mongo/db/query/killcursors_response.o build/opt/mongo/db/query/cursor_response.o build/opt/mongo/db/query/count_command_gen.o build/opt/mongo/db/query/killcursors_request.o build/opt/mongo/db/query/count_command_as_aggregation_command.o build/opt/mongo/db/query/cursor_request.o build/opt/mongo/db/query/view_response_formatter.o build/opt/mongo/db/query/getmore_request.o build/opt/mongo/db/query/find_and_modify_request.o build/opt/mongo/db/query/count_request.o build/opt/mongo/db/storage/storage_options.o build/opt/mongo/db/exec/projection_exec_agg.o build/opt/mongo/db/storage/storage_parameters_gen.o build/opt/mongo/db/storage/write_unit_of_work.o build/opt/mongo/db/pipeline/dependencies.o build/opt/mongo/db/logical_time_validator.o build/opt/mongo/db/index/expression_keys_private.o build/opt/mongo/db/index/sort_key_generator.o build/opt/mongo/db/index/btree_key_generator.o build/opt/mongo/db/index/wildcard_key_generator.o build/opt/mongo/util/net/cidr.o build/opt/mongo/util/net/socket_utils.o build/opt/mongo/util/net/hostandport.o build/opt/mongo/util/net/hostname_canonicalization.o build/opt/mongo/util/net/sockaddr.o build/opt/mongo/util/net/hostandport_gen.o build/opt/mongo/util/net/socket_exception.o build/opt/mongo/util/password_digest.o build/opt/mongo/util/md5.o build/opt/mongo/executor/connection_pool_tl.o build/opt/mongo/executor/network_interface_tl.o build/opt/mongo/transport/transport_layer_manager.o build/opt/third_party/wiredtiger/src/checksum/software/checksum.o build/opt/third_party/wiredtiger/src/checksum/x86/crc32-x86.o build/opt/third_party/wiredtiger/src/checksum/x86/crc32-x86-alt.o build/opt/mongo/db/index/index_descriptor.o build/opt/third_party/abseil-cpp-master/abseil-cpp/absl/container/internal/raw_hash_set.o build/opt/mongo/db/pipeline/runtime_constants_gen.o build/opt/mongo/db/server_options_helpers.o build/opt/mongo/db/server_options_helpers_gen.o build/opt/mongo/db/query/hint_parser.o build/opt/mongo/db/query/hint_gen.o build/opt/mongo/shell/shell_options_gen.o build/opt/mongo/transport/service_executor_adaptive.o build/opt/mongo/transport/service_executor_reserved.o build/opt/mongo/transport/service_executor_synchronous.o build/opt/mongo/transport/service_executor_gen.o build/opt/mongo/transport/service_entry_point_utils.o build/opt/mongo/transport/transport_layer.o build/opt/mongo/transport/session.o build/opt/mongo/db/storage/key_string.o build/opt/third_party/shim_fmt.o build/opt/third_party/shim_snappy.o build/opt/mongo/rpc/metadata/impersonated_user_metadata.o build/opt/mongo/rpc/metadata/impersonated_user_metadata_gen.o build/opt/mongo/s/is_mongos.o build/opt/mongo/db/catalog/index_key_validate.o build/opt/mongo/shell/shell_options_storage.o build/opt/mongo/db/catalog/collection.o build/opt/mongo/db/hasher.o build/opt/mongo/util/cmdline_utils/censor_cmdline.o build/opt/mongo/s/database_version_gen.o build/opt/mongo/s/catalog/type_chunk_base_gen.o build/opt/mongo/s/catalog/type_database.o build/opt/mongo/s/request_types/split_chunk_request_type.o build/opt/mongo/s/shard_id.o build/opt/mongo/s/request_types/add_shard_request_type.o build/opt/mongo/s/catalog/type_changelog.o build/opt/mongo/s/catalog/type_shard_collection.o build/opt/mongo/s/request_types/add_shard_to_zone_request_type.o build/opt/mongo/s/request_types/remove_shard_from_zone_request_type.o build/opt/mongo/s/stale_exception.o build/opt/mongo/s/request_types/clone_catalog_data_gen.o build/opt/mongo/s/catalog/mongo_version_range.o build/opt/mongo/s/catalog/type_mongos.o build/opt/mongo/s/request_types/create_database_gen.o build/opt/mongo/s/chunk_version.o build/opt/mongo/s/request_types/shard_collection_gen.o build/opt/mongo/s/request_types/get_database_version_gen.o build/opt/mongo/s/would_change_owning_shard_exception.o build/opt/mongo/s/request_types/set_shard_version_request.o build/opt/mongo/s/request_types/move_chunk_request.o build/opt/mongo/s/catalog/type_collection.o build/opt/mongo/s/catalog/type_chunk.o build/opt/mongo/s/request_types/commit_chunk_migration_request_type.o build/opt/mongo/s/catalog/type_locks.o build/opt/mongo/s/request_types/flush_database_cache_updates_gen.o build/opt/mongo/s/catalog/type_lockpings.o build/opt/mongo/s/request_types/wait_for_fail_point_gen.o build/opt/mongo/s/request_types/migration_secondary_throttle_options.o build/opt/mongo/s/request_types/clear_jumbo_flag_gen.o build/opt/mongo/s/database_version_helpers.o build/opt/mongo/s/request_types/balance_chunk_request_type.o build/opt/mongo/s/catalog/type_shard_database.o build/opt/mongo/s/catalog/type_config_version.o build/opt/mongo/s/request_types/merge_chunk_request_type.o build/opt/mongo/s/request_types/flush_routing_table_cache_updates_gen.o build/opt/mongo/s/request_types/create_collection_gen.o build/opt/mongo/s/request_types/update_zone_key_range_request_type.o build/opt/mongo/s/catalog/type_tags.o build/opt/mongo/s/catalog/type_shard.o build/opt/mongo/s/cannot_implicitly_create_collection_info.o build/opt/mongo/s/chunk_version_gen.o build/opt/mongo/s/request_types/move_primary_gen.o build/opt/mongo/executor/network_interface.o build/opt/mongo/s/request_types/clone_collection_options_from_primary_shard_gen.o build/opt/mongo/db/commands.o build/opt/mongo/util/net/ssl_options_client.o build/opt/mongo/util/net/ssl_options_client_gen.o build/opt/mongo/db/repl/repl_settings_gen.o build/opt/mongo/db/repl/repl_settings.o build/opt/third_party/shim_mozjs.o ================================================ FILE: cmake/mongodb/windows-debug.objects ================================================ build\debug\mongo\util\net\ssl_options_client.obj build\debug\mongo\util\net\ssl_options_client_gen.obj build\debug\mongo\transport\message_compressor_options_client_gen.obj build\debug\mongo\shell\kms_shell.obj build\debug\mongo\shell\linenoise.obj build\debug\mongo\shell\mk_wcwidth.obj build\debug\mongo\shell\mongo-server.obj build\debug\mongo\shell\shell_options.obj build\debug\mongo\shell\shell_utils.obj build\debug\mongo\shell\shell_utils_extended.obj build\debug\mongo\shell\shell_utils_launcher.obj build\debug\mongo\util\version_impl.obj build\debug\mongo\util\signal_handlers.obj build\debug\mongo\util\signal_win32.obj build\debug\mongo\db\log_process_details.obj build\debug\mongo\db\server_options_server_helpers.obj build\debug\mongo\db\server_options_base.obj build\debug\mongo\db\cluster_auth_mode_option_gen.obj build\debug\mongo\db\keyfile_option_gen.obj build\debug\mongo\db\server_options_base_gen.obj build\debug\mongo\db\server_options_general_gen.obj build\debug\mongo\db\server_options_nongeneral_gen.obj build\debug\mongo\db\repl\is_master_response.obj build\debug\mongo\db\repl\member_config.obj build\debug\mongo\db\repl\repl_set_config.obj build\debug\mongo\db\repl\repl_set_heartbeat_args_v1.obj build\debug\mongo\db\repl\repl_set_heartbeat_response.obj build\debug\mongo\db\repl\repl_set_request_votes_args.obj build\debug\mongo\db\repl\repl_set_tag.obj build\debug\mongo\db\repl\update_position_args.obj build\debug\mongo\db\repl\last_vote.obj build\debug\mongo\db\repl\repl_set_config_gen.obj build\debug\mongo\db\repl\split_horizon.obj build\debug\mongo\db\concurrency\d_concurrency.obj build\debug\mongo\db\concurrency\lock_manager.obj build\debug\mongo\db\concurrency\lock_state.obj build\debug\mongo\db\concurrency\lock_stats.obj build\debug\mongo\db\concurrency\replication_state_transition_lock_guard.obj build\debug\mongo\util\concurrency\ticketholder.obj build\debug\mongo\db\concurrency\flow_control_ticketholder.obj build\debug\mongo\util\password.obj build\debug\mongo\util\password_params_gen.obj build\debug\mongo\util\options_parser\options_parser_init.obj build\debug\mongo\shell\bench.obj build\debug\mongo\db\traffic_reader.obj build\debug\mongo\db\logical_session_id_helpers.obj build\debug\mongo\db\catalog\index_key_validate.obj build\debug\mongo\db\index\btree_key_generator.obj build\debug\mongo\db\index\expression_keys_private.obj build\debug\mongo\db\index\sort_key_generator.obj build\debug\mongo\db\index\wildcard_key_generator.obj build\debug\mongo\db\exec\projection_exec_agg.obj build\debug\mongo\db\pipeline\parsed_aggregation_projection.obj build\debug\mongo\db\pipeline\parsed_aggregation_projection_node.obj build\debug\mongo\db\pipeline\parsed_exclusion_projection.obj build\debug\mongo\db\pipeline\parsed_inclusion_projection.obj build\debug\mongo\db\pipeline\parsed_add_fields.obj build\debug\mongo\db\index\expression_params.obj build\debug\mongo\db\index\s2_common.obj build\debug\mongo\db\hasher.obj build\debug\mongo\db\fts\fts_index_format.obj build\debug\mongo\db\fts\fts_matcher.obj build\debug\mongo\db\fts\fts_query_impl.obj build\debug\mongo\db\fts\fts_query_parser.obj build\debug\mongo\db\fts\fts_spec.obj build\debug\mongo\db\fts\fts_spec_legacy.obj build\debug\mongo\db\fts\fts_language.obj build\debug\mongo\db\fts\fts_basic_phrase_matcher.obj build\debug\mongo\db\fts\fts_basic_tokenizer.obj build\debug\mongo\db\fts\fts_unicode_phrase_matcher.obj build\debug\mongo\db\fts\fts_unicode_tokenizer.obj build\debug\mongo\db\fts\fts_util.obj build\debug\mongo\db\fts\fts_element_iterator.obj build\debug\mongo\db\fts\stemmer.obj build\debug\mongo\db\fts\stop_words.obj build\debug\mongo\db\fts\stop_words_list.obj build\debug\mongo\db\fts\tokenizer.obj build\debug\third_party\shim_stemmer.obj build\debug\third_party\libstemmer_c\runtime\api.obj build\debug\third_party\libstemmer_c\libstemmer\libstemmer_utf8.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_dutch.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_german.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_porter.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_spanish.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_english.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_hungarian.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_portuguese.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_swedish.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_finnish.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_italian.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_romanian.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_turkish.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_danish.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_french.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_norwegian.obj build\debug\third_party\libstemmer_c\src_c\stem_UTF_8_russian.obj build\debug\mongo\db\fts\unicode\codepoints_casefold.obj build\debug\mongo\db\fts\unicode\codepoints_delimiter_list.obj build\debug\mongo\db\fts\unicode\codepoints_diacritic_list.obj build\debug\mongo\db\fts\unicode\codepoints_diacritic_map.obj build\debug\mongo\db\fts\unicode\string.obj build\debug\mongo\shell\linenoise_utf8.obj build\debug\mongo\db\index\index_descriptor.obj build\debug\mongo\db\catalog\index_catalog.obj build\debug\mongo\db\catalog\index_catalog_entry.obj build\debug\mongo\db\catalog\disable_index_spec_namespace_generation_gen.obj build\debug\mongo\shell\encrypted_dbclient_base.obj build\debug\mongo\shell\fle_shell_options_gen.obj build\debug\mongo\shell\shell_options_gen.obj build\debug\mongo\shell\shell_options_storage.obj build\debug\mongo\shell\kms.obj build\debug\mongo\shell\kms_aws.obj build\debug\mongo\shell\kms_local.obj build\debug\mongo\shell\kms_gen.obj build\debug\third_party\shim_kms_message.obj build\debug\third_party\kms-message\src\hexlify.obj build\debug\third_party\kms-message\src\kms_b64.obj build\debug\third_party\kms-message\src\kms_decrypt_request.obj build\debug\third_party\kms-message\src\kms_encrypt_request.obj build\debug\third_party\kms-message\src\kms_kv_list.obj build\debug\third_party\kms-message\src\kms_message.obj build\debug\third_party\kms-message\src\kms_request.obj build\debug\third_party\kms-message\src\kms_request_opt.obj build\debug\third_party\kms-message\src\kms_request_str.obj build\debug\third_party\kms-message\src\kms_response.obj build\debug\third_party\kms-message\src\kms_response_parser.obj build\debug\third_party\kms-message\src\sort.obj build\debug\third_party\kms-message\src\kms_crypto_windows.obj build\debug\mongo\scripting\mozjs\base.obj build\debug\mongo\scripting\mozjs\bindata.obj build\debug\mongo\scripting\mozjs\bson.obj build\debug\mongo\scripting\mozjs\code.obj build\debug\mongo\scripting\mozjs\countdownlatch.obj build\debug\mongo\scripting\mozjs\cursor.obj build\debug\mongo\scripting\mozjs\cursor_handle.obj build\debug\mongo\scripting\mozjs\db.obj build\debug\mongo\scripting\mozjs\dbcollection.obj build\debug\mongo\scripting\mozjs\dbpointer.obj build\debug\mongo\scripting\mozjs\dbquery.obj build\debug\mongo\scripting\mozjs\dbref.obj build\debug\mongo\scripting\mozjs\engine.obj build\debug\mongo\scripting\mozjs\error.obj build\debug\mongo\scripting\mozjs\exception.obj build\debug\mongo\scripting\mozjs\global.obj build\debug\mongo\scripting\mozjs\idwrapper.obj build\debug\mongo\scripting\mozjs\implscope.obj build\debug\mongo\scripting\mozjs\internedstring.obj build\debug\mongo\scripting\mozjs\jscustomallocator.obj build\debug\mongo\scripting\mozjs\jsstringwrapper.obj build\debug\mongo\scripting\mozjs\jsthread.obj build\debug\mongo\scripting\mozjs\maxkey.obj build\debug\mongo\scripting\mozjs\minkey.obj build\debug\mongo\scripting\mozjs\mongo.obj build\debug\mongo\scripting\mozjs\mongohelpers.obj build\debug\mongo\scripting\mozjs\mongohelpers_js.obj build\debug\mongo\scripting\mozjs\nativefunction.obj build\debug\mongo\scripting\mozjs\numberdecimal.obj build\debug\mongo\scripting\mozjs\numberint.obj build\debug\mongo\scripting\mozjs\numberlong.obj build\debug\mongo\scripting\mozjs\object.obj build\debug\mongo\scripting\mozjs\objectwrapper.obj build\debug\mongo\scripting\mozjs\oid.obj build\debug\mongo\scripting\mozjs\PosixNSPR.obj build\debug\mongo\scripting\mozjs\proxyscope.obj build\debug\mongo\scripting\mozjs\regexp.obj build\debug\mongo\scripting\mozjs\session.obj build\debug\mongo\scripting\mozjs\status.obj build\debug\mongo\scripting\mozjs\timestamp.obj build\debug\mongo\scripting\mozjs\uri.obj build\debug\mongo\scripting\mozjs\valuereader.obj build\debug\mongo\scripting\mozjs\valuewriter.obj build\debug\mongo\scripting\mozjs\engine_gen.obj build\debug\mongo\scripting\mozjs\scripting_util_gen.obj build\debug\third_party\shim_mozjs.obj build\debug\third_party\mozjs-60\mongo_sources\mongoErrorReportToString.obj build\debug\third_party\mozjs-60\mongo_sources\freeOpToJSContext.obj build\debug\third_party\mozjs-60\extract\js\src\builtin\RegExp.obj build\debug\third_party\mozjs-60\extract\js\src\frontend\Parser.obj build\debug\third_party\mozjs-60\extract\js\src\gc\StoreBuffer.obj build\debug\third_party\mozjs-60\extract\js\src\jsarray.obj build\debug\third_party\mozjs-60\extract\js\src\jsmath.obj build\debug\third_party\mozjs-60\extract\js\src\mfbt\Unified_cpp_mfbt0.obj build\debug\third_party\mozjs-60\extract\js\src\perf\pm_stub.obj build\debug\third_party\mozjs-60\extract\js\src\util\DoubleToString.obj build\debug\third_party\mozjs-60\extract\js\src\vm\Interpreter.obj build\debug\third_party\mozjs-60\extract\js\src\vm\JSAtom.obj build\debug\third_party\mozjs-60\extract\mfbt\Compression.obj build\debug\third_party\mozjs-60\extract\mfbt\double-conversion\double-conversion\strtod.obj build\debug\third_party\mozjs-60\extract\mfbt\lz4.obj build\debug\third_party\mozjs-60\extract\mozglue\misc\Printf.obj build\debug\third_party\mozjs-60\extract\mozglue\misc\TimeStamp.obj build\debug\third_party\mozjs-60\extract\mozglue\misc\StackWalk.obj build\debug\third_party\mozjs-60\extract\mozglue\misc\ConditionVariable_windows.obj build\debug\third_party\mozjs-60\extract\mozglue\misc\Mutex_windows.obj build\debug\third_party\mozjs-60\extract\mozglue\misc\TimeStamp_windows.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_acos.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_acosh.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_asin.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_atan2.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_atanh.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_cosh.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_exp.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_hypot.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_log.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_log10.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_log2.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_pow.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_sinh.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\e_sqrt.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\k_exp.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_asinh.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_atan.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_cbrt.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_ceil.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_ceilf.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_copysign.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_expm1.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_fabs.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_floor.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_floorf.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_log1p.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_nearbyint.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_rint.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_rintf.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_scalbn.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_tanh.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_trunc.obj build\debug\third_party\mozjs-60\extract\modules\fdlibm\s_truncf.obj build\debug\third_party\mozjs-60\extract\js\src\jit\x86-shared\Disassembler-x86-shared.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src0.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src1.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src10.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src11.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src12.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src13.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src14.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src15.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src16.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src17.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src18.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src19.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src2.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src20.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src21.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src22.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src23.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src24.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src25.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src26.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src27.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src28.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src29.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src3.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src30.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src31.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src32.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src33.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src34.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src35.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src36.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src37.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src38.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src39.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src4.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src40.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src41.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src42.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src43.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src44.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src5.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src6.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src7.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src8.obj build\debug\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src9.obj build\debug\mongo\scripting\deadline_monitor.obj build\debug\mongo\scripting\deadline_monitor_gen.obj build\debug\mongo\scripting\dbdirectclient_factory.obj build\debug\mongo\scripting\engine.obj build\debug\mongo\scripting\jsexception.obj build\debug\mongo\scripting\utils.obj build\debug\mongo\shell\mongo.obj build\debug\mongo\scripting\bson_template_evaluator.obj build\debug\mongo\crypto\aead_encryption.obj build\debug\mongo\db\matcher\expression.obj build\debug\mongo\db\matcher\expression_algo.obj build\debug\mongo\db\matcher\expression_array.obj build\debug\mongo\db\matcher\expression_expr.obj build\debug\mongo\db\matcher\expression_geo.obj build\debug\mongo\db\matcher\expression_internal_expr_eq.obj build\debug\mongo\db\matcher\expression_leaf.obj build\debug\mongo\db\matcher\expression_parser.obj build\debug\mongo\db\matcher\expression_text_base.obj build\debug\mongo\db\matcher\expression_text_noop.obj build\debug\mongo\db\matcher\expression_tree.obj build\debug\mongo\db\matcher\expression_where_base.obj build\debug\mongo\db\matcher\expression_where_noop.obj build\debug\mongo\db\matcher\expression_with_placeholder.obj build\debug\mongo\db\matcher\extensions_callback.obj build\debug\mongo\db\matcher\extensions_callback_noop.obj build\debug\mongo\db\matcher\match_details.obj build\debug\mongo\db\matcher\matchable.obj build\debug\mongo\db\matcher\matcher.obj build\debug\mongo\db\matcher\matcher_type_set.obj build\debug\mongo\db\matcher\rewrite_expr.obj build\debug\mongo\db\matcher\schema\encrypt_schema_types.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_all_elem_match_from_index.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_allowed_properties.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_cond.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_eq.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_fmod.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_match_array_index.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_num_array_items.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_num_properties.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_object_match.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_root_doc_eq.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_str_length.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_unique_items.obj build\debug\mongo\db\matcher\schema\expression_internal_schema_xor.obj build\debug\mongo\db\matcher\schema\json_pointer.obj build\debug\mongo\db\matcher\schema\json_schema_parser.obj build\debug\mongo\db\matcher\schema\encrypt_schema_gen.obj build\debug\mongo\db\query\query_knobs_gen.obj build\debug\mongo\db\pipeline\expression.obj build\debug\mongo\db\pipeline\expression_trigonometric.obj build\debug\mongo\util\summation.obj build\debug\mongo\util\regex_util.obj build\debug\mongo\db\pipeline\expression_context.obj build\debug\mongo\db\pipeline\variables.obj build\debug\mongo\db\query\collation\collator_factory_interface.obj build\debug\mongo\db\query\collation\collation_index_key.obj build\debug\mongo\db\query\collation\collation_spec.obj build\debug\mongo\db\query\collation\collator_interface.obj build\debug\mongo\db\pipeline\aggregation_request.obj build\debug\mongo\db\query\explain_options.obj build\debug\mongo\db\pipeline\document_source_change_stream_gen.obj build\debug\mongo\db\pipeline\document_source_list_sessions_gen.obj build\debug\mongo\db\pipeline\document_source_merge_gen.obj build\debug\mongo\db\pipeline\document_source_merge_modes_gen.obj build\debug\mongo\db\pipeline\document_source_replace_root_gen.obj build\debug\mongo\db\pipeline\exchange_spec_gen.obj build\debug\mongo\db\pipeline\value_gen.obj build\debug\mongo\db\pipeline\document_source_merge_spec.obj build\debug\mongo\db\pipeline\resume_token.obj build\debug\mongo\db\storage\key_string.obj build\debug\mongo\db\pipeline\dependencies.obj build\debug\mongo\db\pipeline\document.obj build\debug\mongo\db\pipeline\document_comparator.obj build\debug\mongo\db\pipeline\document_path_support.obj build\debug\mongo\db\pipeline\value.obj build\debug\mongo\db\pipeline\value_comparator.obj build\debug\mongo\util\intrusive_counter.obj build\debug\mongo\db\query\datetime\date_time_support.obj build\debug\third_party\shim_timelib.obj build\debug\third_party\timelib-2018.01\astro.obj build\debug\third_party\timelib-2018.01\dow.obj build\debug\third_party\timelib-2018.01\interval.obj build\debug\third_party\timelib-2018.01\parse_date.obj build\debug\third_party\timelib-2018.01\parse_iso_intervals.obj build\debug\third_party\timelib-2018.01\parse_tz.obj build\debug\third_party\timelib-2018.01\parse_zoneinfo.obj build\debug\third_party\timelib-2018.01\timelib.obj build\debug\third_party\timelib-2018.01\tm2unixtime.obj build\debug\third_party\timelib-2018.01\unixtime2tm.obj build\debug\mongo\db\pipeline\field_path.obj build\debug\mongo\db\matcher\path.obj build\debug\mongo\db\matcher\path_internal.obj build\debug\mongo\db\geo\geoparser.obj build\debug\mongo\db\geo\geometry_container.obj build\debug\mongo\db\geo\hash.obj build\debug\mongo\db\geo\shapes.obj build\debug\mongo\db\geo\big_polygon.obj build\debug\mongo\db\geo\r2_region_coverer.obj build\debug\third_party\s2\s1angle.obj build\debug\third_party\s2\s2.obj build\debug\third_party\s2\s2cellid.obj build\debug\third_party\s2\s2latlng.obj build\debug\third_party\s2\s1interval.obj build\debug\third_party\s2\s2cap.obj build\debug\third_party\s2\s2cell.obj build\debug\third_party\s2\s2cellunion.obj build\debug\third_party\s2\s2edgeindex.obj build\debug\third_party\s2\s2edgeutil.obj build\debug\third_party\s2\s2latlngrect.obj build\debug\third_party\s2\s2loop.obj build\debug\third_party\s2\s2pointregion.obj build\debug\third_party\s2\s2polygon.obj build\debug\third_party\s2\s2polygonbuilder.obj build\debug\third_party\s2\s2polyline.obj build\debug\third_party\s2\s2r2rect.obj build\debug\third_party\s2\s2region.obj build\debug\third_party\s2\s2regioncoverer.obj build\debug\third_party\s2\s2regionintersection.obj build\debug\third_party\s2\s2regionunion.obj build\debug\third_party\s2\util\math\mathutil.obj build\debug\third_party\s2\util\coding\coder.obj build\debug\third_party\s2\util\coding\varint.obj build\debug\third_party\s2\strings\split.obj build\debug\third_party\s2\strings\stringprintf.obj build\debug\third_party\s2\strings\strutil.obj build\debug\third_party\s2\base\int128.obj build\debug\third_party\s2\base\logging.obj build\debug\third_party\s2\base\stringprintf.obj build\debug\third_party\s2\base\strtoint.obj build\debug\mongo\db\fts\fts_query_noop.obj build\debug\mongo\crypto\symmetric_crypto.obj build\debug\mongo\crypto\symmetric_crypto_windows.obj build\debug\mongo\crypto\symmetric_key.obj build\debug\mongo\db\views\resolved_view.obj build\debug\mongo\db\storage\duplicate_key_error_info.obj build\debug\mongo\client\connection_string_connect.obj build\debug\mongo\client\mongo_uri_connect.obj build\debug\mongo\client\connpool.obj build\debug\mongo\client\dbclient_connection.obj build\debug\mongo\client\dbclient_rs.obj build\debug\mongo\client\global_conn_pool.obj build\debug\mongo\client\global_conn_pool_gen.obj build\debug\mongo\client\replica_set_change_notifier.obj build\debug\mongo\client\replica_set_monitor.obj build\debug\mongo\client\replica_set_monitor_manager.obj build\debug\mongo\executor\thread_pool_task_executor.obj build\debug\mongo\executor\network_interface_thread_pool.obj build\debug\mongo\executor\network_interface_factory.obj build\debug\mongo\executor\connection_pool_tl.obj build\debug\mongo\executor\network_interface_tl.obj build\debug\mongo\transport\transport_layer_manager.obj build\debug\mongo\transport\service_executor_adaptive.obj build\debug\mongo\transport\service_executor_reserved.obj build\debug\mongo\transport\service_executor_synchronous.obj build\debug\mongo\transport\service_executor_gen.obj build\debug\mongo\transport\transport_layer_asio.obj build\debug\mongo\base\system_error.obj build\debug\mongo\client\async_client.obj build\debug\mongo\transport\message_compressor_manager.obj build\debug\mongo\transport\message_compressor_metrics.obj build\debug\mongo\transport\message_compressor_registry.obj build\debug\mongo\transport\message_compressor_snappy.obj build\debug\mongo\transport\message_compressor_zlib.obj build\debug\mongo\transport\message_compressor_zstd.obj build\debug\third_party\shim_zstd.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\common\entropy_common.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\common\fse_decompress.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\common\threading.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\common\pool.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\common\zstd_common.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\common\error_private.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\common\xxhash.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\compress\hist.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\compress\fse_compress.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\compress\huf_compress.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_compress.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\compress\zstdmt_compress.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_fast.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_double_fast.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_lazy.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_opt.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_ldm.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\decompress\huf_decompress.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\decompress\zstd_decompress.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\dictBuilder\cover.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\dictBuilder\fastcover.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\dictBuilder\divsufsort.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\dictBuilder\zdict.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\deprecated\zbuff_common.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\deprecated\zbuff_compress.obj build\debug\third_party\zstandard-1.3.7\zstd\lib\deprecated\zbuff_decompress.obj build\debug\third_party\shim_zlib.obj build\debug\third_party\zlib-1.2.11\adler32.obj build\debug\third_party\zlib-1.2.11\crc32.obj build\debug\third_party\zlib-1.2.11\compress.obj build\debug\third_party\zlib-1.2.11\deflate.obj build\debug\third_party\zlib-1.2.11\infback.obj build\debug\third_party\zlib-1.2.11\inffast.obj build\debug\third_party\zlib-1.2.11\inflate.obj build\debug\third_party\zlib-1.2.11\inftrees.obj build\debug\third_party\zlib-1.2.11\trees.obj build\debug\third_party\zlib-1.2.11\uncompr.obj build\debug\third_party\zlib-1.2.11\zutil.obj build\debug\third_party\shim_snappy.obj build\debug\third_party\snappy-1.1.7\snappy-c.obj build\debug\third_party\snappy-1.1.7\snappy.obj build\debug\third_party\snappy-1.1.7\snappy-sinksource.obj build\debug\mongo\executor\network_interface.obj build\debug\mongo\executor\task_executor.obj build\debug\mongo\executor\connection_pool.obj build\debug\mongo\executor\egress_tag_closer_manager.obj build\debug\mongo\executor\connection_pool_stats.obj build\debug\mongo\client\dbclient_base.obj build\debug\mongo\client\dbclient_cursor.obj build\debug\mongo\client\index_spec.obj build\debug\mongo\util\net\private\ssl_expiration.obj build\debug\mongo\util\net\ssl_manager.obj build\debug\mongo\util\net\ssl_parameters.obj build\debug\mongo\util\net\ssl_manager_windows.obj build\debug\mongo\util\net\ssl_stream.obj build\debug\mongo\util\net\ssl_parameters_gen.obj build\debug\third_party\shim_asio.obj build\debug\third_party\asio-master\asio\src\asio.obj build\debug\mongo\util\net\ssl_types.obj build\debug\mongo\util\net\ssl_options.obj build\debug\mongo\util\net\private\socket_poll.obj build\debug\mongo\util\net\sock.obj build\debug\mongo\util\background.obj build\debug\mongo\db\commands\server_status.obj build\debug\mongo\db\stats\counters.obj build\debug\mongo\db\commands.obj build\debug\mongo\db\audit.obj build\debug\mongo\db\query\count_command_as_aggregation_command.obj build\debug\mongo\db\query\count_request.obj build\debug\mongo\db\query\cursor_request.obj build\debug\mongo\db\query\cursor_response.obj build\debug\mongo\db\query\find_and_modify_request.obj build\debug\mongo\db\query\getmore_request.obj build\debug\mongo\db\query\killcursors_request.obj build\debug\mongo\db\query\killcursors_response.obj build\debug\mongo\db\query\view_response_formatter.obj build\debug\mongo\db\query\count_command_gen.obj build\debug\mongo\rpc\factory.obj build\debug\mongo\rpc\object_check.obj build\debug\mongo\rpc\legacy_request.obj build\debug\mongo\rpc\legacy_request_builder.obj build\debug\mongo\rpc\legacy_reply.obj build\debug\mongo\rpc\legacy_reply_builder.obj build\debug\mongo\rpc\reply_builder_interface.obj build\debug\mongo\rpc\object_check_gen.obj build\debug\mongo\s\catalog\mongo_version_range.obj build\debug\mongo\s\catalog\type_changelog.obj build\debug\mongo\s\catalog\type_chunk.obj build\debug\mongo\s\catalog\type_collection.obj build\debug\mongo\s\catalog\type_config_version.obj build\debug\mongo\s\catalog\type_database.obj build\debug\mongo\s\catalog\type_lockpings.obj build\debug\mongo\s\catalog\type_locks.obj build\debug\mongo\s\catalog\type_mongos.obj build\debug\mongo\s\catalog\type_shard_collection.obj build\debug\mongo\s\catalog\type_shard_database.obj build\debug\mongo\s\catalog\type_shard.obj build\debug\mongo\s\catalog\type_tags.obj build\debug\mongo\s\request_types\add_shard_request_type.obj build\debug\mongo\s\request_types\add_shard_to_zone_request_type.obj build\debug\mongo\s\request_types\balance_chunk_request_type.obj build\debug\mongo\s\request_types\commit_chunk_migration_request_type.obj build\debug\mongo\s\request_types\merge_chunk_request_type.obj build\debug\mongo\s\request_types\migration_secondary_throttle_options.obj build\debug\mongo\s\request_types\move_chunk_request.obj build\debug\mongo\s\request_types\remove_shard_from_zone_request_type.obj build\debug\mongo\s\request_types\set_shard_version_request.obj build\debug\mongo\s\request_types\split_chunk_request_type.obj build\debug\mongo\s\request_types\update_zone_key_range_request_type.obj build\debug\mongo\s\cannot_implicitly_create_collection_info.obj build\debug\mongo\s\chunk_version.obj build\debug\mongo\s\database_version_helpers.obj build\debug\mongo\s\shard_id.obj build\debug\mongo\s\stale_exception.obj build\debug\mongo\s\would_change_owning_shard_exception.obj build\debug\mongo\s\catalog\type_chunk_base_gen.obj build\debug\mongo\s\chunk_version_gen.obj build\debug\mongo\s\database_version_gen.obj build\debug\mongo\s\request_types\clone_catalog_data_gen.obj build\debug\mongo\s\request_types\clear_jumbo_flag_gen.obj build\debug\mongo\s\request_types\create_collection_gen.obj build\debug\mongo\s\request_types\create_database_gen.obj build\debug\mongo\s\request_types\flush_database_cache_updates_gen.obj build\debug\mongo\s\request_types\flush_routing_table_cache_updates_gen.obj build\debug\mongo\s\request_types\get_database_version_gen.obj build\debug\mongo\s\request_types\move_primary_gen.obj build\debug\mongo\s\request_types\shard_collection_gen.obj build\debug\mongo\s\request_types\clone_collection_options_from_primary_shard_gen.obj build\debug\mongo\s\request_types\wait_for_fail_point_gen.obj build\debug\mongo\rpc\message.obj build\debug\mongo\rpc\op_msg.obj build\debug\mongo\rpc\protocol.obj build\debug\third_party\wiredtiger\src\checksum\software\checksum.obj build\debug\third_party\wiredtiger\src\checksum\x86\crc32-x86.obj build\debug\third_party\wiredtiger\src\checksum\x86\crc32-x86-alt.obj build\debug\mongo\db\wire_version.obj build\debug\mongo\db\bson\dotted_path_support.obj build\debug\mongo\db\query\query_request.obj build\debug\mongo\db\query\tailable_mode.obj build\debug\mongo\db\query\tailable_mode_gen.obj build\debug\mongo\db\repl\read_concern_args.obj build\debug\mongo\db\catalog\collection_catalog.obj build\debug\mongo\db\catalog\collection.obj build\debug\mongo\db\ops\write_ops_parsers.obj build\debug\mongo\db\ops\write_ops_gen.obj build\debug\mongo\db\query\hint_parser.obj build\debug\mongo\db\query\hint_gen.obj build\debug\mongo\db\pipeline\runtime_constants_gen.obj build\debug\mongo\db\dbmessage.obj build\debug\mongo\client\connection_string.obj build\debug\mongo\client\mongo_uri.obj build\debug\mongo\util\dns_query.obj build\debug\mongo\client\query.obj build\debug\mongo\client\authenticate.obj build\debug\mongo\client\native_sasl_client_session.obj build\debug\mongo\client\sasl_client_authenticate.obj build\debug\mongo\client\sasl_client_authenticate_impl.obj build\debug\mongo\client\sasl_client_conversation.obj build\debug\mongo\client\sasl_client_session.obj build\debug\mongo\client\sasl_plain_client_conversation.obj build\debug\mongo\client\sasl_scram_client_conversation.obj build\debug\mongo\util\md5.obj build\debug\mongo\util\password_digest.obj build\debug\mongo\util\icu.obj build\debug\mongo\util\icu_init.obj build\debug\third_party\shim_icu.obj build\debug\third_party\icu4c-57.1\source\i18n\affixpatternparser.obj build\debug\third_party\icu4c-57.1\source\i18n\alphaindex.obj build\debug\third_party\icu4c-57.1\source\i18n\anytrans.obj build\debug\third_party\icu4c-57.1\source\i18n\astro.obj build\debug\third_party\icu4c-57.1\source\i18n\basictz.obj build\debug\third_party\icu4c-57.1\source\i18n\bocsu.obj build\debug\third_party\icu4c-57.1\source\i18n\brktrans.obj build\debug\third_party\icu4c-57.1\source\i18n\buddhcal.obj build\debug\third_party\icu4c-57.1\source\i18n\calendar.obj build\debug\third_party\icu4c-57.1\source\i18n\casetrn.obj build\debug\third_party\icu4c-57.1\source\i18n\cecal.obj build\debug\third_party\icu4c-57.1\source\i18n\chnsecal.obj build\debug\third_party\icu4c-57.1\source\i18n\choicfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\coleitr.obj build\debug\third_party\icu4c-57.1\source\i18n\coll.obj build\debug\third_party\icu4c-57.1\source\i18n\collation.obj build\debug\third_party\icu4c-57.1\source\i18n\collationbuilder.obj build\debug\third_party\icu4c-57.1\source\i18n\collationcompare.obj build\debug\third_party\icu4c-57.1\source\i18n\collationdata.obj build\debug\third_party\icu4c-57.1\source\i18n\collationdatabuilder.obj build\debug\third_party\icu4c-57.1\source\i18n\collationdatareader.obj build\debug\third_party\icu4c-57.1\source\i18n\collationdatawriter.obj build\debug\third_party\icu4c-57.1\source\i18n\collationfastlatin.obj build\debug\third_party\icu4c-57.1\source\i18n\collationfastlatinbuilder.obj build\debug\third_party\icu4c-57.1\source\i18n\collationfcd.obj build\debug\third_party\icu4c-57.1\source\i18n\collationiterator.obj build\debug\third_party\icu4c-57.1\source\i18n\collationkeys.obj build\debug\third_party\icu4c-57.1\source\i18n\collationroot.obj build\debug\third_party\icu4c-57.1\source\i18n\collationrootelements.obj build\debug\third_party\icu4c-57.1\source\i18n\collationruleparser.obj build\debug\third_party\icu4c-57.1\source\i18n\collationsets.obj build\debug\third_party\icu4c-57.1\source\i18n\collationsettings.obj build\debug\third_party\icu4c-57.1\source\i18n\collationtailoring.obj build\debug\third_party\icu4c-57.1\source\i18n\collationweights.obj build\debug\third_party\icu4c-57.1\source\i18n\compactdecimalformat.obj build\debug\third_party\icu4c-57.1\source\i18n\coptccal.obj build\debug\third_party\icu4c-57.1\source\i18n\cpdtrans.obj build\debug\third_party\icu4c-57.1\source\i18n\csdetect.obj build\debug\third_party\icu4c-57.1\source\i18n\csmatch.obj build\debug\third_party\icu4c-57.1\source\i18n\csr2022.obj build\debug\third_party\icu4c-57.1\source\i18n\csrecog.obj build\debug\third_party\icu4c-57.1\source\i18n\csrmbcs.obj build\debug\third_party\icu4c-57.1\source\i18n\csrsbcs.obj build\debug\third_party\icu4c-57.1\source\i18n\csrucode.obj build\debug\third_party\icu4c-57.1\source\i18n\csrutf8.obj build\debug\third_party\icu4c-57.1\source\i18n\curramt.obj build\debug\third_party\icu4c-57.1\source\i18n\currfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\currpinf.obj build\debug\third_party\icu4c-57.1\source\i18n\currunit.obj build\debug\third_party\icu4c-57.1\source\i18n\dangical.obj build\debug\third_party\icu4c-57.1\source\i18n\datefmt.obj build\debug\third_party\icu4c-57.1\source\i18n\dayperiodrules.obj build\debug\third_party\icu4c-57.1\source\i18n\dcfmtsym.obj build\debug\third_party\icu4c-57.1\source\i18n\decContext.obj build\debug\third_party\icu4c-57.1\source\i18n\decNumber.obj build\debug\third_party\icu4c-57.1\source\i18n\decfmtst.obj build\debug\third_party\icu4c-57.1\source\i18n\decimalformatpattern.obj build\debug\third_party\icu4c-57.1\source\i18n\decimfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\decimfmtimpl.obj build\debug\third_party\icu4c-57.1\source\i18n\digitaffix.obj build\debug\third_party\icu4c-57.1\source\i18n\digitaffixesandpadding.obj build\debug\third_party\icu4c-57.1\source\i18n\digitformatter.obj build\debug\third_party\icu4c-57.1\source\i18n\digitgrouping.obj build\debug\third_party\icu4c-57.1\source\i18n\digitinterval.obj build\debug\third_party\icu4c-57.1\source\i18n\digitlst.obj build\debug\third_party\icu4c-57.1\source\i18n\dtfmtsym.obj build\debug\third_party\icu4c-57.1\source\i18n\dtitvfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\dtitvinf.obj build\debug\third_party\icu4c-57.1\source\i18n\dtptngen.obj build\debug\third_party\icu4c-57.1\source\i18n\dtrule.obj build\debug\third_party\icu4c-57.1\source\i18n\esctrn.obj build\debug\third_party\icu4c-57.1\source\i18n\ethpccal.obj build\debug\third_party\icu4c-57.1\source\i18n\fmtable.obj build\debug\third_party\icu4c-57.1\source\i18n\fmtable_cnv.obj build\debug\third_party\icu4c-57.1\source\i18n\format.obj build\debug\third_party\icu4c-57.1\source\i18n\fphdlimp.obj build\debug\third_party\icu4c-57.1\source\i18n\fpositer.obj build\debug\third_party\icu4c-57.1\source\i18n\funcrepl.obj build\debug\third_party\icu4c-57.1\source\i18n\gender.obj build\debug\third_party\icu4c-57.1\source\i18n\gregocal.obj build\debug\third_party\icu4c-57.1\source\i18n\gregoimp.obj build\debug\third_party\icu4c-57.1\source\i18n\hebrwcal.obj build\debug\third_party\icu4c-57.1\source\i18n\identifier_info.obj build\debug\third_party\icu4c-57.1\source\i18n\indiancal.obj build\debug\third_party\icu4c-57.1\source\i18n\inputext.obj build\debug\third_party\icu4c-57.1\source\i18n\islamcal.obj build\debug\third_party\icu4c-57.1\source\i18n\japancal.obj build\debug\third_party\icu4c-57.1\source\i18n\measfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\measunit.obj build\debug\third_party\icu4c-57.1\source\i18n\measure.obj build\debug\third_party\icu4c-57.1\source\i18n\msgfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\name2uni.obj build\debug\third_party\icu4c-57.1\source\i18n\nfrs.obj build\debug\third_party\icu4c-57.1\source\i18n\nfrule.obj build\debug\third_party\icu4c-57.1\source\i18n\nfsubs.obj build\debug\third_party\icu4c-57.1\source\i18n\nortrans.obj build\debug\third_party\icu4c-57.1\source\i18n\nultrans.obj build\debug\third_party\icu4c-57.1\source\i18n\numfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\numsys.obj build\debug\third_party\icu4c-57.1\source\i18n\olsontz.obj build\debug\third_party\icu4c-57.1\source\i18n\persncal.obj build\debug\third_party\icu4c-57.1\source\i18n\pluralaffix.obj build\debug\third_party\icu4c-57.1\source\i18n\plurfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\plurrule.obj build\debug\third_party\icu4c-57.1\source\i18n\precision.obj build\debug\third_party\icu4c-57.1\source\i18n\quant.obj build\debug\third_party\icu4c-57.1\source\i18n\quantityformatter.obj build\debug\third_party\icu4c-57.1\source\i18n\rbnf.obj build\debug\third_party\icu4c-57.1\source\i18n\rbt.obj build\debug\third_party\icu4c-57.1\source\i18n\rbt_data.obj build\debug\third_party\icu4c-57.1\source\i18n\rbt_pars.obj build\debug\third_party\icu4c-57.1\source\i18n\rbt_rule.obj build\debug\third_party\icu4c-57.1\source\i18n\rbt_set.obj build\debug\third_party\icu4c-57.1\source\i18n\rbtz.obj build\debug\third_party\icu4c-57.1\source\i18n\regexcmp.obj build\debug\third_party\icu4c-57.1\source\i18n\regeximp.obj build\debug\third_party\icu4c-57.1\source\i18n\regexst.obj build\debug\third_party\icu4c-57.1\source\i18n\regextxt.obj build\debug\third_party\icu4c-57.1\source\i18n\region.obj build\debug\third_party\icu4c-57.1\source\i18n\reldatefmt.obj build\debug\third_party\icu4c-57.1\source\i18n\reldtfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\rematch.obj build\debug\third_party\icu4c-57.1\source\i18n\remtrans.obj build\debug\third_party\icu4c-57.1\source\i18n\repattrn.obj build\debug\third_party\icu4c-57.1\source\i18n\rulebasedcollator.obj build\debug\third_party\icu4c-57.1\source\i18n\scientificnumberformatter.obj build\debug\third_party\icu4c-57.1\source\i18n\scriptset.obj build\debug\third_party\icu4c-57.1\source\i18n\search.obj build\debug\third_party\icu4c-57.1\source\i18n\selfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\sharedbreakiterator.obj build\debug\third_party\icu4c-57.1\source\i18n\simpletz.obj build\debug\third_party\icu4c-57.1\source\i18n\smallintformatter.obj build\debug\third_party\icu4c-57.1\source\i18n\smpdtfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\smpdtfst.obj build\debug\third_party\icu4c-57.1\source\i18n\sortkey.obj build\debug\third_party\icu4c-57.1\source\i18n\standardplural.obj build\debug\third_party\icu4c-57.1\source\i18n\strmatch.obj build\debug\third_party\icu4c-57.1\source\i18n\strrepl.obj build\debug\third_party\icu4c-57.1\source\i18n\stsearch.obj build\debug\third_party\icu4c-57.1\source\i18n\taiwncal.obj build\debug\third_party\icu4c-57.1\source\i18n\timezone.obj build\debug\third_party\icu4c-57.1\source\i18n\titletrn.obj build\debug\third_party\icu4c-57.1\source\i18n\tmunit.obj build\debug\third_party\icu4c-57.1\source\i18n\tmutamt.obj build\debug\third_party\icu4c-57.1\source\i18n\tmutfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\tolowtrn.obj build\debug\third_party\icu4c-57.1\source\i18n\toupptrn.obj build\debug\third_party\icu4c-57.1\source\i18n\translit.obj build\debug\third_party\icu4c-57.1\source\i18n\transreg.obj build\debug\third_party\icu4c-57.1\source\i18n\tridpars.obj build\debug\third_party\icu4c-57.1\source\i18n\tzfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\tzgnames.obj build\debug\third_party\icu4c-57.1\source\i18n\tznames.obj build\debug\third_party\icu4c-57.1\source\i18n\tznames_impl.obj build\debug\third_party\icu4c-57.1\source\i18n\tzrule.obj build\debug\third_party\icu4c-57.1\source\i18n\tztrans.obj build\debug\third_party\icu4c-57.1\source\i18n\ucal.obj build\debug\third_party\icu4c-57.1\source\i18n\ucln_in.obj build\debug\third_party\icu4c-57.1\source\i18n\ucol.obj build\debug\third_party\icu4c-57.1\source\i18n\ucol_res.obj build\debug\third_party\icu4c-57.1\source\i18n\ucol_sit.obj build\debug\third_party\icu4c-57.1\source\i18n\ucoleitr.obj build\debug\third_party\icu4c-57.1\source\i18n\ucsdet.obj build\debug\third_party\icu4c-57.1\source\i18n\udat.obj build\debug\third_party\icu4c-57.1\source\i18n\udateintervalformat.obj build\debug\third_party\icu4c-57.1\source\i18n\udatpg.obj build\debug\third_party\icu4c-57.1\source\i18n\ufieldpositer.obj build\debug\third_party\icu4c-57.1\source\i18n\uitercollationiterator.obj build\debug\third_party\icu4c-57.1\source\i18n\ulocdata.obj build\debug\third_party\icu4c-57.1\source\i18n\umsg.obj build\debug\third_party\icu4c-57.1\source\i18n\unesctrn.obj build\debug\third_party\icu4c-57.1\source\i18n\uni2name.obj build\debug\third_party\icu4c-57.1\source\i18n\unum.obj build\debug\third_party\icu4c-57.1\source\i18n\unumsys.obj build\debug\third_party\icu4c-57.1\source\i18n\upluralrules.obj build\debug\third_party\icu4c-57.1\source\i18n\uregex.obj build\debug\third_party\icu4c-57.1\source\i18n\uregexc.obj build\debug\third_party\icu4c-57.1\source\i18n\uregion.obj build\debug\third_party\icu4c-57.1\source\i18n\usearch.obj build\debug\third_party\icu4c-57.1\source\i18n\uspoof.obj build\debug\third_party\icu4c-57.1\source\i18n\uspoof_build.obj build\debug\third_party\icu4c-57.1\source\i18n\uspoof_conf.obj build\debug\third_party\icu4c-57.1\source\i18n\uspoof_impl.obj build\debug\third_party\icu4c-57.1\source\i18n\uspoof_wsconf.obj build\debug\third_party\icu4c-57.1\source\i18n\utf16collationiterator.obj build\debug\third_party\icu4c-57.1\source\i18n\utf8collationiterator.obj build\debug\third_party\icu4c-57.1\source\i18n\utmscale.obj build\debug\third_party\icu4c-57.1\source\i18n\utrans.obj build\debug\third_party\icu4c-57.1\source\i18n\valueformatter.obj build\debug\third_party\icu4c-57.1\source\i18n\visibledigits.obj build\debug\third_party\icu4c-57.1\source\i18n\vtzone.obj build\debug\third_party\icu4c-57.1\source\i18n\vzone.obj build\debug\third_party\icu4c-57.1\source\i18n\windtfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\winnmfmt.obj build\debug\third_party\icu4c-57.1\source\i18n\wintzimpl.obj build\debug\third_party\icu4c-57.1\source\i18n\zonemeta.obj build\debug\third_party\icu4c-57.1\source\i18n\zrule.obj build\debug\third_party\icu4c-57.1\source\i18n\ztrans.obj build\debug\third_party\icu4c-57.1\source\common\appendable.obj build\debug\third_party\icu4c-57.1\source\common\bmpset.obj build\debug\third_party\icu4c-57.1\source\common\brkeng.obj build\debug\third_party\icu4c-57.1\source\common\brkiter.obj build\debug\third_party\icu4c-57.1\source\common\bytestream.obj build\debug\third_party\icu4c-57.1\source\common\bytestrie.obj build\debug\third_party\icu4c-57.1\source\common\bytestriebuilder.obj build\debug\third_party\icu4c-57.1\source\common\bytestrieiterator.obj build\debug\third_party\icu4c-57.1\source\common\caniter.obj build\debug\third_party\icu4c-57.1\source\common\chariter.obj build\debug\third_party\icu4c-57.1\source\common\charstr.obj build\debug\third_party\icu4c-57.1\source\common\cmemory.obj build\debug\third_party\icu4c-57.1\source\common\cstr.obj build\debug\third_party\icu4c-57.1\source\common\cstring.obj build\debug\third_party\icu4c-57.1\source\common\cwchar.obj build\debug\third_party\icu4c-57.1\source\common\dictbe.obj build\debug\third_party\icu4c-57.1\source\common\dictionarydata.obj build\debug\third_party\icu4c-57.1\source\common\dtintrv.obj build\debug\third_party\icu4c-57.1\source\common\errorcode.obj build\debug\third_party\icu4c-57.1\source\common\filteredbrk.obj build\debug\third_party\icu4c-57.1\source\common\filterednormalizer2.obj build\debug\third_party\icu4c-57.1\source\common\icudataver.obj build\debug\third_party\icu4c-57.1\source\common\icuplug.obj build\debug\third_party\icu4c-57.1\source\common\listformatter.obj build\debug\third_party\icu4c-57.1\source\common\loadednormalizer2impl.obj build\debug\third_party\icu4c-57.1\source\common\locavailable.obj build\debug\third_party\icu4c-57.1\source\common\locbased.obj build\debug\third_party\icu4c-57.1\source\common\locdispnames.obj build\debug\third_party\icu4c-57.1\source\common\locdspnm.obj build\debug\third_party\icu4c-57.1\source\common\locid.obj build\debug\third_party\icu4c-57.1\source\common\loclikely.obj build\debug\third_party\icu4c-57.1\source\common\locmap.obj build\debug\third_party\icu4c-57.1\source\common\locresdata.obj build\debug\third_party\icu4c-57.1\source\common\locutil.obj build\debug\third_party\icu4c-57.1\source\common\messagepattern.obj build\debug\third_party\icu4c-57.1\source\common\normalizer2.obj build\debug\third_party\icu4c-57.1\source\common\normalizer2impl.obj build\debug\third_party\icu4c-57.1\source\common\normlzr.obj build\debug\third_party\icu4c-57.1\source\common\parsepos.obj build\debug\third_party\icu4c-57.1\source\common\patternprops.obj build\debug\third_party\icu4c-57.1\source\common\pluralmap.obj build\debug\third_party\icu4c-57.1\source\common\propname.obj build\debug\third_party\icu4c-57.1\source\common\propsvec.obj build\debug\third_party\icu4c-57.1\source\common\punycode.obj build\debug\third_party\icu4c-57.1\source\common\putil.obj build\debug\third_party\icu4c-57.1\source\common\rbbi.obj build\debug\third_party\icu4c-57.1\source\common\rbbidata.obj build\debug\third_party\icu4c-57.1\source\common\rbbinode.obj build\debug\third_party\icu4c-57.1\source\common\rbbirb.obj build\debug\third_party\icu4c-57.1\source\common\rbbiscan.obj build\debug\third_party\icu4c-57.1\source\common\rbbisetb.obj build\debug\third_party\icu4c-57.1\source\common\rbbistbl.obj build\debug\third_party\icu4c-57.1\source\common\rbbitblb.obj build\debug\third_party\icu4c-57.1\source\common\resbund.obj build\debug\third_party\icu4c-57.1\source\common\resbund_cnv.obj build\debug\third_party\icu4c-57.1\source\common\resource.obj build\debug\third_party\icu4c-57.1\source\common\ruleiter.obj build\debug\third_party\icu4c-57.1\source\common\schriter.obj build\debug\third_party\icu4c-57.1\source\common\serv.obj build\debug\third_party\icu4c-57.1\source\common\servlk.obj build\debug\third_party\icu4c-57.1\source\common\servlkf.obj build\debug\third_party\icu4c-57.1\source\common\servls.obj build\debug\third_party\icu4c-57.1\source\common\servnotf.obj build\debug\third_party\icu4c-57.1\source\common\servrbf.obj build\debug\third_party\icu4c-57.1\source\common\servslkf.obj build\debug\third_party\icu4c-57.1\source\common\sharedobject.obj build\debug\third_party\icu4c-57.1\source\common\simpleformatter.obj build\debug\third_party\icu4c-57.1\source\common\stringpiece.obj build\debug\third_party\icu4c-57.1\source\common\stringtriebuilder.obj build\debug\third_party\icu4c-57.1\source\common\uarrsort.obj build\debug\third_party\icu4c-57.1\source\common\ubidi.obj build\debug\third_party\icu4c-57.1\source\common\ubidi_props.obj build\debug\third_party\icu4c-57.1\source\common\ubidiln.obj build\debug\third_party\icu4c-57.1\source\common\ubidiwrt.obj build\debug\third_party\icu4c-57.1\source\common\ubrk.obj build\debug\third_party\icu4c-57.1\source\common\ucase.obj build\debug\third_party\icu4c-57.1\source\common\ucasemap.obj build\debug\third_party\icu4c-57.1\source\common\ucasemap_titlecase_brkiter.obj build\debug\third_party\icu4c-57.1\source\common\ucat.obj build\debug\third_party\icu4c-57.1\source\common\uchar.obj build\debug\third_party\icu4c-57.1\source\common\ucharstrie.obj build\debug\third_party\icu4c-57.1\source\common\ucharstriebuilder.obj build\debug\third_party\icu4c-57.1\source\common\ucharstrieiterator.obj build\debug\third_party\icu4c-57.1\source\common\uchriter.obj build\debug\third_party\icu4c-57.1\source\common\ucln_cmn.obj build\debug\third_party\icu4c-57.1\source\common\ucmndata.obj build\debug\third_party\icu4c-57.1\source\common\ucnv.obj build\debug\third_party\icu4c-57.1\source\common\ucnv2022.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_bld.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_cb.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_cnv.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_ct.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_err.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_ext.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_io.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_lmb.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_set.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_u16.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_u32.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_u7.obj build\debug\third_party\icu4c-57.1\source\common\ucnv_u8.obj build\debug\third_party\icu4c-57.1\source\common\ucnvbocu.obj build\debug\third_party\icu4c-57.1\source\common\ucnvdisp.obj build\debug\third_party\icu4c-57.1\source\common\ucnvhz.obj build\debug\third_party\icu4c-57.1\source\common\ucnvisci.obj build\debug\third_party\icu4c-57.1\source\common\ucnvlat1.obj build\debug\third_party\icu4c-57.1\source\common\ucnvmbcs.obj build\debug\third_party\icu4c-57.1\source\common\ucnvscsu.obj build\debug\third_party\icu4c-57.1\source\common\ucnvsel.obj build\debug\third_party\icu4c-57.1\source\common\ucol_swp.obj build\debug\third_party\icu4c-57.1\source\common\ucurr.obj build\debug\third_party\icu4c-57.1\source\common\udata.obj build\debug\third_party\icu4c-57.1\source\common\udatamem.obj build\debug\third_party\icu4c-57.1\source\common\udataswp.obj build\debug\third_party\icu4c-57.1\source\common\uenum.obj build\debug\third_party\icu4c-57.1\source\common\uhash.obj build\debug\third_party\icu4c-57.1\source\common\uhash_us.obj build\debug\third_party\icu4c-57.1\source\common\uidna.obj build\debug\third_party\icu4c-57.1\source\common\uinit.obj build\debug\third_party\icu4c-57.1\source\common\uinvchar.obj build\debug\third_party\icu4c-57.1\source\common\uiter.obj build\debug\third_party\icu4c-57.1\source\common\ulist.obj build\debug\third_party\icu4c-57.1\source\common\ulistformatter.obj build\debug\third_party\icu4c-57.1\source\common\uloc.obj build\debug\third_party\icu4c-57.1\source\common\uloc_keytype.obj build\debug\third_party\icu4c-57.1\source\common\uloc_tag.obj build\debug\third_party\icu4c-57.1\source\common\umapfile.obj build\debug\third_party\icu4c-57.1\source\common\umath.obj build\debug\third_party\icu4c-57.1\source\common\umutex.obj build\debug\third_party\icu4c-57.1\source\common\unames.obj build\debug\third_party\icu4c-57.1\source\common\unifiedcache.obj build\debug\third_party\icu4c-57.1\source\common\unifilt.obj build\debug\third_party\icu4c-57.1\source\common\unifunct.obj build\debug\third_party\icu4c-57.1\source\common\uniset.obj build\debug\third_party\icu4c-57.1\source\common\uniset_closure.obj build\debug\third_party\icu4c-57.1\source\common\uniset_props.obj build\debug\third_party\icu4c-57.1\source\common\unisetspan.obj build\debug\third_party\icu4c-57.1\source\common\unistr.obj build\debug\third_party\icu4c-57.1\source\common\unistr_case.obj build\debug\third_party\icu4c-57.1\source\common\unistr_case_locale.obj build\debug\third_party\icu4c-57.1\source\common\unistr_cnv.obj build\debug\third_party\icu4c-57.1\source\common\unistr_props.obj build\debug\third_party\icu4c-57.1\source\common\unistr_titlecase_brkiter.obj build\debug\third_party\icu4c-57.1\source\common\unorm.obj build\debug\third_party\icu4c-57.1\source\common\unormcmp.obj build\debug\third_party\icu4c-57.1\source\common\uobject.obj build\debug\third_party\icu4c-57.1\source\common\uprops.obj build\debug\third_party\icu4c-57.1\source\common\ures_cnv.obj build\debug\third_party\icu4c-57.1\source\common\uresbund.obj build\debug\third_party\icu4c-57.1\source\common\uresdata.obj build\debug\third_party\icu4c-57.1\source\common\usc_impl.obj build\debug\third_party\icu4c-57.1\source\common\uscript.obj build\debug\third_party\icu4c-57.1\source\common\uscript_props.obj build\debug\third_party\icu4c-57.1\source\common\uset.obj build\debug\third_party\icu4c-57.1\source\common\uset_props.obj build\debug\third_party\icu4c-57.1\source\common\usetiter.obj build\debug\third_party\icu4c-57.1\source\common\ushape.obj build\debug\third_party\icu4c-57.1\source\common\usprep.obj build\debug\third_party\icu4c-57.1\source\common\ustack.obj build\debug\third_party\icu4c-57.1\source\common\ustr_cnv.obj build\debug\third_party\icu4c-57.1\source\common\ustr_titlecase_brkiter.obj build\debug\third_party\icu4c-57.1\source\common\ustr_wcs.obj build\debug\third_party\icu4c-57.1\source\common\ustrcase.obj build\debug\third_party\icu4c-57.1\source\common\ustrcase_locale.obj build\debug\third_party\icu4c-57.1\source\common\ustrenum.obj build\debug\third_party\icu4c-57.1\source\common\ustrfmt.obj build\debug\third_party\icu4c-57.1\source\common\ustring.obj build\debug\third_party\icu4c-57.1\source\common\ustrtrns.obj build\debug\third_party\icu4c-57.1\source\common\utext.obj build\debug\third_party\icu4c-57.1\source\common\utf_impl.obj build\debug\third_party\icu4c-57.1\source\common\util.obj build\debug\third_party\icu4c-57.1\source\common\util_props.obj build\debug\third_party\icu4c-57.1\source\common\utrace.obj build\debug\third_party\icu4c-57.1\source\common\utrie.obj build\debug\third_party\icu4c-57.1\source\common\utrie2.obj build\debug\third_party\icu4c-57.1\source\common\utrie2_builder.obj build\debug\third_party\icu4c-57.1\source\common\uts46.obj build\debug\third_party\icu4c-57.1\source\common\utypes.obj build\debug\third_party\icu4c-57.1\source\common\uvector.obj build\debug\third_party\icu4c-57.1\source\common\uvectr32.obj build\debug\third_party\icu4c-57.1\source\common\uvectr64.obj build\debug\third_party\icu4c-57.1\source\common\wintz.obj build\debug\third_party\icu4c-57.1\source\stubdata\stubdata.obj build\debug\mongo\rpc\get_status_from_command_result.obj build\debug\mongo\rpc\write_concern_error_detail.obj build\debug\mongo\executor\remote_command_request.obj build\debug\mongo\executor\remote_command_response.obj build\debug\mongo\rpc\metadata.obj build\debug\mongo\rpc\metadata\config_server_metadata.obj build\debug\mongo\rpc\metadata\egress_metadata_hook_list.obj build\debug\mongo\rpc\metadata\logical_time_metadata.obj build\debug\mongo\rpc\metadata\sharding_metadata.obj build\debug\mongo\rpc\metadata\repl_set_metadata.obj build\debug\mongo\rpc\metadata\oplog_query_metadata.obj build\debug\mongo\rpc\metadata\tracking_metadata.obj build\debug\mongo\rpc\metadata\client_metadata.obj build\debug\mongo\rpc\metadata\client_metadata_ismaster.obj build\debug\mongo\transport\service_entry_point_utils.obj build\debug\mongo\transport\session.obj build\debug\mongo\transport\transport_layer.obj build\debug\mongo\s\is_mongos.obj build\debug\mongo\db\logical_time_validator.obj build\debug\mongo\db\signed_logical_time.obj build\debug\mongo\db\keys_collection_manager_gen.obj build\debug\mongo\db\keys_collection_manager.obj build\debug\mongo\db\keys_collection_cache.obj build\debug\mongo\db\key_generator.obj build\debug\mongo\db\repl\repl_client_info.obj build\debug\mongo\db\repl\replication_coordinator.obj build\debug\mongo\db\repl\replication_coordinator_noop.obj build\debug\mongo\db\repl\replication_consistency_markers.obj build\debug\mongo\db\repl\replication_process.obj build\debug\mongo\db\repl\storage_interface.obj build\debug\mongo\db\repl\rollback_gen.obj build\debug\mongo\db\namespace_string.obj build\debug\mongo\db\repl\bson_extract_optime.obj build\debug\mongo\db\repl\optime.obj build\debug\mongo\db\keys_collection_client_sharded.obj build\debug\mongo\s\catalog\sharding_catalog_client.obj build\debug\mongo\db\logical_clock.obj build\debug\mongo\db\logical_clock_gen.obj build\debug\mongo\db\global_settings.obj build\debug\mongo\db\repl\repl_settings.obj build\debug\mongo\db\repl\repl_settings_gen.obj build\debug\mongo\db\keys_collection_document.obj build\debug\mongo\db\time_proof_service.obj build\debug\mongo\db\logical_time.obj build\debug\mongo\db\operation_time_tracker.obj build\debug\mongo\db\auth\action_set.obj build\debug\mongo\db\auth\action_type.obj build\debug\mongo\db\auth\impersonation_session.obj build\debug\mongo\db\auth\privilege.obj build\debug\mongo\db\auth\privilege_parser.obj build\debug\mongo\db\auth\resource_pattern.obj build\debug\mongo\db\auth\user_management_commands_parser.obj build\debug\mongo\rpc\metadata\impersonated_user_metadata.obj build\debug\mongo\rpc\metadata\impersonated_user_metadata_gen.obj build\debug\mongo\db\server_options_helpers.obj build\debug\mongo\db\server_options_helpers_gen.obj build\debug\mongo\util\cmdline_utils\censor_cmdline.obj build\debug\mongo\db\field_ref.obj build\debug\mongo\db\field_ref_set.obj build\debug\mongo\db\field_parser.obj build\debug\mongo\db\keypattern.obj build\debug\mongo\db\write_concern_options.obj build\debug\mongo\db\index_names.obj build\debug\mongo\db\commands\test_commands_enabled.obj build\debug\mongo\db\commands\test_commands_enabled_gen.obj build\debug\mongo\db\commands\server_status_internal.obj build\debug\mongo\db\commands\server_status_metric.obj build\debug\mongo\db\auth\address_restriction.obj build\debug\mongo\db\auth\address_restriction_gen.obj build\debug\mongo\db\auth\restriction_environment.obj build\debug\mongo\bson\mutable\document.obj build\debug\mongo\bson\mutable\element.obj build\debug\mongo\util\safe_num.obj build\debug\mongo\db\auth\authorization_manager.obj build\debug\mongo\db\auth\authorization_session.obj build\debug\mongo\db\auth\auth_decorations.obj build\debug\mongo\db\auth\user_name.obj build\debug\mongo\db\auth\role_name.obj build\debug\mongo\client\read_preference.obj build\debug\mongo\db\baton.obj build\debug\mongo\db\client.obj build\debug\mongo\db\default_baton.obj build\debug\mongo\db\operation_context.obj build\debug\mongo\db\operation_context_group.obj build\debug\mongo\db\service_context.obj build\debug\mongo\db\server_recovery.obj build\debug\mongo\db\unclean_shutdown.obj build\debug\mongo\db\repl_set_member_in_standalone_mode.obj build\debug\mongo\util\periodic_runner.obj build\debug\mongo\util\background_thread_clock_source.obj build\debug\mongo\util\clock_source.obj build\debug\mongo\util\fast_clock_source_factory.obj build\debug\mongo\db\storage\write_unit_of_work.obj build\debug\mongo\util\fail_point.obj build\debug\mongo\util\fail_point_registry.obj build\debug\mongo\util\fail_point_service.obj build\debug\mongo\util\fail_point_server_parameter_gen.obj build\debug\mongo\db\storage\storage_options.obj build\debug\mongo\db\storage\storage_parameters_gen.obj build\debug\mongo\db\multi_key_path_tracker.obj build\debug\mongo\db\logical_session_id.obj build\debug\mongo\db\logical_session_id_gen.obj build\debug\mongo\idl\server_parameter.obj build\debug\mongo\idl\server_parameter_with_storage.obj build\debug\mongo\util\options_parser\constraints.obj build\debug\mongo\util\options_parser\environment.obj build\debug\mongo\util\options_parser\option_description.obj build\debug\mongo\util\options_parser\option_section.obj build\debug\mongo\util\options_parser\options_parser.obj build\debug\mongo\util\options_parser\startup_option_init.obj build\debug\mongo\util\options_parser\startup_options.obj build\debug\mongo\util\options_parser\value.obj build\debug\third_party\shim_yaml.obj build\debug\third_party\yaml-cpp-0.6.2\src\binary.obj build\debug\third_party\yaml-cpp-0.6.2\src\contrib\graphbuilder.obj build\debug\third_party\yaml-cpp-0.6.2\src\contrib\graphbuilderadapter.obj build\debug\third_party\yaml-cpp-0.6.2\src\convert.obj build\debug\third_party\yaml-cpp-0.6.2\src\directives.obj build\debug\third_party\yaml-cpp-0.6.2\src\emit.obj build\debug\third_party\yaml-cpp-0.6.2\src\emitfromevents.obj build\debug\third_party\yaml-cpp-0.6.2\src\emitter.obj build\debug\third_party\yaml-cpp-0.6.2\src\emitterstate.obj build\debug\third_party\yaml-cpp-0.6.2\src\emitterutils.obj build\debug\third_party\yaml-cpp-0.6.2\src\exceptions.obj build\debug\third_party\yaml-cpp-0.6.2\src\exp.obj build\debug\third_party\yaml-cpp-0.6.2\src\memory.obj build\debug\third_party\yaml-cpp-0.6.2\src\node.obj build\debug\third_party\yaml-cpp-0.6.2\src\node_data.obj build\debug\third_party\yaml-cpp-0.6.2\src\nodebuilder.obj build\debug\third_party\yaml-cpp-0.6.2\src\nodeevents.obj build\debug\third_party\yaml-cpp-0.6.2\src\null.obj build\debug\third_party\yaml-cpp-0.6.2\src\ostream_wrapper.obj build\debug\third_party\yaml-cpp-0.6.2\src\parse.obj build\debug\third_party\yaml-cpp-0.6.2\src\parser.obj build\debug\third_party\yaml-cpp-0.6.2\src\regex_yaml.obj build\debug\third_party\yaml-cpp-0.6.2\src\scanner.obj build\debug\third_party\yaml-cpp-0.6.2\src\scanscalar.obj build\debug\third_party\yaml-cpp-0.6.2\src\scantag.obj build\debug\third_party\yaml-cpp-0.6.2\src\scantoken.obj build\debug\third_party\yaml-cpp-0.6.2\src\simplekey.obj build\debug\third_party\yaml-cpp-0.6.2\src\singledocparser.obj build\debug\third_party\yaml-cpp-0.6.2\src\stream.obj build\debug\third_party\yaml-cpp-0.6.2\src\tag.obj build\debug\mongo\util\net\cidr.obj build\debug\mongo\util\net\hostandport.obj build\debug\mongo\util\net\hostname_canonicalization.obj build\debug\mongo\util\net\sockaddr.obj build\debug\mongo\util\net\socket_exception.obj build\debug\mongo\util\net\socket_utils.obj build\debug\mongo\util\net\hostandport_gen.obj build\debug\mongo\util\winutil.obj build\debug\mongo\util\concurrency\spin_lock.obj build\debug\mongo\util\net\http_client_winhttp.obj build\debug\mongo\idl\idl_parser.obj build\debug\mongo\db\command_generic_argument.obj build\debug\mongo\crypto\sha_block_windows.obj build\debug\mongo\crypto\sha1_block.obj build\debug\mongo\crypto\sha256_block.obj build\debug\mongo\util\secure_compare_memory.obj build\debug\mongo\base\secure_allocator.obj build\debug\mongo\util\secure_zero_memory.obj build\debug\mongo\util\processinfo.obj build\debug\mongo\util\processinfo_windows.obj build\debug\mongo\db\server_options.obj build\debug\mongo\bson\util\bson_extract.obj build\debug\mongo\base\data_range.obj build\debug\mongo\base\data_range_cursor.obj build\debug\mongo\base\data_type.obj build\debug\mongo\base\data_type_string_data.obj build\debug\mongo\base\data_type_terminated.obj build\debug\mongo\base\error_codes.obj build\debug\mongo\base\error_extra_info.obj build\debug\mongo\base\global_initializer.obj build\debug\mongo\base\global_initializer_registerer.obj build\debug\mongo\base\init.obj build\debug\mongo\base\initializer.obj build\debug\mongo\base\initializer_dependency_graph.obj build\debug\mongo\base\make_string_vector.obj build\debug\mongo\base\parse_number.obj build\debug\mongo\base\shim.obj build\debug\mongo\base\simple_string_data_comparator.obj build\debug\mongo\base\status.obj build\debug\mongo\base\string_data.obj build\debug\mongo\base\transaction_error.obj build\debug\mongo\base\validate_locale.obj build\debug\mongo\bson\bson_comparator_interface_base.obj build\debug\mongo\bson\bson_depth.obj build\debug\mongo\bson\bson_validate.obj build\debug\mongo\bson\bsonelement.obj build\debug\mongo\bson\bsonmisc.obj build\debug\mongo\bson\bsonobj.obj build\debug\mongo\bson\bsonobjbuilder.obj build\debug\mongo\bson\bsontypes.obj build\debug\mongo\bson\json.obj build\debug\mongo\bson\oid.obj build\debug\mongo\bson\simple_bsonelement_comparator.obj build\debug\mongo\bson\simple_bsonobj_comparator.obj build\debug\mongo\bson\timestamp.obj build\debug\mongo\logger\component_message_log_domain.obj build\debug\mongo\logger\console.obj build\debug\mongo\logger\log_component.obj build\debug\mongo\logger\log_component_settings.obj build\debug\mongo\logger\log_manager.obj build\debug\mongo\logger\log_severity.obj build\debug\mongo\logger\logger.obj build\debug\mongo\logger\logstream_builder.obj build\debug\mongo\logger\message_event_utf8_encoder.obj build\debug\mongo\logger\message_log_domain.obj build\debug\mongo\logger\ramlog.obj build\debug\mongo\logger\redaction.obj build\debug\mongo\logger\rotatable_file_manager.obj build\debug\mongo\logger\rotatable_file_writer.obj build\debug\mongo\platform\decimal128.obj build\debug\mongo\platform\mutex.obj build\debug\mongo\platform\posix_fadvise.obj build\debug\mongo\platform\process_id.obj build\debug\mongo\platform\random.obj build\debug\mongo\platform\shared_library.obj build\debug\mongo\platform\shared_library_windows.obj build\debug\mongo\platform\stack_locator.obj build\debug\mongo\platform\stack_locator_windows.obj build\debug\mongo\platform\strcasestr.obj build\debug\mongo\platform\strnlen.obj build\debug\mongo\util\allocator.obj build\debug\mongo\util\assert_util.obj build\debug\mongo\util\base64.obj build\debug\mongo\util\boost_assert_impl.obj build\debug\mongo\util\concurrency\idle_thread_block.obj build\debug\mongo\util\concurrency\thread_name.obj build\debug\mongo\util\duration.obj build\debug\mongo\util\errno_util.obj build\debug\mongo\util\exception_filter_win32.obj build\debug\mongo\util\exit.obj build\debug\mongo\util\file.obj build\debug\mongo\util\hex.obj build\debug\mongo\util\itoa.obj build\debug\mongo\util\log.obj build\debug\mongo\util\platform_init.obj build\debug\mongo\util\shell_exec.obj build\debug\mongo\util\signal_handlers_synchronous.obj build\debug\mongo\util\stacktrace.obj build\debug\mongo\util\stacktrace_windows.obj build\debug\mongo\util\startup_test.obj build\debug\mongo\util\str.obj build\debug\mongo\util\system_clock_source.obj build\debug\mongo\util\system_tick_source.obj build\debug\mongo\util\text.obj build\debug\mongo\util\time_support.obj build\debug\mongo\util\timer.obj build\debug\mongo\util\uuid.obj build\debug\mongo\util\version.obj build\debug\third_party\shim_pcrecpp.obj build\debug\third_party\pcre-8.42\pcre_byte_order.obj build\debug\third_party\pcre-8.42\pcre_compile.obj build\debug\third_party\pcre-8.42\pcre_config.obj build\debug\third_party\pcre-8.42\pcre_dfa_exec.obj build\debug\third_party\pcre-8.42\pcre_exec.obj build\debug\third_party\pcre-8.42\pcre_fullinfo.obj build\debug\third_party\pcre-8.42\pcre_get.obj build\debug\third_party\pcre-8.42\pcre_globals.obj build\debug\third_party\pcre-8.42\pcre_maketables.obj build\debug\third_party\pcre-8.42\pcre_newline.obj build\debug\third_party\pcre-8.42\pcre_ord2utf8.obj build\debug\third_party\pcre-8.42\pcre_refcount.obj build\debug\third_party\pcre-8.42\pcre_string_utils.obj build\debug\third_party\pcre-8.42\pcre_study.obj build\debug\third_party\pcre-8.42\pcre_tables.obj build\debug\third_party\pcre-8.42\pcre_ucd.obj build\debug\third_party\pcre-8.42\pcre_valid_utf8.obj build\debug\third_party\pcre-8.42\pcre_version.obj build\debug\third_party\pcre-8.42\pcre_xclass.obj build\debug\third_party\pcre-8.42\pcre_chartables.obj build\debug\third_party\pcre-8.42\pcrecpp.obj build\debug\third_party\pcre-8.42\pcre_scanner.obj build\debug\third_party\pcre-8.42\pcre_stringpiece.obj build\debug\third_party\shim_intel_decimal128.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_exception.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_four_over_pi.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_bessel.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_bid.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_cbrt.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_erf.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_exp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_int.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_inv_hyper.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_inv_trig.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_lgamma.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_log.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_mod.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_ops.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_ops_64.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_pow.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_powi.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_sqrt.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_trig.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\sqrt_tab_t.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_2_str_tables.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_acos.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_acosh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_add.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_asin.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_asinh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_atan.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_atan2.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_atanh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_cbrt.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_compare.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_cos.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_cosh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_div.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_erf.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_erfc.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_exp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_exp10.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_exp2.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_expm1.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_fdimd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_fma.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_fmod.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_frexp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_hypot.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_ldexp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_lgamma.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_llrintd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_log.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_log10.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_log1p.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_log2.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_logb.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_logbd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_lrintd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_lround.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_minmax.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_modf.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_mul.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_nearbyintd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_next.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_nexttowardd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_noncomp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_pow.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_quantexpd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_quantize.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_rem.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_round_integral.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_scalb.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_scalbl.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_sin.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_sinh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_sqrt.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_string.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_tan.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_tanh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_tgamma.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_int16.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_int32.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_int64.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_int8.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_uint16.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_uint32.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_uint64.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_uint8.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_acos.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_acosh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_add.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_asin.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_asinh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_atan.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_atan2.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_atanh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_cbrt.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_compare.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_cos.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_cosh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_div.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_erf.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_erfc.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_exp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_exp10.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_exp2.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_expm1.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_fdimd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_fma.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_fmod.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_frexp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_hypot.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_ldexp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_lgamma.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_llrintd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_log.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_log10.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_log1p.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_log2.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_logb.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_logbd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_lrintd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_lround.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_minmax.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_modf.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_mul.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_nearbyintd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_next.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_nexttowardd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_noncomp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_pow.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_quantexpd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_quantize.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_rem.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_round_integral.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_scalb.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_scalbl.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_sin.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_sinh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_sqrt.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_string.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_sub.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_tan.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_tanh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_tgamma.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_bid128.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_bid64.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_int16.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_int32.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_int64.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_int8.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_uint16.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_uint32.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_uint64.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_uint8.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_acos.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_acosh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_add.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_asin.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_asinh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_atan.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_atan2.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_atanh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_cbrt.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_compare.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_cos.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_cosh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_div.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_erf.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_erfc.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_exp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_exp10.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_exp2.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_expm1.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_fdimd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_fma.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_fmod.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_frexp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_hypot.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_ldexp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_lgamma.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_llrintd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_log.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_log10.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_log1p.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_log2.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_logb.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_logbd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_lrintd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_lround.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_minmax.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_modf.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_mul.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_nearbyintd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_next.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_nexttowardd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_noncomp.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_pow.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_quantexpd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_quantize.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_rem.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_round_integral.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_scalb.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_scalbl.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_sin.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_sinh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_sqrt.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_string.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_tan.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_tanh.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_tgamma.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_bid128.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_int16.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_int32.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_int64.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_int8.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_uint16.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_uint32.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_uint64.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_uint8.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_binarydecimal.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_convert_data.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_decimal_data.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_decimal_globals.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_dpd.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_feclearexcept.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_fegetexceptflag.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_feraiseexcept.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_fesetexceptflag.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_fetestexcept.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_flag_operations.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_from_int.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_round.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\strtod128.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\strtod32.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\strtod64.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\wcstod128.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\wcstod32.obj build\debug\third_party\IntelRDFPMathLib20U1\LIBRARY\src\wcstod64.obj build\debug\third_party\shim_fmt.obj build\debug\third_party\fmt\dist\src\format.obj build\debug\third_party\fmt\dist\src\posix.obj build\debug\third_party\shim_boost.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\cmdline.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\config_file.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\convert.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\options_description.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\parsers.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\positional_options.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\split.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\utf8_codecvt_facet.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\value_semantic.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\variables_map.obj build\debug\third_party\boost-1.70.0\libs\program_options\src\winmain.obj build\debug\third_party\boost-1.70.0\libs\iostreams\src\file_descriptor.obj build\debug\third_party\boost-1.70.0\libs\iostreams\src\mapped_file.obj build\debug\third_party\boost-1.70.0\libs\filesystem\src\codecvt_error_category.obj build\debug\third_party\boost-1.70.0\libs\filesystem\src\operations.obj build\debug\third_party\boost-1.70.0\libs\filesystem\src\path.obj build\debug\third_party\boost-1.70.0\libs\filesystem\src\path_traits.obj build\debug\third_party\boost-1.70.0\libs\filesystem\src\portability.obj build\debug\third_party\boost-1.70.0\libs\filesystem\src\unique_path.obj build\debug\third_party\boost-1.70.0\libs\filesystem\src\utf8_codecvt_facet.obj build\debug\third_party\boost-1.70.0\libs\filesystem\src\windows_file_codecvt.obj build\debug\third_party\boost-1.70.0\libs\system\src\error_code.obj build\debug\third_party\shim_abseil.obj build\debug\third_party\abseil-cpp-master\abseil-cpp\absl\container\internal\raw_hash_set.obj build\debug\third_party\abseil-cpp-master\abseil-cpp\absl\hash\internal\city.obj build\debug\third_party\abseil-cpp-master\abseil-cpp\absl\hash\internal\hash.obj build\debug\third_party\murmurhash3\MurmurHash3.obj build\debug\mongo\util\quick_exit.obj build\debug\third_party\shim_allocator.obj build\debug\third_party\gperftools-2.7\dist\src\base\dynamic_annotations.obj build\debug\third_party\gperftools-2.7\dist\src\base\elf_mem_image.obj build\debug\third_party\gperftools-2.7\dist\src\base\logging.obj build\debug\third_party\gperftools-2.7\dist\src\base\spinlock.obj build\debug\third_party\gperftools-2.7\dist\src\base\spinlock_internal.obj build\debug\third_party\gperftools-2.7\dist\src\base\sysinfo.obj build\debug\third_party\gperftools-2.7\dist\src\base\vdso_support.obj build\debug\third_party\gperftools-2.7\dist\src\central_freelist.obj build\debug\third_party\gperftools-2.7\dist\src\common.obj build\debug\third_party\gperftools-2.7\dist\src\internal_logging.obj build\debug\third_party\gperftools-2.7\dist\src\malloc_extension.obj build\debug\third_party\gperftools-2.7\dist\src\malloc_hook.obj build\debug\third_party\gperftools-2.7\dist\src\memfs_malloc.obj build\debug\third_party\gperftools-2.7\dist\src\page_heap.obj build\debug\third_party\gperftools-2.7\dist\src\sampler.obj build\debug\third_party\gperftools-2.7\dist\src\span.obj build\debug\third_party\gperftools-2.7\dist\src\stack_trace_table.obj build\debug\third_party\gperftools-2.7\dist\src\stacktrace.obj build\debug\third_party\gperftools-2.7\dist\src\static_vars.obj build\debug\third_party\gperftools-2.7\dist\src\symbolize.obj build\debug\third_party\gperftools-2.7\dist\src\thread_cache.obj build\debug\third_party\gperftools-2.7\dist\src\tcmalloc.obj build\debug\third_party\gperftools-2.7\dist\src\windows\port.obj build\debug\third_party\gperftools-2.7\dist\src\windows\system-alloc.obj build\debug\third_party\gperftools-2.7\dist\src\fake_stacktrace_scope.obj build\debug\mongo\util\debugger.obj build\debug\mongo\util\boost_assert_shim.obj build\debug\mongo\shell\mongodbcr.obj build\debug\mongo\shell\shell_options_init.obj ================================================ FILE: cmake/mongodb/windows-release.objects ================================================ build\opt\mongo\util\net\ssl_options_client.obj build\opt\mongo\util\net\ssl_options_client_gen.obj build\opt\mongo\transport\message_compressor_options_client_gen.obj build\opt\mongo\shell\kms_shell.obj build\opt\mongo\shell\linenoise.obj build\opt\mongo\shell\mk_wcwidth.obj build\opt\mongo\shell\mongo-server.obj build\opt\mongo\shell\shell_options.obj build\opt\mongo\shell\shell_utils.obj build\opt\mongo\shell\shell_utils_extended.obj build\opt\mongo\shell\shell_utils_launcher.obj build\opt\mongo\util\version_impl.obj build\opt\mongo\util\signal_handlers.obj build\opt\mongo\util\signal_win32.obj build\opt\mongo\db\log_process_details.obj build\opt\mongo\db\server_options_server_helpers.obj build\opt\mongo\db\server_options_base.obj build\opt\mongo\db\cluster_auth_mode_option_gen.obj build\opt\mongo\db\keyfile_option_gen.obj build\opt\mongo\db\server_options_base_gen.obj build\opt\mongo\db\server_options_general_gen.obj build\opt\mongo\db\server_options_nongeneral_gen.obj build\opt\mongo\db\repl\is_master_response.obj build\opt\mongo\db\repl\member_config.obj build\opt\mongo\db\repl\repl_set_config.obj build\opt\mongo\db\repl\repl_set_heartbeat_args_v1.obj build\opt\mongo\db\repl\repl_set_heartbeat_response.obj build\opt\mongo\db\repl\repl_set_request_votes_args.obj build\opt\mongo\db\repl\repl_set_tag.obj build\opt\mongo\db\repl\update_position_args.obj build\opt\mongo\db\repl\last_vote.obj build\opt\mongo\db\repl\repl_set_config_gen.obj build\opt\mongo\db\repl\split_horizon.obj build\opt\mongo\db\concurrency\d_concurrency.obj build\opt\mongo\db\concurrency\lock_manager.obj build\opt\mongo\db\concurrency\lock_state.obj build\opt\mongo\db\concurrency\lock_stats.obj build\opt\mongo\db\concurrency\replication_state_transition_lock_guard.obj build\opt\mongo\util\concurrency\ticketholder.obj build\opt\mongo\db\concurrency\flow_control_ticketholder.obj build\opt\mongo\util\password.obj build\opt\mongo\util\password_params_gen.obj build\opt\mongo\util\options_parser\options_parser_init.obj build\opt\mongo\shell\bench.obj build\opt\mongo\db\traffic_reader.obj build\opt\mongo\db\logical_session_id_helpers.obj build\opt\mongo\db\catalog\index_key_validate.obj build\opt\mongo\db\index\btree_key_generator.obj build\opt\mongo\db\index\expression_keys_private.obj build\opt\mongo\db\index\sort_key_generator.obj build\opt\mongo\db\index\wildcard_key_generator.obj build\opt\mongo\db\exec\projection_exec_agg.obj build\opt\mongo\db\pipeline\parsed_aggregation_projection.obj build\opt\mongo\db\pipeline\parsed_aggregation_projection_node.obj build\opt\mongo\db\pipeline\parsed_exclusion_projection.obj build\opt\mongo\db\pipeline\parsed_inclusion_projection.obj build\opt\mongo\db\pipeline\parsed_add_fields.obj build\opt\mongo\db\index\expression_params.obj build\opt\mongo\db\index\s2_common.obj build\opt\mongo\db\hasher.obj build\opt\mongo\db\fts\fts_index_format.obj build\opt\mongo\db\fts\fts_matcher.obj build\opt\mongo\db\fts\fts_query_impl.obj build\opt\mongo\db\fts\fts_query_parser.obj build\opt\mongo\db\fts\fts_spec.obj build\opt\mongo\db\fts\fts_spec_legacy.obj build\opt\mongo\db\fts\fts_language.obj build\opt\mongo\db\fts\fts_basic_phrase_matcher.obj build\opt\mongo\db\fts\fts_basic_tokenizer.obj build\opt\mongo\db\fts\fts_unicode_phrase_matcher.obj build\opt\mongo\db\fts\fts_unicode_tokenizer.obj build\opt\mongo\db\fts\fts_util.obj build\opt\mongo\db\fts\fts_element_iterator.obj build\opt\mongo\db\fts\stemmer.obj build\opt\mongo\db\fts\stop_words.obj build\opt\mongo\db\fts\stop_words_list.obj build\opt\mongo\db\fts\tokenizer.obj build\opt\third_party\shim_stemmer.obj build\opt\third_party\libstemmer_c\runtime\api.obj build\opt\third_party\libstemmer_c\libstemmer\libstemmer_utf8.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_dutch.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_german.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_porter.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_spanish.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_english.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_hungarian.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_portuguese.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_swedish.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_finnish.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_italian.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_romanian.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_turkish.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_danish.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_french.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_norwegian.obj build\opt\third_party\libstemmer_c\src_c\stem_UTF_8_russian.obj build\opt\mongo\db\fts\unicode\codepoints_casefold.obj build\opt\mongo\db\fts\unicode\codepoints_delimiter_list.obj build\opt\mongo\db\fts\unicode\codepoints_diacritic_list.obj build\opt\mongo\db\fts\unicode\codepoints_diacritic_map.obj build\opt\mongo\db\fts\unicode\string.obj build\opt\mongo\shell\linenoise_utf8.obj build\opt\mongo\db\index\index_descriptor.obj build\opt\mongo\db\catalog\index_catalog.obj build\opt\mongo\db\catalog\index_catalog_entry.obj build\opt\mongo\db\catalog\disable_index_spec_namespace_generation_gen.obj build\opt\mongo\shell\encrypted_dbclient_base.obj build\opt\mongo\shell\fle_shell_options_gen.obj build\opt\mongo\shell\shell_options_gen.obj build\opt\mongo\shell\shell_options_storage.obj build\opt\mongo\shell\kms.obj build\opt\mongo\shell\kms_aws.obj build\opt\mongo\shell\kms_local.obj build\opt\mongo\shell\kms_gen.obj build\opt\third_party\shim_kms_message.obj build\opt\third_party\kms-message\src\hexlify.obj build\opt\third_party\kms-message\src\kms_b64.obj build\opt\third_party\kms-message\src\kms_decrypt_request.obj build\opt\third_party\kms-message\src\kms_encrypt_request.obj build\opt\third_party\kms-message\src\kms_kv_list.obj build\opt\third_party\kms-message\src\kms_message.obj build\opt\third_party\kms-message\src\kms_request.obj build\opt\third_party\kms-message\src\kms_request_opt.obj build\opt\third_party\kms-message\src\kms_request_str.obj build\opt\third_party\kms-message\src\kms_response.obj build\opt\third_party\kms-message\src\kms_response_parser.obj build\opt\third_party\kms-message\src\sort.obj build\opt\third_party\kms-message\src\kms_crypto_windows.obj build\opt\mongo\scripting\mozjs\base.obj build\opt\mongo\scripting\mozjs\bindata.obj build\opt\mongo\scripting\mozjs\bson.obj build\opt\mongo\scripting\mozjs\code.obj build\opt\mongo\scripting\mozjs\countdownlatch.obj build\opt\mongo\scripting\mozjs\cursor.obj build\opt\mongo\scripting\mozjs\cursor_handle.obj build\opt\mongo\scripting\mozjs\db.obj build\opt\mongo\scripting\mozjs\dbcollection.obj build\opt\mongo\scripting\mozjs\dbpointer.obj build\opt\mongo\scripting\mozjs\dbquery.obj build\opt\mongo\scripting\mozjs\dbref.obj build\opt\mongo\scripting\mozjs\engine.obj build\opt\mongo\scripting\mozjs\error.obj build\opt\mongo\scripting\mozjs\exception.obj build\opt\mongo\scripting\mozjs\global.obj build\opt\mongo\scripting\mozjs\idwrapper.obj build\opt\mongo\scripting\mozjs\implscope.obj build\opt\mongo\scripting\mozjs\internedstring.obj build\opt\mongo\scripting\mozjs\jscustomallocator.obj build\opt\mongo\scripting\mozjs\jsstringwrapper.obj build\opt\mongo\scripting\mozjs\jsthread.obj build\opt\mongo\scripting\mozjs\maxkey.obj build\opt\mongo\scripting\mozjs\minkey.obj build\opt\mongo\scripting\mozjs\mongo.obj build\opt\mongo\scripting\mozjs\mongohelpers.obj build\opt\mongo\scripting\mozjs\mongohelpers_js.obj build\opt\mongo\scripting\mozjs\nativefunction.obj build\opt\mongo\scripting\mozjs\numberdecimal.obj build\opt\mongo\scripting\mozjs\numberint.obj build\opt\mongo\scripting\mozjs\numberlong.obj build\opt\mongo\scripting\mozjs\object.obj build\opt\mongo\scripting\mozjs\objectwrapper.obj build\opt\mongo\scripting\mozjs\oid.obj build\opt\mongo\scripting\mozjs\PosixNSPR.obj build\opt\mongo\scripting\mozjs\proxyscope.obj build\opt\mongo\scripting\mozjs\regexp.obj build\opt\mongo\scripting\mozjs\session.obj build\opt\mongo\scripting\mozjs\status.obj build\opt\mongo\scripting\mozjs\timestamp.obj build\opt\mongo\scripting\mozjs\uri.obj build\opt\mongo\scripting\mozjs\valuereader.obj build\opt\mongo\scripting\mozjs\valuewriter.obj build\opt\mongo\scripting\mozjs\engine_gen.obj build\opt\mongo\scripting\mozjs\scripting_util_gen.obj build\opt\third_party\shim_mozjs.obj build\opt\third_party\mozjs-60\mongo_sources\mongoErrorReportToString.obj build\opt\third_party\mozjs-60\mongo_sources\freeOpToJSContext.obj build\opt\third_party\mozjs-60\extract\js\src\builtin\RegExp.obj build\opt\third_party\mozjs-60\extract\js\src\frontend\Parser.obj build\opt\third_party\mozjs-60\extract\js\src\gc\StoreBuffer.obj build\opt\third_party\mozjs-60\extract\js\src\jsarray.obj build\opt\third_party\mozjs-60\extract\js\src\jsmath.obj build\opt\third_party\mozjs-60\extract\js\src\mfbt\Unified_cpp_mfbt0.obj build\opt\third_party\mozjs-60\extract\js\src\perf\pm_stub.obj build\opt\third_party\mozjs-60\extract\js\src\util\DoubleToString.obj build\opt\third_party\mozjs-60\extract\js\src\vm\Interpreter.obj build\opt\third_party\mozjs-60\extract\js\src\vm\JSAtom.obj build\opt\third_party\mozjs-60\extract\mfbt\Compression.obj build\opt\third_party\mozjs-60\extract\mfbt\double-conversion\double-conversion\strtod.obj build\opt\third_party\mozjs-60\extract\mfbt\lz4.obj build\opt\third_party\mozjs-60\extract\mozglue\misc\Printf.obj build\opt\third_party\mozjs-60\extract\mozglue\misc\TimeStamp.obj build\opt\third_party\mozjs-60\extract\mozglue\misc\StackWalk.obj build\opt\third_party\mozjs-60\extract\mozglue\misc\ConditionVariable_windows.obj build\opt\third_party\mozjs-60\extract\mozglue\misc\Mutex_windows.obj build\opt\third_party\mozjs-60\extract\mozglue\misc\TimeStamp_windows.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_acos.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_acosh.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_asin.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_atan2.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_atanh.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_cosh.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_exp.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_hypot.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_log.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_log10.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_log2.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_pow.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_sinh.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\e_sqrt.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\k_exp.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_asinh.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_atan.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_cbrt.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_ceil.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_ceilf.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_copysign.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_expm1.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_fabs.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_floor.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_floorf.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_log1p.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_nearbyint.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_rint.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_rintf.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_scalbn.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_tanh.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_trunc.obj build\opt\third_party\mozjs-60\extract\modules\fdlibm\s_truncf.obj build\opt\third_party\mozjs-60\extract\js\src\jit\x86-shared\Disassembler-x86-shared.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src0.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src1.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src10.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src11.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src12.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src13.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src14.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src15.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src16.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src17.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src18.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src19.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src2.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src20.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src21.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src22.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src23.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src24.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src25.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src26.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src27.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src28.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src29.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src3.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src30.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src31.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src32.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src33.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src34.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src35.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src36.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src37.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src38.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src39.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src4.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src40.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src41.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src42.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src43.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src44.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src5.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src6.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src7.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src8.obj build\opt\third_party\mozjs-60\platform\x86_64\windows\build\Unified_cpp_js_src9.obj build\opt\mongo\scripting\deadline_monitor.obj build\opt\mongo\scripting\deadline_monitor_gen.obj build\opt\mongo\scripting\dbdirectclient_factory.obj build\opt\mongo\scripting\engine.obj build\opt\mongo\scripting\jsexception.obj build\opt\mongo\scripting\utils.obj build\opt\mongo\shell\mongo.obj build\opt\mongo\scripting\bson_template_evaluator.obj build\opt\mongo\crypto\aead_encryption.obj build\opt\mongo\db\matcher\expression.obj build\opt\mongo\db\matcher\expression_algo.obj build\opt\mongo\db\matcher\expression_array.obj build\opt\mongo\db\matcher\expression_expr.obj build\opt\mongo\db\matcher\expression_geo.obj build\opt\mongo\db\matcher\expression_internal_expr_eq.obj build\opt\mongo\db\matcher\expression_leaf.obj build\opt\mongo\db\matcher\expression_parser.obj build\opt\mongo\db\matcher\expression_text_base.obj build\opt\mongo\db\matcher\expression_text_noop.obj build\opt\mongo\db\matcher\expression_tree.obj build\opt\mongo\db\matcher\expression_where_base.obj build\opt\mongo\db\matcher\expression_where_noop.obj build\opt\mongo\db\matcher\expression_with_placeholder.obj build\opt\mongo\db\matcher\extensions_callback.obj build\opt\mongo\db\matcher\extensions_callback_noop.obj build\opt\mongo\db\matcher\match_details.obj build\opt\mongo\db\matcher\matchable.obj build\opt\mongo\db\matcher\matcher.obj build\opt\mongo\db\matcher\matcher_type_set.obj build\opt\mongo\db\matcher\rewrite_expr.obj build\opt\mongo\db\matcher\schema\encrypt_schema_types.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_all_elem_match_from_index.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_allowed_properties.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_cond.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_eq.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_fmod.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_match_array_index.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_num_array_items.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_num_properties.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_object_match.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_root_doc_eq.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_str_length.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_unique_items.obj build\opt\mongo\db\matcher\schema\expression_internal_schema_xor.obj build\opt\mongo\db\matcher\schema\json_pointer.obj build\opt\mongo\db\matcher\schema\json_schema_parser.obj build\opt\mongo\db\matcher\schema\encrypt_schema_gen.obj build\opt\mongo\db\query\query_knobs_gen.obj build\opt\mongo\db\pipeline\expression.obj build\opt\mongo\db\pipeline\expression_trigonometric.obj build\opt\mongo\util\summation.obj build\opt\mongo\util\regex_util.obj build\opt\mongo\db\pipeline\expression_context.obj build\opt\mongo\db\pipeline\variables.obj build\opt\mongo\db\query\collation\collator_factory_interface.obj build\opt\mongo\db\query\collation\collation_index_key.obj build\opt\mongo\db\query\collation\collation_spec.obj build\opt\mongo\db\query\collation\collator_interface.obj build\opt\mongo\db\pipeline\aggregation_request.obj build\opt\mongo\db\query\explain_options.obj build\opt\mongo\db\pipeline\document_source_change_stream_gen.obj build\opt\mongo\db\pipeline\document_source_list_sessions_gen.obj build\opt\mongo\db\pipeline\document_source_merge_gen.obj build\opt\mongo\db\pipeline\document_source_merge_modes_gen.obj build\opt\mongo\db\pipeline\document_source_replace_root_gen.obj build\opt\mongo\db\pipeline\exchange_spec_gen.obj build\opt\mongo\db\pipeline\value_gen.obj build\opt\mongo\db\pipeline\document_source_merge_spec.obj build\opt\mongo\db\pipeline\resume_token.obj build\opt\mongo\db\storage\key_string.obj build\opt\mongo\db\pipeline\dependencies.obj build\opt\mongo\db\pipeline\document.obj build\opt\mongo\db\pipeline\document_comparator.obj build\opt\mongo\db\pipeline\document_path_support.obj build\opt\mongo\db\pipeline\value.obj build\opt\mongo\db\pipeline\value_comparator.obj build\opt\mongo\util\intrusive_counter.obj build\opt\mongo\db\query\datetime\date_time_support.obj build\opt\third_party\shim_timelib.obj build\opt\third_party\timelib-2018.01\astro.obj build\opt\third_party\timelib-2018.01\dow.obj build\opt\third_party\timelib-2018.01\interval.obj build\opt\third_party\timelib-2018.01\parse_date.obj build\opt\third_party\timelib-2018.01\parse_iso_intervals.obj build\opt\third_party\timelib-2018.01\parse_tz.obj build\opt\third_party\timelib-2018.01\parse_zoneinfo.obj build\opt\third_party\timelib-2018.01\timelib.obj build\opt\third_party\timelib-2018.01\tm2unixtime.obj build\opt\third_party\timelib-2018.01\unixtime2tm.obj build\opt\mongo\db\pipeline\field_path.obj build\opt\mongo\db\matcher\path.obj build\opt\mongo\db\matcher\path_internal.obj build\opt\mongo\db\geo\geoparser.obj build\opt\mongo\db\geo\geometry_container.obj build\opt\mongo\db\geo\hash.obj build\opt\mongo\db\geo\shapes.obj build\opt\mongo\db\geo\big_polygon.obj build\opt\mongo\db\geo\r2_region_coverer.obj build\opt\third_party\s2\s1angle.obj build\opt\third_party\s2\s2.obj build\opt\third_party\s2\s2cellid.obj build\opt\third_party\s2\s2latlng.obj build\opt\third_party\s2\s1interval.obj build\opt\third_party\s2\s2cap.obj build\opt\third_party\s2\s2cell.obj build\opt\third_party\s2\s2cellunion.obj build\opt\third_party\s2\s2edgeindex.obj build\opt\third_party\s2\s2edgeutil.obj build\opt\third_party\s2\s2latlngrect.obj build\opt\third_party\s2\s2loop.obj build\opt\third_party\s2\s2pointregion.obj build\opt\third_party\s2\s2polygon.obj build\opt\third_party\s2\s2polygonbuilder.obj build\opt\third_party\s2\s2polyline.obj build\opt\third_party\s2\s2r2rect.obj build\opt\third_party\s2\s2region.obj build\opt\third_party\s2\s2regioncoverer.obj build\opt\third_party\s2\s2regionintersection.obj build\opt\third_party\s2\s2regionunion.obj build\opt\third_party\s2\util\math\mathutil.obj build\opt\third_party\s2\util\coding\coder.obj build\opt\third_party\s2\util\coding\varint.obj build\opt\third_party\s2\strings\split.obj build\opt\third_party\s2\strings\stringprintf.obj build\opt\third_party\s2\strings\strutil.obj build\opt\third_party\s2\base\int128.obj build\opt\third_party\s2\base\logging.obj build\opt\third_party\s2\base\stringprintf.obj build\opt\third_party\s2\base\strtoint.obj build\opt\mongo\db\fts\fts_query_noop.obj build\opt\mongo\crypto\symmetric_crypto.obj build\opt\mongo\crypto\symmetric_crypto_windows.obj build\opt\mongo\crypto\symmetric_key.obj build\opt\mongo\db\views\resolved_view.obj build\opt\mongo\db\storage\duplicate_key_error_info.obj build\opt\mongo\client\connection_string_connect.obj build\opt\mongo\client\mongo_uri_connect.obj build\opt\mongo\client\connpool.obj build\opt\mongo\client\dbclient_connection.obj build\opt\mongo\client\dbclient_rs.obj build\opt\mongo\client\global_conn_pool.obj build\opt\mongo\client\global_conn_pool_gen.obj build\opt\mongo\client\replica_set_change_notifier.obj build\opt\mongo\client\replica_set_monitor.obj build\opt\mongo\client\replica_set_monitor_manager.obj build\opt\mongo\executor\thread_pool_task_executor.obj build\opt\mongo\executor\network_interface_thread_pool.obj build\opt\mongo\executor\network_interface_factory.obj build\opt\mongo\executor\connection_pool_tl.obj build\opt\mongo\executor\network_interface_tl.obj build\opt\mongo\transport\transport_layer_manager.obj build\opt\mongo\transport\service_executor_adaptive.obj build\opt\mongo\transport\service_executor_reserved.obj build\opt\mongo\transport\service_executor_synchronous.obj build\opt\mongo\transport\service_executor_gen.obj build\opt\mongo\transport\transport_layer_asio.obj build\opt\mongo\base\system_error.obj build\opt\mongo\client\async_client.obj build\opt\mongo\transport\message_compressor_manager.obj build\opt\mongo\transport\message_compressor_metrics.obj build\opt\mongo\transport\message_compressor_registry.obj build\opt\mongo\transport\message_compressor_snappy.obj build\opt\mongo\transport\message_compressor_zlib.obj build\opt\mongo\transport\message_compressor_zstd.obj build\opt\third_party\shim_zstd.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\common\entropy_common.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\common\fse_decompress.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\common\threading.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\common\pool.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\common\zstd_common.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\common\error_private.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\common\xxhash.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\compress\hist.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\compress\fse_compress.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\compress\huf_compress.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_compress.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\compress\zstdmt_compress.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_fast.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_double_fast.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_lazy.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_opt.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\compress\zstd_ldm.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\decompress\huf_decompress.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\decompress\zstd_decompress.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\dictBuilder\cover.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\dictBuilder\fastcover.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\dictBuilder\divsufsort.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\dictBuilder\zdict.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\deprecated\zbuff_common.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\deprecated\zbuff_compress.obj build\opt\third_party\zstandard-1.3.7\zstd\lib\deprecated\zbuff_decompress.obj build\opt\third_party\shim_zlib.obj build\opt\third_party\zlib-1.2.11\adler32.obj build\opt\third_party\zlib-1.2.11\crc32.obj build\opt\third_party\zlib-1.2.11\compress.obj build\opt\third_party\zlib-1.2.11\deflate.obj build\opt\third_party\zlib-1.2.11\infback.obj build\opt\third_party\zlib-1.2.11\inffast.obj build\opt\third_party\zlib-1.2.11\inflate.obj build\opt\third_party\zlib-1.2.11\inftrees.obj build\opt\third_party\zlib-1.2.11\trees.obj build\opt\third_party\zlib-1.2.11\uncompr.obj build\opt\third_party\zlib-1.2.11\zutil.obj build\opt\third_party\shim_snappy.obj build\opt\third_party\snappy-1.1.7\snappy-c.obj build\opt\third_party\snappy-1.1.7\snappy.obj build\opt\third_party\snappy-1.1.7\snappy-sinksource.obj build\opt\mongo\executor\network_interface.obj build\opt\mongo\executor\task_executor.obj build\opt\mongo\executor\connection_pool.obj build\opt\mongo\executor\egress_tag_closer_manager.obj build\opt\mongo\executor\connection_pool_stats.obj build\opt\mongo\client\dbclient_base.obj build\opt\mongo\client\dbclient_cursor.obj build\opt\mongo\client\index_spec.obj build\opt\mongo\util\net\private\ssl_expiration.obj build\opt\mongo\util\net\ssl_manager.obj build\opt\mongo\util\net\ssl_parameters.obj build\opt\mongo\util\net\ssl_manager_windows.obj build\opt\mongo\util\net\ssl_stream.obj build\opt\mongo\util\net\ssl_parameters_gen.obj build\opt\third_party\shim_asio.obj build\opt\third_party\asio-master\asio\src\asio.obj build\opt\mongo\util\net\ssl_types.obj build\opt\mongo\util\net\ssl_options.obj build\opt\mongo\util\net\private\socket_poll.obj build\opt\mongo\util\net\sock.obj build\opt\mongo\util\background.obj build\opt\mongo\db\commands\server_status.obj build\opt\mongo\db\stats\counters.obj build\opt\mongo\db\commands.obj build\opt\mongo\db\audit.obj build\opt\mongo\db\query\count_command_as_aggregation_command.obj build\opt\mongo\db\query\count_request.obj build\opt\mongo\db\query\cursor_request.obj build\opt\mongo\db\query\cursor_response.obj build\opt\mongo\db\query\find_and_modify_request.obj build\opt\mongo\db\query\getmore_request.obj build\opt\mongo\db\query\killcursors_request.obj build\opt\mongo\db\query\killcursors_response.obj build\opt\mongo\db\query\view_response_formatter.obj build\opt\mongo\db\query\count_command_gen.obj build\opt\mongo\rpc\factory.obj build\opt\mongo\rpc\object_check.obj build\opt\mongo\rpc\legacy_request.obj build\opt\mongo\rpc\legacy_request_builder.obj build\opt\mongo\rpc\legacy_reply.obj build\opt\mongo\rpc\legacy_reply_builder.obj build\opt\mongo\rpc\reply_builder_interface.obj build\opt\mongo\rpc\object_check_gen.obj build\opt\mongo\s\catalog\mongo_version_range.obj build\opt\mongo\s\catalog\type_changelog.obj build\opt\mongo\s\catalog\type_chunk.obj build\opt\mongo\s\catalog\type_collection.obj build\opt\mongo\s\catalog\type_config_version.obj build\opt\mongo\s\catalog\type_database.obj build\opt\mongo\s\catalog\type_lockpings.obj build\opt\mongo\s\catalog\type_locks.obj build\opt\mongo\s\catalog\type_mongos.obj build\opt\mongo\s\catalog\type_shard_collection.obj build\opt\mongo\s\catalog\type_shard_database.obj build\opt\mongo\s\catalog\type_shard.obj build\opt\mongo\s\catalog\type_tags.obj build\opt\mongo\s\request_types\add_shard_request_type.obj build\opt\mongo\s\request_types\add_shard_to_zone_request_type.obj build\opt\mongo\s\request_types\balance_chunk_request_type.obj build\opt\mongo\s\request_types\commit_chunk_migration_request_type.obj build\opt\mongo\s\request_types\merge_chunk_request_type.obj build\opt\mongo\s\request_types\migration_secondary_throttle_options.obj build\opt\mongo\s\request_types\move_chunk_request.obj build\opt\mongo\s\request_types\remove_shard_from_zone_request_type.obj build\opt\mongo\s\request_types\set_shard_version_request.obj build\opt\mongo\s\request_types\split_chunk_request_type.obj build\opt\mongo\s\request_types\update_zone_key_range_request_type.obj build\opt\mongo\s\cannot_implicitly_create_collection_info.obj build\opt\mongo\s\chunk_version.obj build\opt\mongo\s\database_version_helpers.obj build\opt\mongo\s\shard_id.obj build\opt\mongo\s\stale_exception.obj build\opt\mongo\s\would_change_owning_shard_exception.obj build\opt\mongo\s\catalog\type_chunk_base_gen.obj build\opt\mongo\s\chunk_version_gen.obj build\opt\mongo\s\database_version_gen.obj build\opt\mongo\s\request_types\clone_catalog_data_gen.obj build\opt\mongo\s\request_types\clear_jumbo_flag_gen.obj build\opt\mongo\s\request_types\create_collection_gen.obj build\opt\mongo\s\request_types\create_database_gen.obj build\opt\mongo\s\request_types\flush_database_cache_updates_gen.obj build\opt\mongo\s\request_types\flush_routing_table_cache_updates_gen.obj build\opt\mongo\s\request_types\get_database_version_gen.obj build\opt\mongo\s\request_types\move_primary_gen.obj build\opt\mongo\s\request_types\shard_collection_gen.obj build\opt\mongo\s\request_types\clone_collection_options_from_primary_shard_gen.obj build\opt\mongo\s\request_types\wait_for_fail_point_gen.obj build\opt\mongo\rpc\message.obj build\opt\mongo\rpc\op_msg.obj build\opt\mongo\rpc\protocol.obj build\opt\third_party\wiredtiger\src\checksum\software\checksum.obj build\opt\third_party\wiredtiger\src\checksum\x86\crc32-x86.obj build\opt\third_party\wiredtiger\src\checksum\x86\crc32-x86-alt.obj build\opt\mongo\db\wire_version.obj build\opt\mongo\db\bson\dotted_path_support.obj build\opt\mongo\db\query\query_request.obj build\opt\mongo\db\query\tailable_mode.obj build\opt\mongo\db\query\tailable_mode_gen.obj build\opt\mongo\db\repl\read_concern_args.obj build\opt\mongo\db\catalog\collection_catalog.obj build\opt\mongo\db\catalog\collection.obj build\opt\mongo\db\ops\write_ops_parsers.obj build\opt\mongo\db\ops\write_ops_gen.obj build\opt\mongo\db\query\hint_parser.obj build\opt\mongo\db\query\hint_gen.obj build\opt\mongo\db\pipeline\runtime_constants_gen.obj build\opt\mongo\db\dbmessage.obj build\opt\mongo\client\connection_string.obj build\opt\mongo\client\mongo_uri.obj build\opt\mongo\util\dns_query.obj build\opt\mongo\client\query.obj build\opt\mongo\client\authenticate.obj build\opt\mongo\client\native_sasl_client_session.obj build\opt\mongo\client\sasl_client_authenticate.obj build\opt\mongo\client\sasl_client_authenticate_impl.obj build\opt\mongo\client\sasl_client_conversation.obj build\opt\mongo\client\sasl_client_session.obj build\opt\mongo\client\sasl_plain_client_conversation.obj build\opt\mongo\client\sasl_scram_client_conversation.obj build\opt\mongo\util\md5.obj build\opt\mongo\util\password_digest.obj build\opt\mongo\util\icu.obj build\opt\mongo\util\icu_init.obj build\opt\third_party\shim_icu.obj build\opt\third_party\icu4c-57.1\source\i18n\affixpatternparser.obj build\opt\third_party\icu4c-57.1\source\i18n\alphaindex.obj build\opt\third_party\icu4c-57.1\source\i18n\anytrans.obj build\opt\third_party\icu4c-57.1\source\i18n\astro.obj build\opt\third_party\icu4c-57.1\source\i18n\basictz.obj build\opt\third_party\icu4c-57.1\source\i18n\bocsu.obj build\opt\third_party\icu4c-57.1\source\i18n\brktrans.obj build\opt\third_party\icu4c-57.1\source\i18n\buddhcal.obj build\opt\third_party\icu4c-57.1\source\i18n\calendar.obj build\opt\third_party\icu4c-57.1\source\i18n\casetrn.obj build\opt\third_party\icu4c-57.1\source\i18n\cecal.obj build\opt\third_party\icu4c-57.1\source\i18n\chnsecal.obj build\opt\third_party\icu4c-57.1\source\i18n\choicfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\coleitr.obj build\opt\third_party\icu4c-57.1\source\i18n\coll.obj build\opt\third_party\icu4c-57.1\source\i18n\collation.obj build\opt\third_party\icu4c-57.1\source\i18n\collationbuilder.obj build\opt\third_party\icu4c-57.1\source\i18n\collationcompare.obj build\opt\third_party\icu4c-57.1\source\i18n\collationdata.obj build\opt\third_party\icu4c-57.1\source\i18n\collationdatabuilder.obj build\opt\third_party\icu4c-57.1\source\i18n\collationdatareader.obj build\opt\third_party\icu4c-57.1\source\i18n\collationdatawriter.obj build\opt\third_party\icu4c-57.1\source\i18n\collationfastlatin.obj build\opt\third_party\icu4c-57.1\source\i18n\collationfastlatinbuilder.obj build\opt\third_party\icu4c-57.1\source\i18n\collationfcd.obj build\opt\third_party\icu4c-57.1\source\i18n\collationiterator.obj build\opt\third_party\icu4c-57.1\source\i18n\collationkeys.obj build\opt\third_party\icu4c-57.1\source\i18n\collationroot.obj build\opt\third_party\icu4c-57.1\source\i18n\collationrootelements.obj build\opt\third_party\icu4c-57.1\source\i18n\collationruleparser.obj build\opt\third_party\icu4c-57.1\source\i18n\collationsets.obj build\opt\third_party\icu4c-57.1\source\i18n\collationsettings.obj build\opt\third_party\icu4c-57.1\source\i18n\collationtailoring.obj build\opt\third_party\icu4c-57.1\source\i18n\collationweights.obj build\opt\third_party\icu4c-57.1\source\i18n\compactdecimalformat.obj build\opt\third_party\icu4c-57.1\source\i18n\coptccal.obj build\opt\third_party\icu4c-57.1\source\i18n\cpdtrans.obj build\opt\third_party\icu4c-57.1\source\i18n\csdetect.obj build\opt\third_party\icu4c-57.1\source\i18n\csmatch.obj build\opt\third_party\icu4c-57.1\source\i18n\csr2022.obj build\opt\third_party\icu4c-57.1\source\i18n\csrecog.obj build\opt\third_party\icu4c-57.1\source\i18n\csrmbcs.obj build\opt\third_party\icu4c-57.1\source\i18n\csrsbcs.obj build\opt\third_party\icu4c-57.1\source\i18n\csrucode.obj build\opt\third_party\icu4c-57.1\source\i18n\csrutf8.obj build\opt\third_party\icu4c-57.1\source\i18n\curramt.obj build\opt\third_party\icu4c-57.1\source\i18n\currfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\currpinf.obj build\opt\third_party\icu4c-57.1\source\i18n\currunit.obj build\opt\third_party\icu4c-57.1\source\i18n\dangical.obj build\opt\third_party\icu4c-57.1\source\i18n\datefmt.obj build\opt\third_party\icu4c-57.1\source\i18n\dayperiodrules.obj build\opt\third_party\icu4c-57.1\source\i18n\dcfmtsym.obj build\opt\third_party\icu4c-57.1\source\i18n\decContext.obj build\opt\third_party\icu4c-57.1\source\i18n\decNumber.obj build\opt\third_party\icu4c-57.1\source\i18n\decfmtst.obj build\opt\third_party\icu4c-57.1\source\i18n\decimalformatpattern.obj build\opt\third_party\icu4c-57.1\source\i18n\decimfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\decimfmtimpl.obj build\opt\third_party\icu4c-57.1\source\i18n\digitaffix.obj build\opt\third_party\icu4c-57.1\source\i18n\digitaffixesandpadding.obj build\opt\third_party\icu4c-57.1\source\i18n\digitformatter.obj build\opt\third_party\icu4c-57.1\source\i18n\digitgrouping.obj build\opt\third_party\icu4c-57.1\source\i18n\digitinterval.obj build\opt\third_party\icu4c-57.1\source\i18n\digitlst.obj build\opt\third_party\icu4c-57.1\source\i18n\dtfmtsym.obj build\opt\third_party\icu4c-57.1\source\i18n\dtitvfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\dtitvinf.obj build\opt\third_party\icu4c-57.1\source\i18n\dtptngen.obj build\opt\third_party\icu4c-57.1\source\i18n\dtrule.obj build\opt\third_party\icu4c-57.1\source\i18n\esctrn.obj build\opt\third_party\icu4c-57.1\source\i18n\ethpccal.obj build\opt\third_party\icu4c-57.1\source\i18n\fmtable.obj build\opt\third_party\icu4c-57.1\source\i18n\fmtable_cnv.obj build\opt\third_party\icu4c-57.1\source\i18n\format.obj build\opt\third_party\icu4c-57.1\source\i18n\fphdlimp.obj build\opt\third_party\icu4c-57.1\source\i18n\fpositer.obj build\opt\third_party\icu4c-57.1\source\i18n\funcrepl.obj build\opt\third_party\icu4c-57.1\source\i18n\gender.obj build\opt\third_party\icu4c-57.1\source\i18n\gregocal.obj build\opt\third_party\icu4c-57.1\source\i18n\gregoimp.obj build\opt\third_party\icu4c-57.1\source\i18n\hebrwcal.obj build\opt\third_party\icu4c-57.1\source\i18n\identifier_info.obj build\opt\third_party\icu4c-57.1\source\i18n\indiancal.obj build\opt\third_party\icu4c-57.1\source\i18n\inputext.obj build\opt\third_party\icu4c-57.1\source\i18n\islamcal.obj build\opt\third_party\icu4c-57.1\source\i18n\japancal.obj build\opt\third_party\icu4c-57.1\source\i18n\measfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\measunit.obj build\opt\third_party\icu4c-57.1\source\i18n\measure.obj build\opt\third_party\icu4c-57.1\source\i18n\msgfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\name2uni.obj build\opt\third_party\icu4c-57.1\source\i18n\nfrs.obj build\opt\third_party\icu4c-57.1\source\i18n\nfrule.obj build\opt\third_party\icu4c-57.1\source\i18n\nfsubs.obj build\opt\third_party\icu4c-57.1\source\i18n\nortrans.obj build\opt\third_party\icu4c-57.1\source\i18n\nultrans.obj build\opt\third_party\icu4c-57.1\source\i18n\numfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\numsys.obj build\opt\third_party\icu4c-57.1\source\i18n\olsontz.obj build\opt\third_party\icu4c-57.1\source\i18n\persncal.obj build\opt\third_party\icu4c-57.1\source\i18n\pluralaffix.obj build\opt\third_party\icu4c-57.1\source\i18n\plurfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\plurrule.obj build\opt\third_party\icu4c-57.1\source\i18n\precision.obj build\opt\third_party\icu4c-57.1\source\i18n\quant.obj build\opt\third_party\icu4c-57.1\source\i18n\quantityformatter.obj build\opt\third_party\icu4c-57.1\source\i18n\rbnf.obj build\opt\third_party\icu4c-57.1\source\i18n\rbt.obj build\opt\third_party\icu4c-57.1\source\i18n\rbt_data.obj build\opt\third_party\icu4c-57.1\source\i18n\rbt_pars.obj build\opt\third_party\icu4c-57.1\source\i18n\rbt_rule.obj build\opt\third_party\icu4c-57.1\source\i18n\rbt_set.obj build\opt\third_party\icu4c-57.1\source\i18n\rbtz.obj build\opt\third_party\icu4c-57.1\source\i18n\regexcmp.obj build\opt\third_party\icu4c-57.1\source\i18n\regeximp.obj build\opt\third_party\icu4c-57.1\source\i18n\regexst.obj build\opt\third_party\icu4c-57.1\source\i18n\regextxt.obj build\opt\third_party\icu4c-57.1\source\i18n\region.obj build\opt\third_party\icu4c-57.1\source\i18n\reldatefmt.obj build\opt\third_party\icu4c-57.1\source\i18n\reldtfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\rematch.obj build\opt\third_party\icu4c-57.1\source\i18n\remtrans.obj build\opt\third_party\icu4c-57.1\source\i18n\repattrn.obj build\opt\third_party\icu4c-57.1\source\i18n\rulebasedcollator.obj build\opt\third_party\icu4c-57.1\source\i18n\scientificnumberformatter.obj build\opt\third_party\icu4c-57.1\source\i18n\scriptset.obj build\opt\third_party\icu4c-57.1\source\i18n\search.obj build\opt\third_party\icu4c-57.1\source\i18n\selfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\sharedbreakiterator.obj build\opt\third_party\icu4c-57.1\source\i18n\simpletz.obj build\opt\third_party\icu4c-57.1\source\i18n\smallintformatter.obj build\opt\third_party\icu4c-57.1\source\i18n\smpdtfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\smpdtfst.obj build\opt\third_party\icu4c-57.1\source\i18n\sortkey.obj build\opt\third_party\icu4c-57.1\source\i18n\standardplural.obj build\opt\third_party\icu4c-57.1\source\i18n\strmatch.obj build\opt\third_party\icu4c-57.1\source\i18n\strrepl.obj build\opt\third_party\icu4c-57.1\source\i18n\stsearch.obj build\opt\third_party\icu4c-57.1\source\i18n\taiwncal.obj build\opt\third_party\icu4c-57.1\source\i18n\timezone.obj build\opt\third_party\icu4c-57.1\source\i18n\titletrn.obj build\opt\third_party\icu4c-57.1\source\i18n\tmunit.obj build\opt\third_party\icu4c-57.1\source\i18n\tmutamt.obj build\opt\third_party\icu4c-57.1\source\i18n\tmutfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\tolowtrn.obj build\opt\third_party\icu4c-57.1\source\i18n\toupptrn.obj build\opt\third_party\icu4c-57.1\source\i18n\translit.obj build\opt\third_party\icu4c-57.1\source\i18n\transreg.obj build\opt\third_party\icu4c-57.1\source\i18n\tridpars.obj build\opt\third_party\icu4c-57.1\source\i18n\tzfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\tzgnames.obj build\opt\third_party\icu4c-57.1\source\i18n\tznames.obj build\opt\third_party\icu4c-57.1\source\i18n\tznames_impl.obj build\opt\third_party\icu4c-57.1\source\i18n\tzrule.obj build\opt\third_party\icu4c-57.1\source\i18n\tztrans.obj build\opt\third_party\icu4c-57.1\source\i18n\ucal.obj build\opt\third_party\icu4c-57.1\source\i18n\ucln_in.obj build\opt\third_party\icu4c-57.1\source\i18n\ucol.obj build\opt\third_party\icu4c-57.1\source\i18n\ucol_res.obj build\opt\third_party\icu4c-57.1\source\i18n\ucol_sit.obj build\opt\third_party\icu4c-57.1\source\i18n\ucoleitr.obj build\opt\third_party\icu4c-57.1\source\i18n\ucsdet.obj build\opt\third_party\icu4c-57.1\source\i18n\udat.obj build\opt\third_party\icu4c-57.1\source\i18n\udateintervalformat.obj build\opt\third_party\icu4c-57.1\source\i18n\udatpg.obj build\opt\third_party\icu4c-57.1\source\i18n\ufieldpositer.obj build\opt\third_party\icu4c-57.1\source\i18n\uitercollationiterator.obj build\opt\third_party\icu4c-57.1\source\i18n\ulocdata.obj build\opt\third_party\icu4c-57.1\source\i18n\umsg.obj build\opt\third_party\icu4c-57.1\source\i18n\unesctrn.obj build\opt\third_party\icu4c-57.1\source\i18n\uni2name.obj build\opt\third_party\icu4c-57.1\source\i18n\unum.obj build\opt\third_party\icu4c-57.1\source\i18n\unumsys.obj build\opt\third_party\icu4c-57.1\source\i18n\upluralrules.obj build\opt\third_party\icu4c-57.1\source\i18n\uregex.obj build\opt\third_party\icu4c-57.1\source\i18n\uregexc.obj build\opt\third_party\icu4c-57.1\source\i18n\uregion.obj build\opt\third_party\icu4c-57.1\source\i18n\usearch.obj build\opt\third_party\icu4c-57.1\source\i18n\uspoof.obj build\opt\third_party\icu4c-57.1\source\i18n\uspoof_build.obj build\opt\third_party\icu4c-57.1\source\i18n\uspoof_conf.obj build\opt\third_party\icu4c-57.1\source\i18n\uspoof_impl.obj build\opt\third_party\icu4c-57.1\source\i18n\uspoof_wsconf.obj build\opt\third_party\icu4c-57.1\source\i18n\utf16collationiterator.obj build\opt\third_party\icu4c-57.1\source\i18n\utf8collationiterator.obj build\opt\third_party\icu4c-57.1\source\i18n\utmscale.obj build\opt\third_party\icu4c-57.1\source\i18n\utrans.obj build\opt\third_party\icu4c-57.1\source\i18n\valueformatter.obj build\opt\third_party\icu4c-57.1\source\i18n\visibledigits.obj build\opt\third_party\icu4c-57.1\source\i18n\vtzone.obj build\opt\third_party\icu4c-57.1\source\i18n\vzone.obj build\opt\third_party\icu4c-57.1\source\i18n\windtfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\winnmfmt.obj build\opt\third_party\icu4c-57.1\source\i18n\wintzimpl.obj build\opt\third_party\icu4c-57.1\source\i18n\zonemeta.obj build\opt\third_party\icu4c-57.1\source\i18n\zrule.obj build\opt\third_party\icu4c-57.1\source\i18n\ztrans.obj build\opt\third_party\icu4c-57.1\source\common\appendable.obj build\opt\third_party\icu4c-57.1\source\common\bmpset.obj build\opt\third_party\icu4c-57.1\source\common\brkeng.obj build\opt\third_party\icu4c-57.1\source\common\brkiter.obj build\opt\third_party\icu4c-57.1\source\common\bytestream.obj build\opt\third_party\icu4c-57.1\source\common\bytestrie.obj build\opt\third_party\icu4c-57.1\source\common\bytestriebuilder.obj build\opt\third_party\icu4c-57.1\source\common\bytestrieiterator.obj build\opt\third_party\icu4c-57.1\source\common\caniter.obj build\opt\third_party\icu4c-57.1\source\common\chariter.obj build\opt\third_party\icu4c-57.1\source\common\charstr.obj build\opt\third_party\icu4c-57.1\source\common\cmemory.obj build\opt\third_party\icu4c-57.1\source\common\cstr.obj build\opt\third_party\icu4c-57.1\source\common\cstring.obj build\opt\third_party\icu4c-57.1\source\common\cwchar.obj build\opt\third_party\icu4c-57.1\source\common\dictbe.obj build\opt\third_party\icu4c-57.1\source\common\dictionarydata.obj build\opt\third_party\icu4c-57.1\source\common\dtintrv.obj build\opt\third_party\icu4c-57.1\source\common\errorcode.obj build\opt\third_party\icu4c-57.1\source\common\filteredbrk.obj build\opt\third_party\icu4c-57.1\source\common\filterednormalizer2.obj build\opt\third_party\icu4c-57.1\source\common\icudataver.obj build\opt\third_party\icu4c-57.1\source\common\icuplug.obj build\opt\third_party\icu4c-57.1\source\common\listformatter.obj build\opt\third_party\icu4c-57.1\source\common\loadednormalizer2impl.obj build\opt\third_party\icu4c-57.1\source\common\locavailable.obj build\opt\third_party\icu4c-57.1\source\common\locbased.obj build\opt\third_party\icu4c-57.1\source\common\locdispnames.obj build\opt\third_party\icu4c-57.1\source\common\locdspnm.obj build\opt\third_party\icu4c-57.1\source\common\locid.obj build\opt\third_party\icu4c-57.1\source\common\loclikely.obj build\opt\third_party\icu4c-57.1\source\common\locmap.obj build\opt\third_party\icu4c-57.1\source\common\locresdata.obj build\opt\third_party\icu4c-57.1\source\common\locutil.obj build\opt\third_party\icu4c-57.1\source\common\messagepattern.obj build\opt\third_party\icu4c-57.1\source\common\normalizer2.obj build\opt\third_party\icu4c-57.1\source\common\normalizer2impl.obj build\opt\third_party\icu4c-57.1\source\common\normlzr.obj build\opt\third_party\icu4c-57.1\source\common\parsepos.obj build\opt\third_party\icu4c-57.1\source\common\patternprops.obj build\opt\third_party\icu4c-57.1\source\common\pluralmap.obj build\opt\third_party\icu4c-57.1\source\common\propname.obj build\opt\third_party\icu4c-57.1\source\common\propsvec.obj build\opt\third_party\icu4c-57.1\source\common\punycode.obj build\opt\third_party\icu4c-57.1\source\common\putil.obj build\opt\third_party\icu4c-57.1\source\common\rbbi.obj build\opt\third_party\icu4c-57.1\source\common\rbbidata.obj build\opt\third_party\icu4c-57.1\source\common\rbbinode.obj build\opt\third_party\icu4c-57.1\source\common\rbbirb.obj build\opt\third_party\icu4c-57.1\source\common\rbbiscan.obj build\opt\third_party\icu4c-57.1\source\common\rbbisetb.obj build\opt\third_party\icu4c-57.1\source\common\rbbistbl.obj build\opt\third_party\icu4c-57.1\source\common\rbbitblb.obj build\opt\third_party\icu4c-57.1\source\common\resbund.obj build\opt\third_party\icu4c-57.1\source\common\resbund_cnv.obj build\opt\third_party\icu4c-57.1\source\common\resource.obj build\opt\third_party\icu4c-57.1\source\common\ruleiter.obj build\opt\third_party\icu4c-57.1\source\common\schriter.obj build\opt\third_party\icu4c-57.1\source\common\serv.obj build\opt\third_party\icu4c-57.1\source\common\servlk.obj build\opt\third_party\icu4c-57.1\source\common\servlkf.obj build\opt\third_party\icu4c-57.1\source\common\servls.obj build\opt\third_party\icu4c-57.1\source\common\servnotf.obj build\opt\third_party\icu4c-57.1\source\common\servrbf.obj build\opt\third_party\icu4c-57.1\source\common\servslkf.obj build\opt\third_party\icu4c-57.1\source\common\sharedobject.obj build\opt\third_party\icu4c-57.1\source\common\simpleformatter.obj build\opt\third_party\icu4c-57.1\source\common\stringpiece.obj build\opt\third_party\icu4c-57.1\source\common\stringtriebuilder.obj build\opt\third_party\icu4c-57.1\source\common\uarrsort.obj build\opt\third_party\icu4c-57.1\source\common\ubidi.obj build\opt\third_party\icu4c-57.1\source\common\ubidi_props.obj build\opt\third_party\icu4c-57.1\source\common\ubidiln.obj build\opt\third_party\icu4c-57.1\source\common\ubidiwrt.obj build\opt\third_party\icu4c-57.1\source\common\ubrk.obj build\opt\third_party\icu4c-57.1\source\common\ucase.obj build\opt\third_party\icu4c-57.1\source\common\ucasemap.obj build\opt\third_party\icu4c-57.1\source\common\ucasemap_titlecase_brkiter.obj build\opt\third_party\icu4c-57.1\source\common\ucat.obj build\opt\third_party\icu4c-57.1\source\common\uchar.obj build\opt\third_party\icu4c-57.1\source\common\ucharstrie.obj build\opt\third_party\icu4c-57.1\source\common\ucharstriebuilder.obj build\opt\third_party\icu4c-57.1\source\common\ucharstrieiterator.obj build\opt\third_party\icu4c-57.1\source\common\uchriter.obj build\opt\third_party\icu4c-57.1\source\common\ucln_cmn.obj build\opt\third_party\icu4c-57.1\source\common\ucmndata.obj build\opt\third_party\icu4c-57.1\source\common\ucnv.obj build\opt\third_party\icu4c-57.1\source\common\ucnv2022.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_bld.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_cb.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_cnv.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_ct.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_err.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_ext.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_io.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_lmb.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_set.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_u16.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_u32.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_u7.obj build\opt\third_party\icu4c-57.1\source\common\ucnv_u8.obj build\opt\third_party\icu4c-57.1\source\common\ucnvbocu.obj build\opt\third_party\icu4c-57.1\source\common\ucnvdisp.obj build\opt\third_party\icu4c-57.1\source\common\ucnvhz.obj build\opt\third_party\icu4c-57.1\source\common\ucnvisci.obj build\opt\third_party\icu4c-57.1\source\common\ucnvlat1.obj build\opt\third_party\icu4c-57.1\source\common\ucnvmbcs.obj build\opt\third_party\icu4c-57.1\source\common\ucnvscsu.obj build\opt\third_party\icu4c-57.1\source\common\ucnvsel.obj build\opt\third_party\icu4c-57.1\source\common\ucol_swp.obj build\opt\third_party\icu4c-57.1\source\common\ucurr.obj build\opt\third_party\icu4c-57.1\source\common\udata.obj build\opt\third_party\icu4c-57.1\source\common\udatamem.obj build\opt\third_party\icu4c-57.1\source\common\udataswp.obj build\opt\third_party\icu4c-57.1\source\common\uenum.obj build\opt\third_party\icu4c-57.1\source\common\uhash.obj build\opt\third_party\icu4c-57.1\source\common\uhash_us.obj build\opt\third_party\icu4c-57.1\source\common\uidna.obj build\opt\third_party\icu4c-57.1\source\common\uinit.obj build\opt\third_party\icu4c-57.1\source\common\uinvchar.obj build\opt\third_party\icu4c-57.1\source\common\uiter.obj build\opt\third_party\icu4c-57.1\source\common\ulist.obj build\opt\third_party\icu4c-57.1\source\common\ulistformatter.obj build\opt\third_party\icu4c-57.1\source\common\uloc.obj build\opt\third_party\icu4c-57.1\source\common\uloc_keytype.obj build\opt\third_party\icu4c-57.1\source\common\uloc_tag.obj build\opt\third_party\icu4c-57.1\source\common\umapfile.obj build\opt\third_party\icu4c-57.1\source\common\umath.obj build\opt\third_party\icu4c-57.1\source\common\umutex.obj build\opt\third_party\icu4c-57.1\source\common\unames.obj build\opt\third_party\icu4c-57.1\source\common\unifiedcache.obj build\opt\third_party\icu4c-57.1\source\common\unifilt.obj build\opt\third_party\icu4c-57.1\source\common\unifunct.obj build\opt\third_party\icu4c-57.1\source\common\uniset.obj build\opt\third_party\icu4c-57.1\source\common\uniset_closure.obj build\opt\third_party\icu4c-57.1\source\common\uniset_props.obj build\opt\third_party\icu4c-57.1\source\common\unisetspan.obj build\opt\third_party\icu4c-57.1\source\common\unistr.obj build\opt\third_party\icu4c-57.1\source\common\unistr_case.obj build\opt\third_party\icu4c-57.1\source\common\unistr_case_locale.obj build\opt\third_party\icu4c-57.1\source\common\unistr_cnv.obj build\opt\third_party\icu4c-57.1\source\common\unistr_props.obj build\opt\third_party\icu4c-57.1\source\common\unistr_titlecase_brkiter.obj build\opt\third_party\icu4c-57.1\source\common\unorm.obj build\opt\third_party\icu4c-57.1\source\common\unormcmp.obj build\opt\third_party\icu4c-57.1\source\common\uobject.obj build\opt\third_party\icu4c-57.1\source\common\uprops.obj build\opt\third_party\icu4c-57.1\source\common\ures_cnv.obj build\opt\third_party\icu4c-57.1\source\common\uresbund.obj build\opt\third_party\icu4c-57.1\source\common\uresdata.obj build\opt\third_party\icu4c-57.1\source\common\usc_impl.obj build\opt\third_party\icu4c-57.1\source\common\uscript.obj build\opt\third_party\icu4c-57.1\source\common\uscript_props.obj build\opt\third_party\icu4c-57.1\source\common\uset.obj build\opt\third_party\icu4c-57.1\source\common\uset_props.obj build\opt\third_party\icu4c-57.1\source\common\usetiter.obj build\opt\third_party\icu4c-57.1\source\common\ushape.obj build\opt\third_party\icu4c-57.1\source\common\usprep.obj build\opt\third_party\icu4c-57.1\source\common\ustack.obj build\opt\third_party\icu4c-57.1\source\common\ustr_cnv.obj build\opt\third_party\icu4c-57.1\source\common\ustr_titlecase_brkiter.obj build\opt\third_party\icu4c-57.1\source\common\ustr_wcs.obj build\opt\third_party\icu4c-57.1\source\common\ustrcase.obj build\opt\third_party\icu4c-57.1\source\common\ustrcase_locale.obj build\opt\third_party\icu4c-57.1\source\common\ustrenum.obj build\opt\third_party\icu4c-57.1\source\common\ustrfmt.obj build\opt\third_party\icu4c-57.1\source\common\ustring.obj build\opt\third_party\icu4c-57.1\source\common\ustrtrns.obj build\opt\third_party\icu4c-57.1\source\common\utext.obj build\opt\third_party\icu4c-57.1\source\common\utf_impl.obj build\opt\third_party\icu4c-57.1\source\common\util.obj build\opt\third_party\icu4c-57.1\source\common\util_props.obj build\opt\third_party\icu4c-57.1\source\common\utrace.obj build\opt\third_party\icu4c-57.1\source\common\utrie.obj build\opt\third_party\icu4c-57.1\source\common\utrie2.obj build\opt\third_party\icu4c-57.1\source\common\utrie2_builder.obj build\opt\third_party\icu4c-57.1\source\common\uts46.obj build\opt\third_party\icu4c-57.1\source\common\utypes.obj build\opt\third_party\icu4c-57.1\source\common\uvector.obj build\opt\third_party\icu4c-57.1\source\common\uvectr32.obj build\opt\third_party\icu4c-57.1\source\common\uvectr64.obj build\opt\third_party\icu4c-57.1\source\common\wintz.obj build\opt\third_party\icu4c-57.1\source\stubdata\stubdata.obj build\opt\mongo\rpc\get_status_from_command_result.obj build\opt\mongo\rpc\write_concern_error_detail.obj build\opt\mongo\executor\remote_command_request.obj build\opt\mongo\executor\remote_command_response.obj build\opt\mongo\rpc\metadata.obj build\opt\mongo\rpc\metadata\config_server_metadata.obj build\opt\mongo\rpc\metadata\egress_metadata_hook_list.obj build\opt\mongo\rpc\metadata\logical_time_metadata.obj build\opt\mongo\rpc\metadata\sharding_metadata.obj build\opt\mongo\rpc\metadata\repl_set_metadata.obj build\opt\mongo\rpc\metadata\oplog_query_metadata.obj build\opt\mongo\rpc\metadata\tracking_metadata.obj build\opt\mongo\rpc\metadata\client_metadata.obj build\opt\mongo\rpc\metadata\client_metadata_ismaster.obj build\opt\mongo\transport\service_entry_point_utils.obj build\opt\mongo\transport\session.obj build\opt\mongo\transport\transport_layer.obj build\opt\mongo\s\is_mongos.obj build\opt\mongo\db\logical_time_validator.obj build\opt\mongo\db\signed_logical_time.obj build\opt\mongo\db\keys_collection_manager_gen.obj build\opt\mongo\db\keys_collection_manager.obj build\opt\mongo\db\keys_collection_cache.obj build\opt\mongo\db\key_generator.obj build\opt\mongo\db\repl\repl_client_info.obj build\opt\mongo\db\repl\replication_coordinator.obj build\opt\mongo\db\repl\replication_coordinator_noop.obj build\opt\mongo\db\repl\replication_consistency_markers.obj build\opt\mongo\db\repl\replication_process.obj build\opt\mongo\db\repl\storage_interface.obj build\opt\mongo\db\repl\rollback_gen.obj build\opt\mongo\db\namespace_string.obj build\opt\mongo\db\repl\bson_extract_optime.obj build\opt\mongo\db\repl\optime.obj build\opt\mongo\db\keys_collection_client_sharded.obj build\opt\mongo\s\catalog\sharding_catalog_client.obj build\opt\mongo\db\logical_clock.obj build\opt\mongo\db\logical_clock_gen.obj build\opt\mongo\db\global_settings.obj build\opt\mongo\db\repl\repl_settings.obj build\opt\mongo\db\repl\repl_settings_gen.obj build\opt\mongo\db\keys_collection_document.obj build\opt\mongo\db\time_proof_service.obj build\opt\mongo\db\logical_time.obj build\opt\mongo\db\operation_time_tracker.obj build\opt\mongo\db\auth\action_set.obj build\opt\mongo\db\auth\action_type.obj build\opt\mongo\db\auth\impersonation_session.obj build\opt\mongo\db\auth\privilege.obj build\opt\mongo\db\auth\privilege_parser.obj build\opt\mongo\db\auth\resource_pattern.obj build\opt\mongo\db\auth\user_management_commands_parser.obj build\opt\mongo\rpc\metadata\impersonated_user_metadata.obj build\opt\mongo\rpc\metadata\impersonated_user_metadata_gen.obj build\opt\mongo\db\server_options_helpers.obj build\opt\mongo\db\server_options_helpers_gen.obj build\opt\mongo\util\cmdline_utils\censor_cmdline.obj build\opt\mongo\db\field_ref.obj build\opt\mongo\db\field_ref_set.obj build\opt\mongo\db\field_parser.obj build\opt\mongo\db\keypattern.obj build\opt\mongo\db\write_concern_options.obj build\opt\mongo\db\index_names.obj build\opt\mongo\db\commands\test_commands_enabled.obj build\opt\mongo\db\commands\test_commands_enabled_gen.obj build\opt\mongo\db\commands\server_status_internal.obj build\opt\mongo\db\commands\server_status_metric.obj build\opt\mongo\db\auth\address_restriction.obj build\opt\mongo\db\auth\address_restriction_gen.obj build\opt\mongo\db\auth\restriction_environment.obj build\opt\mongo\bson\mutable\document.obj build\opt\mongo\bson\mutable\element.obj build\opt\mongo\util\safe_num.obj build\opt\mongo\db\auth\authorization_manager.obj build\opt\mongo\db\auth\authorization_session.obj build\opt\mongo\db\auth\auth_decorations.obj build\opt\mongo\db\auth\user_name.obj build\opt\mongo\db\auth\role_name.obj build\opt\mongo\client\read_preference.obj build\opt\mongo\db\baton.obj build\opt\mongo\db\client.obj build\opt\mongo\db\default_baton.obj build\opt\mongo\db\operation_context.obj build\opt\mongo\db\operation_context_group.obj build\opt\mongo\db\service_context.obj build\opt\mongo\db\server_recovery.obj build\opt\mongo\db\unclean_shutdown.obj build\opt\mongo\db\repl_set_member_in_standalone_mode.obj build\opt\mongo\util\periodic_runner.obj build\opt\mongo\util\background_thread_clock_source.obj build\opt\mongo\util\clock_source.obj build\opt\mongo\util\fast_clock_source_factory.obj build\opt\mongo\db\storage\write_unit_of_work.obj build\opt\mongo\util\fail_point.obj build\opt\mongo\util\fail_point_registry.obj build\opt\mongo\util\fail_point_service.obj build\opt\mongo\util\fail_point_server_parameter_gen.obj build\opt\mongo\db\storage\storage_options.obj build\opt\mongo\db\storage\storage_parameters_gen.obj build\opt\mongo\db\multi_key_path_tracker.obj build\opt\mongo\db\logical_session_id.obj build\opt\mongo\db\logical_session_id_gen.obj build\opt\mongo\idl\server_parameter.obj build\opt\mongo\idl\server_parameter_with_storage.obj build\opt\mongo\util\options_parser\constraints.obj build\opt\mongo\util\options_parser\environment.obj build\opt\mongo\util\options_parser\option_description.obj build\opt\mongo\util\options_parser\option_section.obj build\opt\mongo\util\options_parser\options_parser.obj build\opt\mongo\util\options_parser\startup_option_init.obj build\opt\mongo\util\options_parser\startup_options.obj build\opt\mongo\util\options_parser\value.obj build\opt\third_party\shim_yaml.obj build\opt\third_party\yaml-cpp-0.6.2\src\binary.obj build\opt\third_party\yaml-cpp-0.6.2\src\contrib\graphbuilder.obj build\opt\third_party\yaml-cpp-0.6.2\src\contrib\graphbuilderadapter.obj build\opt\third_party\yaml-cpp-0.6.2\src\convert.obj build\opt\third_party\yaml-cpp-0.6.2\src\directives.obj build\opt\third_party\yaml-cpp-0.6.2\src\emit.obj build\opt\third_party\yaml-cpp-0.6.2\src\emitfromevents.obj build\opt\third_party\yaml-cpp-0.6.2\src\emitter.obj build\opt\third_party\yaml-cpp-0.6.2\src\emitterstate.obj build\opt\third_party\yaml-cpp-0.6.2\src\emitterutils.obj build\opt\third_party\yaml-cpp-0.6.2\src\exceptions.obj build\opt\third_party\yaml-cpp-0.6.2\src\exp.obj build\opt\third_party\yaml-cpp-0.6.2\src\memory.obj build\opt\third_party\yaml-cpp-0.6.2\src\node.obj build\opt\third_party\yaml-cpp-0.6.2\src\node_data.obj build\opt\third_party\yaml-cpp-0.6.2\src\nodebuilder.obj build\opt\third_party\yaml-cpp-0.6.2\src\nodeevents.obj build\opt\third_party\yaml-cpp-0.6.2\src\null.obj build\opt\third_party\yaml-cpp-0.6.2\src\ostream_wrapper.obj build\opt\third_party\yaml-cpp-0.6.2\src\parse.obj build\opt\third_party\yaml-cpp-0.6.2\src\parser.obj build\opt\third_party\yaml-cpp-0.6.2\src\regex_yaml.obj build\opt\third_party\yaml-cpp-0.6.2\src\scanner.obj build\opt\third_party\yaml-cpp-0.6.2\src\scanscalar.obj build\opt\third_party\yaml-cpp-0.6.2\src\scantag.obj build\opt\third_party\yaml-cpp-0.6.2\src\scantoken.obj build\opt\third_party\yaml-cpp-0.6.2\src\simplekey.obj build\opt\third_party\yaml-cpp-0.6.2\src\singledocparser.obj build\opt\third_party\yaml-cpp-0.6.2\src\stream.obj build\opt\third_party\yaml-cpp-0.6.2\src\tag.obj build\opt\mongo\util\net\cidr.obj build\opt\mongo\util\net\hostandport.obj build\opt\mongo\util\net\hostname_canonicalization.obj build\opt\mongo\util\net\sockaddr.obj build\opt\mongo\util\net\socket_exception.obj build\opt\mongo\util\net\socket_utils.obj build\opt\mongo\util\net\hostandport_gen.obj build\opt\mongo\util\winutil.obj build\opt\mongo\util\concurrency\spin_lock.obj build\opt\mongo\util\net\http_client_winhttp.obj build\opt\mongo\idl\idl_parser.obj build\opt\mongo\db\command_generic_argument.obj build\opt\mongo\crypto\sha_block_windows.obj build\opt\mongo\crypto\sha1_block.obj build\opt\mongo\crypto\sha256_block.obj build\opt\mongo\util\secure_compare_memory.obj build\opt\mongo\base\secure_allocator.obj build\opt\mongo\util\secure_zero_memory.obj build\opt\mongo\util\processinfo.obj build\opt\mongo\util\processinfo_windows.obj build\opt\mongo\db\server_options.obj build\opt\mongo\bson\util\bson_extract.obj build\opt\mongo\base\data_range.obj build\opt\mongo\base\data_range_cursor.obj build\opt\mongo\base\data_type.obj build\opt\mongo\base\data_type_string_data.obj build\opt\mongo\base\data_type_terminated.obj build\opt\mongo\base\error_codes.obj build\opt\mongo\base\error_extra_info.obj build\opt\mongo\base\global_initializer.obj build\opt\mongo\base\global_initializer_registerer.obj build\opt\mongo\base\init.obj build\opt\mongo\base\initializer.obj build\opt\mongo\base\initializer_dependency_graph.obj build\opt\mongo\base\make_string_vector.obj build\opt\mongo\base\parse_number.obj build\opt\mongo\base\shim.obj build\opt\mongo\base\simple_string_data_comparator.obj build\opt\mongo\base\status.obj build\opt\mongo\base\string_data.obj build\opt\mongo\base\transaction_error.obj build\opt\mongo\base\validate_locale.obj build\opt\mongo\bson\bson_comparator_interface_base.obj build\opt\mongo\bson\bson_depth.obj build\opt\mongo\bson\bson_validate.obj build\opt\mongo\bson\bsonelement.obj build\opt\mongo\bson\bsonmisc.obj build\opt\mongo\bson\bsonobj.obj build\opt\mongo\bson\bsonobjbuilder.obj build\opt\mongo\bson\bsontypes.obj build\opt\mongo\bson\json.obj build\opt\mongo\bson\oid.obj build\opt\mongo\bson\simple_bsonelement_comparator.obj build\opt\mongo\bson\simple_bsonobj_comparator.obj build\opt\mongo\bson\timestamp.obj build\opt\mongo\logger\component_message_log_domain.obj build\opt\mongo\logger\console.obj build\opt\mongo\logger\log_component.obj build\opt\mongo\logger\log_component_settings.obj build\opt\mongo\logger\log_manager.obj build\opt\mongo\logger\log_severity.obj build\opt\mongo\logger\logger.obj build\opt\mongo\logger\logstream_builder.obj build\opt\mongo\logger\message_event_utf8_encoder.obj build\opt\mongo\logger\message_log_domain.obj build\opt\mongo\logger\ramlog.obj build\opt\mongo\logger\redaction.obj build\opt\mongo\logger\rotatable_file_manager.obj build\opt\mongo\logger\rotatable_file_writer.obj build\opt\mongo\platform\decimal128.obj build\opt\mongo\platform\mutex.obj build\opt\mongo\platform\posix_fadvise.obj build\opt\mongo\platform\process_id.obj build\opt\mongo\platform\random.obj build\opt\mongo\platform\shared_library.obj build\opt\mongo\platform\shared_library_windows.obj build\opt\mongo\platform\stack_locator.obj build\opt\mongo\platform\stack_locator_windows.obj build\opt\mongo\platform\strcasestr.obj build\opt\mongo\platform\strnlen.obj build\opt\mongo\util\allocator.obj build\opt\mongo\util\assert_util.obj build\opt\mongo\util\base64.obj build\opt\mongo\util\boost_assert_impl.obj build\opt\mongo\util\concurrency\idle_thread_block.obj build\opt\mongo\util\concurrency\thread_name.obj build\opt\mongo\util\duration.obj build\opt\mongo\util\errno_util.obj build\opt\mongo\util\exception_filter_win32.obj build\opt\mongo\util\exit.obj build\opt\mongo\util\file.obj build\opt\mongo\util\hex.obj build\opt\mongo\util\itoa.obj build\opt\mongo\util\log.obj build\opt\mongo\util\platform_init.obj build\opt\mongo\util\shell_exec.obj build\opt\mongo\util\signal_handlers_synchronous.obj build\opt\mongo\util\stacktrace.obj build\opt\mongo\util\stacktrace_windows.obj build\opt\mongo\util\startup_test.obj build\opt\mongo\util\str.obj build\opt\mongo\util\system_clock_source.obj build\opt\mongo\util\system_tick_source.obj build\opt\mongo\util\text.obj build\opt\mongo\util\time_support.obj build\opt\mongo\util\timer.obj build\opt\mongo\util\uuid.obj build\opt\mongo\util\version.obj build\opt\third_party\shim_pcrecpp.obj build\opt\third_party\pcre-8.42\pcre_byte_order.obj build\opt\third_party\pcre-8.42\pcre_compile.obj build\opt\third_party\pcre-8.42\pcre_config.obj build\opt\third_party\pcre-8.42\pcre_dfa_exec.obj build\opt\third_party\pcre-8.42\pcre_exec.obj build\opt\third_party\pcre-8.42\pcre_fullinfo.obj build\opt\third_party\pcre-8.42\pcre_get.obj build\opt\third_party\pcre-8.42\pcre_globals.obj build\opt\third_party\pcre-8.42\pcre_maketables.obj build\opt\third_party\pcre-8.42\pcre_newline.obj build\opt\third_party\pcre-8.42\pcre_ord2utf8.obj build\opt\third_party\pcre-8.42\pcre_refcount.obj build\opt\third_party\pcre-8.42\pcre_string_utils.obj build\opt\third_party\pcre-8.42\pcre_study.obj build\opt\third_party\pcre-8.42\pcre_tables.obj build\opt\third_party\pcre-8.42\pcre_ucd.obj build\opt\third_party\pcre-8.42\pcre_valid_utf8.obj build\opt\third_party\pcre-8.42\pcre_version.obj build\opt\third_party\pcre-8.42\pcre_xclass.obj build\opt\third_party\pcre-8.42\pcre_chartables.obj build\opt\third_party\pcre-8.42\pcrecpp.obj build\opt\third_party\pcre-8.42\pcre_scanner.obj build\opt\third_party\pcre-8.42\pcre_stringpiece.obj build\opt\third_party\shim_intel_decimal128.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_exception.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_four_over_pi.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_bessel.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_bid.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_cbrt.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_erf.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_exp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_int.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_inv_hyper.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_inv_trig.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_lgamma.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_log.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_mod.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_ops.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_ops_64.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_pow.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_powi.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_sqrt.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\dpml_ux_trig.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\float128\sqrt_tab_t.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_2_str_tables.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_acos.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_acosh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_add.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_asin.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_asinh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_atan.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_atan2.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_atanh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_cbrt.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_compare.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_cos.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_cosh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_div.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_erf.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_erfc.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_exp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_exp10.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_exp2.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_expm1.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_fdimd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_fma.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_fmod.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_frexp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_hypot.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_ldexp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_lgamma.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_llrintd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_log.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_log10.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_log1p.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_log2.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_logb.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_logbd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_lrintd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_lround.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_minmax.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_modf.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_mul.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_nearbyintd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_next.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_nexttowardd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_noncomp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_pow.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_quantexpd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_quantize.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_rem.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_round_integral.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_scalb.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_scalbl.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_sin.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_sinh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_sqrt.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_string.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_tan.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_tanh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_tgamma.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_int16.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_int32.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_int64.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_int8.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_uint16.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_uint32.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_uint64.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid128_to_uint8.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_acos.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_acosh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_add.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_asin.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_asinh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_atan.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_atan2.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_atanh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_cbrt.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_compare.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_cos.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_cosh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_div.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_erf.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_erfc.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_exp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_exp10.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_exp2.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_expm1.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_fdimd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_fma.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_fmod.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_frexp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_hypot.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_ldexp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_lgamma.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_llrintd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_log.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_log10.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_log1p.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_log2.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_logb.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_logbd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_lrintd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_lround.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_minmax.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_modf.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_mul.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_nearbyintd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_next.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_nexttowardd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_noncomp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_pow.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_quantexpd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_quantize.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_rem.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_round_integral.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_scalb.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_scalbl.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_sin.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_sinh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_sqrt.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_string.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_sub.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_tan.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_tanh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_tgamma.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_bid128.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_bid64.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_int16.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_int32.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_int64.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_int8.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_uint16.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_uint32.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_uint64.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid32_to_uint8.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_acos.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_acosh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_add.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_asin.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_asinh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_atan.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_atan2.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_atanh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_cbrt.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_compare.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_cos.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_cosh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_div.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_erf.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_erfc.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_exp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_exp10.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_exp2.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_expm1.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_fdimd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_fma.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_fmod.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_frexp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_hypot.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_ldexp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_lgamma.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_llrintd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_log.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_log10.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_log1p.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_log2.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_logb.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_logbd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_lrintd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_lround.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_minmax.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_modf.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_mul.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_nearbyintd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_next.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_nexttowardd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_noncomp.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_pow.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_quantexpd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_quantize.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_rem.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_round_integral.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_scalb.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_scalbl.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_sin.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_sinh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_sqrt.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_string.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_tan.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_tanh.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_tgamma.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_bid128.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_int16.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_int32.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_int64.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_int8.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_uint16.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_uint32.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_uint64.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid64_to_uint8.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_binarydecimal.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_convert_data.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_decimal_data.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_decimal_globals.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_dpd.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_feclearexcept.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_fegetexceptflag.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_feraiseexcept.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_fesetexceptflag.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_fetestexcept.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_flag_operations.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_from_int.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\bid_round.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\strtod128.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\strtod32.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\strtod64.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\wcstod128.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\wcstod32.obj build\opt\third_party\IntelRDFPMathLib20U1\LIBRARY\src\wcstod64.obj build\opt\third_party\shim_fmt.obj build\opt\third_party\fmt\dist\src\format.obj build\opt\third_party\fmt\dist\src\posix.obj build\opt\third_party\shim_boost.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\cmdline.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\config_file.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\convert.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\options_description.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\parsers.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\positional_options.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\split.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\utf8_codecvt_facet.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\value_semantic.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\variables_map.obj build\opt\third_party\boost-1.70.0\libs\program_options\src\winmain.obj build\opt\third_party\boost-1.70.0\libs\iostreams\src\file_descriptor.obj build\opt\third_party\boost-1.70.0\libs\iostreams\src\mapped_file.obj build\opt\third_party\boost-1.70.0\libs\filesystem\src\codecvt_error_category.obj build\opt\third_party\boost-1.70.0\libs\filesystem\src\operations.obj build\opt\third_party\boost-1.70.0\libs\filesystem\src\path.obj build\opt\third_party\boost-1.70.0\libs\filesystem\src\path_traits.obj build\opt\third_party\boost-1.70.0\libs\filesystem\src\portability.obj build\opt\third_party\boost-1.70.0\libs\filesystem\src\unique_path.obj build\opt\third_party\boost-1.70.0\libs\filesystem\src\utf8_codecvt_facet.obj build\opt\third_party\boost-1.70.0\libs\filesystem\src\windows_file_codecvt.obj build\opt\third_party\boost-1.70.0\libs\system\src\error_code.obj build\opt\third_party\shim_abseil.obj build\opt\third_party\abseil-cpp-master\abseil-cpp\absl\container\internal\raw_hash_set.obj build\opt\third_party\abseil-cpp-master\abseil-cpp\absl\hash\internal\city.obj build\opt\third_party\abseil-cpp-master\abseil-cpp\absl\hash\internal\hash.obj build\opt\third_party\murmurhash3\MurmurHash3.obj build\opt\mongo\util\quick_exit.obj build\opt\third_party\shim_allocator.obj build\opt\third_party\gperftools-2.7\dist\src\base\dynamic_annotations.obj build\opt\third_party\gperftools-2.7\dist\src\base\elf_mem_image.obj build\opt\third_party\gperftools-2.7\dist\src\base\logging.obj build\opt\third_party\gperftools-2.7\dist\src\base\spinlock.obj build\opt\third_party\gperftools-2.7\dist\src\base\spinlock_internal.obj build\opt\third_party\gperftools-2.7\dist\src\base\sysinfo.obj build\opt\third_party\gperftools-2.7\dist\src\base\vdso_support.obj build\opt\third_party\gperftools-2.7\dist\src\central_freelist.obj build\opt\third_party\gperftools-2.7\dist\src\common.obj build\opt\third_party\gperftools-2.7\dist\src\internal_logging.obj build\opt\third_party\gperftools-2.7\dist\src\malloc_extension.obj build\opt\third_party\gperftools-2.7\dist\src\malloc_hook.obj build\opt\third_party\gperftools-2.7\dist\src\memfs_malloc.obj build\opt\third_party\gperftools-2.7\dist\src\page_heap.obj build\opt\third_party\gperftools-2.7\dist\src\sampler.obj build\opt\third_party\gperftools-2.7\dist\src\span.obj build\opt\third_party\gperftools-2.7\dist\src\stack_trace_table.obj build\opt\third_party\gperftools-2.7\dist\src\stacktrace.obj build\opt\third_party\gperftools-2.7\dist\src\static_vars.obj build\opt\third_party\gperftools-2.7\dist\src\symbolize.obj build\opt\third_party\gperftools-2.7\dist\src\thread_cache.obj build\opt\third_party\gperftools-2.7\dist\src\tcmalloc.obj build\opt\third_party\gperftools-2.7\dist\src\windows\port.obj build\opt\third_party\gperftools-2.7\dist\src\windows\system-alloc.obj build\opt\third_party\gperftools-2.7\dist\src\fake_stacktrace_scope.obj build\opt\mongo\util\debugger.obj build\opt\mongo\util\boost_assert_shim.obj build\opt\mongo\shell\mongodbcr.obj build\opt\mongo\shell\shell_options_init.obj ================================================ FILE: cmake/readme.md ================================================ Quick reference to CMake ======================== if() True if the constant is `1`, `ON`, `YES`, `TRUE`, `Y`, or a non-zero number. False if the constant is `0`, `OFF`, `NO`, `FALSE`, `N`, `IGNORE`, `NOTFOUND`, the empty string, or ends in the suffix `-NOTFOUND`. Named boolean constants are case-insensitive. If the argument is not one of these constants, it is treated as a variable. if() True if the variable is defined to a value that is not a false constant. False otherwise. (Note macro arguments are not variables.) if(DEFINED ) True if the given variable is defined. It does not matter if the variable is true or false just if it has been set. (Note macro arguments are not variables.) ================================================ FILE: docs/BuildRobo3TOnMacAndLinux.md ================================================ Building Robo 3T (macOS and Linux) ================== A. Prerequisites ------------- 1. Modern Compiler A modern and complete C++17 compiler: ``` GCC 8.0 or newer Clang 7 (or Apple XCode 10 Clang) or newer On Linux and macOS, the libcurl library and header is required. macOS includes libcurl. Fedora/RHEL - dnf install libcurl-devel Ubuntu/Debian - apt-get install libcurl-dev Python 3.7 ``` For details see: https://github.com/mongodb/mongo/wiki/Build-Mongodb-From-Source 2. Install CMake (3.10.0 or later) Example: ``` sudo apt-get install build-essential // or wget http://www.cmake.org/files/v3.10/cmake-3.10.0.tar.gz tar xf cmake-3.10.0.tar.gz cd cmake-3.10.0 ./configure // macos: env CC=gcc ./bootstrap make sudo make install (sudo make uninstall) ``` 3. Install [Scons](http://scons.org/tag/releases.html) (3.1.2 or later) ``` cd scons-** python setup.py install ``` 4. Install Qt macOS: Qt 5.12.8 Linux: Qt 5.9.3 ```sh Example installation for macOS and Ubuntu: Go to http://download.qt.io/archive/qt/5.x/5.x.y/ Download, run and install qt-opensource-mac-x64-clang-5.x.y.dmg (for macOS) qt-opensource-linux-x64-5.x.y.run (for Linux) After successful installation you should have /path/to/qt-5.x.y/5.x/clang_64 (for macOS) /path/to/qt-5.x.y/5.x/gcc_64 (for Linux) More information for installing on Ubuntu: https://wiki.qt.io/Install_Qt_5_on_Ubuntu ``` 5. Download and Build OpenSSL - (explained below in section B) B. Building Robo 3T and Dependencies ------------- Ref: https://wiki.openssl.org/index.php/Compilation_and_Installation#OS_X #### 1. Build OpenSSL (Todo: Can we find pre-built OpenSSL? To check => https://wiki.openssl.org/index.php/Binaries) Version 1.1.1f (Mar/2020) on macOS Version 1.0.2o (Mar/2018) on Linux **macOS:** ```sh wget https://www.openssl.org/source/old/1.1.1/openssl-1.1.1f.tar.gz tar -xf openssl-1.1.1f.tar.gz cd /opt/openssl-1.1.1f (make clean) ./Configure darwin64-x86_64-cc shared no-ssl2 no-ssl3 no-comp -mmacosx-version-min=xy.zt // Notes: // 1. Change -mmacosx-version same as CMAKE_OSX_DEPLOYMENT_TARGET in // https://github.com/Studio3T/robomongo/blob/master/CMakeLists.txt // 2. With openssl version 1.1.1, configure command with rpath stopped working // Last working configure command with rpath was: // ./Configure darwin64-x86_64-cc shared --openssldir="@rpath" make (or sudo make) // Verify libssl.dylib and libcrypto.dylib files are created ll lib*.dylib // Due to broken './Configure' command with rpath above, these extra steps are also required: install_name_tool -id "@rpath/lib/libssl.1.1.dylib" libssl.1.1.dylib install_name_tool -change /usr/local/lib/libcrypto.1.1.dylib @rpath/lib/libcrypto.1.1.dylib libssl.1.1.dylib install_name_tool -id "@rpath/lib/libcrypto.1.1.dylib" libcrypto.1.1.dylib // Finally, we should have this output using otool: otool -L libssl.1.1.dylib libssl.dylib: @rpath/lib/libssl.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) @rpath/lib/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1) otool -L libcrypto.1.1.dylib libcrypto.dylib: @rpath/lib/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1) // For robo-unit-test dependency: mkdir lib/ cp lib*.dylib/lib/ ``` **Linux:** ```sh wget https://www.openssl.org/source/old/1.0.2/openssl-1.0.2o.tar.gz tar -xf openssl-1.0.2o.tar.gz cd /opt/openssl-1.0.2o (make clean) ./config shared make // Verify libssl.so and libcrypto.so are created ``` #### 2. Build Robo 3T Shell (fork of MongoDB) Clone Robo 3T Shell and make sure you are on latest branch: ```sh $ git clone https://github.com/paralect/robomongo-shell.git $ cd robomongo-shell $ git branch // "roboshell-v4.2" ``` Set special environment variable `ROBOMONGO_CMAKE_PREFIX_PATH` to point to a set of directories: 1. Location of Qt 5 SDK 2. Location of Robo 3T Shell 3. Location of OpenSSL Separate directories by semicolon `;` (not colon): // macOS example: export ROBOMONGO_CMAKE_PREFIX_PATH="/path/to/qt-5.x.y/5.x/clang_64;/opt/robo-shell;/opt/openssl-x.y.z" // Ubuntu example: export ROBOMONGO_CMAKE_PREFIX_PATH="/home//Qt5.x.y/5.x.y/gcc_64/;/opt//robo-shell;/opt/openssl-x.y.z" Install pip requirements ``` // macOS / Linux pip3 install --user -r etc/pip/compile-requirements.txt pip3 install --user -r etc/pip/dev-requirements.txt ``` ``` // For ubuntu, the followings might be needed sudo aptitude install libcurl-dev sudo pip3 install Typing sudo pip3 install Cheetah ``` Build Robo 3T Shell (in release mode by default): ```sh $ bin/build ``` Build Robo 3T Shell in debug mode: ```sh $ bin/build debug ``` Clean build files for release mode: ```sh $ bin/clean ``` Clean build files for debug mode: ```sh $ bin/clean debug ``` For more information refer to [Building Robo 3T Shell](BuildRobo3TShell.md) #### 3. Build Robo 3T Download Robo 3T: $ git clone https://github.com/Studio3T/robomongo $ cd robomongo Run configuration step: $ bin/configure And finally, build Robo 3T: $ bin/build To run robomongo: $ bin/run **Debug mode** For debug mode append `debug` for each command e.g. `bin/configure debug` or `bin/build debug` etc.. **Helper commands** Clean build files (in order to start build from scratch): $ bin/clean Install Robo 3T to `build/release/install` folder: $ bin/install Create package for your OS in `build/release/package` folder: $ bin/pack **Important Notes** - For macOS builds, it might be needed to patch QScintilla with `QScintilla-2.9.3-xcode8.patch` found at robomongo repository's root. - For Ubuntu 16.04 builds, it has been reported that Robo 3T /bin/configure step might fail and installing `mesa-common-dev` package solves it. Details: https://github.com/paralect/robomongo/issues/1268 - For Centos builds, Robo 3T /bin/configure step might fail due to error: `failed to find gl/gl.h`. In this case, the following packages must be installed: ```sh $ sudo yum install mesa-libGL $ sudo yum install mesa-libGL-devel ``` ================================================ FILE: docs/BuildRobo3TOnWindows.md ================================================ Building Robo 3T (Windows) ============================== A. Prerequisites ------------- #### 0. Git Download Git installer from http://git-scm.com (we are using Git-2.7.0-64-bit). When you'll see "Adjusting your PATH environment" wizard page, select "Use Git from the Windows Command Prompt" option. This will allow to execute Git from both Windows Command Prompt and Bash. This is required, because both MongoDB and Robo 3T uses `git` command during build process which must be perfomed from Windows Command Prompt (not Bash). On "Configuring the line ending conversion" wizard page, select "Checkout Windows-style, commit Unix-style line ending" option. This is a common mode for cross-platform projects and required by both MongoDB and Robo 3T. All other Git installer options are not important (use defaults). If you are using SSH clone url, start SSH agent and add your private key with this commands in Git Bash: $ eval `ssh-agent` # starts ssh agent $ ssh-add ~/.ssh/mykey # add your key (which can be in any folder) #### 1. Visual Studio 2017 version 15.9 or newer (complete C++17 compiler) We use Visual Studio 2017 version 15.9.22, and recommend to use this version to save time for Qt compilation. Currently prebuilt binaries for Qt are available from official Qt site. But you can use any newer version and compile Qt library yourself. #### 2. ActivePython 3.7.4 Download ActivePython from http://www.activestate.com/activepython/downloads Download MSI x64 version. Use default settings in installation wizard. This installer will add path to `python.exe` to your `PATH` variable. #### 3. SCons 3.1.2 or newer We use SCons 3.1.2 Download Scons from http://scons.org/tag/releases.html Use ZIP archive, not MSI Open Command Prompt, navigate to Scons directory and run: $ setup.py install This command will make `scons` command accessible from Command Prompt #### 4. CMake We use CMake 3.10.0 Download CMake from https://cmake.org/download Use installer package, it will allow you to configure your PATH variable and `cmake` command will be available from Command Prompt. For this set "Add CMake to the system PATH for current user" in the installer wizard page. #### 5. Qt 5.12.8 Download and install Qt from http://download.qt.io/archive/qt Run the installer and select only 'msvc2017 64-bit' #### 6. OpenSSL (1.1.1f) Download openssl from https://www.openssl.org/source/old/1.1.1/openssl-1.1.1f.tar.gz B. Building Robo 3T and Dependencies ------------- #### 1. Build OpenSSL (Todo: Can we find pre-built OpenSSL? To check => https://wiki.openssl.org/index.php/Binaries) Steps to build OpenSSL on windows: - Ensure you have perl installed on your machine (e.g. ActiveState or Strawberry), and available on your %PATH% - Ensure you have NASM installed on your machine, and available on your %PATH% ```sh Open Visual Studio tool x64 Cross Tools Command prompt cd to the directory where you have openssl sources cd c:\myPath\openssl perl Configure VC-WIN64A (nmake clean) nmake // Verify these files are created: libcrypto-1_1x64.dll, libssl-1_1-x64.dll (with all the build additionals such as .pdb .lik or static .lib) ``` Refer to OpenSSL documentation for more information: https://wiki.openssl.org/index.php/Compilation_and_Installation#OpenSSL_1.1.0 #### 2. Build Robo 3T Shell (fork of MongoDB) Clone Robo 3T Shell and make sure to be at `roboshell-v4.2` branch: ```sh $ git clone https://github.com/paralect/robomongo-shell.git $ cd robomongo-shell $ git branch // roboshell-v4.2 ``` Set environment variable `ROBOMONGO_CMAKE_PREFIX_PATH`, required by Robo 3T-Shell and Robo 3T build scripts, needs to be set according to the following directories: 1. Location of Qt SDK 2. Location of Robo 3T Shell 3. Location of OpenSSL **Important:** Make sure to move all your repos (robo, openssl, robo-shell etc..) into root directory of your drives (e.g. e:\robo-shell) and keep short repo names (e.g. robo-shell instead of robomongo-shell) otherwise you might end up with this error `If you see fatal error LNK1170: line in command file contains 131071 or more characters` Separate directories by semicolon `;` (not colon). You can do this in Command Prompt: > setx ROBOMONGO_CMAKE_PREFIX_PATH "e:\Qt\Qt5.9.3\5.9.3\msvc2015_64;e:\robo-shell;e:\openssl-x.y.z" Open '**a new**' VS2017 x64 Native Tools Command Prompt and navigate to `robomongo-shell` folder. **Note:** Run `pip3 install --user -r ../etc/pip/dev-requirements.txt` If you have this type of errors: ``` // 1st error ImportError: No module named typing // Solution: pip install Typing pip install Cheetah // 2nd error: shell: cheetah module not found // Solution: copy C:\Users\\AppData\Roaming\Python\Python27\site-packages to C:\Python27\Lib\site-packages ``` Build shell in release mode: > bin\build Build shell in debug mode: > bin\build debug Note that backslash is used (`\`), and not forward slash (`/`). Clean build files for release mode: > bin\clean Clean build files for debug mode: > bin\clean debug Refer to MongoDB documentation for additional information: https://docs.mongodb.org/manual/contributors/tutorial/build-mongodb-from-source/#windows-specific-instructions #### 3. Build Robo 3T Clone Robo 3T: ```sh $ git clone https://github.com/paralect/robomongo.git ``` Open VS2017 x64 Native Tools Command Prompt and navigate to `robomongo` folder. Run configuration step: > bin\configure And finally, build Robo 3T: > bin\build ``` // If you see fatal error LNK1170: line in command file contains 131071 or more characters Solution-1: 1. Move all your repos (robo, openssl, robo-shell etc..) into root directory of your drives. 2. Use short names e.g. robo-shell instead of robomongo-shell. 3. Update ROBOMONGO_CMAKE_PREFIX_PATH env. variable and try a fresh (clean) build. Solution-2: 1. Create environment variable MongoDB_OBJECTS, set a short path e.g. "C:/" 2. Open a new VS2017 x64 Native Tools Command Prompt and build Robo 3T Shell 3. Robo 3T Shell build script will copy the object files into MongoDB_OBJECTS directory 4. Build Robo 3T again (clean build might be needed) ``` **Run Robo 3T** Install Robo 3T to `build\Release\install` folder: > bin\install And run Robo 3T > \robomongo\build\Release\install\robo3t.exe **Package Robo 3T** Install Robo 3T to `build\Release\install` folder: > bin\pack Note: nsis-2.46-setup.exe with Full setup must be present. **Debug mode** For debug mode append `debug` for each command e.g. `bin\configure debug` or `bin\build debug` etc.. **Helper commands** Clean build files (in order to start build from scratch): > bin\clean ================================================ FILE: docs/BuildRobo3TShell.md ================================================ Building Robo 3T Shell ======================== Robo 3T uses [modified version](https://github.com/paralect/robomongo-shell/tree/roboshell-v3.4) of MongoDB that we call Robo 3T Shell. Before you can build Robo 3T, you have to build Robo 3T Shell. The following instructions are applicable only for Mac OS X and Linux. For Windows instructions please check [Building Robo 3T on Windows](BuildRobo3TOnWindows.md). Build for Mac OS X or Linux --------------------------- #### Prerequisites 1. Install [Scons](http://scons.org/tag/releases.html) (2.3.5 or newer) 2. Clone Robo 3T Shell and checkout to `roboshell-v3.4` branch: ```sh $ git clone https://github.com/paralect/robomongo-shell.git $ cd robomongo-shell $ git checkout roboshell-v3.4 ``` 3. Build Robo 3T Shell: ```sh $ bin/build ``` Done! Now you can continue with building Robo 3T with embedded MongoDB 3.4 shell (that you've just built).
#### Advanced helper commands The following commands are needed only if you are planning to develop or deeper understand Robo 3T or Robo 3T Shell build processes. Clean build files for release mode (folder `build/opt` will be removed): $ bin/clean Here is a command that is executed by `bin/build` script: $ scons mongo -j8 --release --osx-version-min=10.9 Argument `--osx-version-min` is required only for Mac OS. Build MongoDB shell in debug mode: $ scons mongo -j8 --dbg Clear builds: $ scons -c mongo # clears release build $ scons -c --dbg mongo # clears debug build ================================================ FILE: docs/BuildingMongoDB.md ================================================ Building Robomongo Shell (Old Document) ======================== **Important Note:** This document is for versions 1.0 and earlier and for MongoDB 3.2. See latest documents here: https://github.com/Studio3T/robomongo/wiki Robomongo uses [modified version](https://github.com/paralect/robomongo-shell/tree/roboshell-v3.2) of MongoDB that we call Robomongo Shell. Before you can build Robomongo, you have to build Robomongo Shell. The following instructions are applicable only for Mac OS X and Linux. For Windows instructions please check [Building Robomongo on Windows](BuildingRobomongoOnWindows.md). Build for Mac OS X or Linux --------------------------- #### Prerequisites 1. Install [Scons](http://scons.org/tag/releases.html) (2.4 or later) 2. Clone Robomongo Shell and checkout to `roboshell-v3.2` branch: ```sh $ git clone https://github.com/paralect/robomongo-shell.git $ cd robomongo-shell $ git checkout roboshell-v3.2 ``` 3. Build Robomongo Shell: ```sh $ bin/build ``` Done! Now you can continue with [Step 2](BuildingRobomongo.md#step-2) and build Robomongo with embedded MongoDB 3.2 shell (that you've just built).
#### Advanced helper commands The following commands are needed only if you are planning to develop or deeper understand Robomongo or Robomongo Shell build processes. Clean build files for release mode (folder `build/opt` will be removed): $ bin/clean Here is a command that is executed by `bin/build` script: $ scons mongo -j8 --release --osx-version-min=10.9 Argument `--osx-version-min` is required only for Mac OS. Build MongoDB shell in debug mode: $ scons mongo -j8 --dbg Clear builds: $ scons -c mongo # clears release build $ scons -c --dbg mongo # clears debug build ================================================ FILE: docs/BuildingRobomongo.md ================================================ Building Robomongo (Mac OS X and Linux) ================== **Important Note:** This document is for versions 1.0 and earlier and for MongoDB 3.2. See latest documents here: https://github.com/Studio3T/robomongo/wiki A. Prerequisites ------------- 1. Install CMake (3.2 or later) 2. Install [Scons](http://scons.org/tag/releases.html) (2.4 or later) 3. Install Qt 5.7 ```sh Example installation for MAC OSX and Ubuntu: Go to http://download.qt.io/archive/qt/5.7/5.7.0/ Download, run and install qt-opensource-mac-x64-clang-5.7.0.dmg (for MAC OSX) qt-opensource-linux-x64-5.7.0.run (for Linux) After successful installation you should have /path/to/qt-5.7.0/5.7/clang_64 (for MAC OSX) /path/to/qt-5.7.0/5.7/gcc_64 (for Linux) More information for installing on Ubuntu: https://wiki.qt.io/Install_Qt_5_on_Ubuntu ``` 4. Download and Build OpenSSL (1.0.1p) - (explained below in section B) B. Building Robomongo and Dependencies ------------- #### Step 1. Build OpenSSL (1.0.1p) Steps to build OpenSSL on MAC: ```sh Download openssl-1.0.1p (ftp://ftp.openssl.org/source/old/1.0.1/openssl-1.0.1p.tar.gz) tar -xvzf openssl-1.0.1p.tar.gz cd /Users//Downloads/openssl-1.0.1p ./Configure darwin64-x86_64-cc shared --openssldir="@rpath" make (or sudo make) mkdir lib cp libssl.1.0.0.dylib libcrypto.1.0.0.dylib lib/ ``` Helper Commands ```sh // to start fresh build make clean ``` Steps to build OpenSSL on Linux: ```sh Download openssl-1.0.1p (ftp://ftp.openssl.org/source/old/1.0.1/openssl-1.0.1p.tar.gz) tar -xvzf openssl-1.0.1p.tar.gz cd /home//Downloads/openssl-1.0.1p ./config shared make mkdir lib cp libssl* libcrypto* lib/ ``` #### Step 2. Build Robomongo Shell (fork of MongoDB) Clone Robomongo Shell and checkout to `roboshell-v3.2` branch: ```sh $ git clone https://github.com/paralect/robomongo-shell.git $ cd robomongo-shell $ git checkout roboshell-v3.2 ``` Set special environment variable `ROBOMONGO_CMAKE_PREFIX_PATH` to point to a set of directories: 1. Location of Qt 5 SDK 2. Location of Robomongo Shell 3. Location of OpenSSL Separate directories by semicolon `;` (not colon): // MAC OSX example: $ export ROBOMONGO_CMAKE_PREFIX_PATH="/path/to/qt-5.7.0/5.7/clang_64;/path/to/robomongo-shell;/path/to/openssl-1.0.1p" // Ubuntu example: $ export ROBOMONGO_CMAKE_PREFIX_PATH="/path/to/qt-5.7.0/5.7/gcc_64;/path/to/robomongo-shell;/path/to/openssl-1.0.1p" Build Robomongo Shell: ```sh $ bin/build ``` For more information refer to [Building Robomongo Shell](BuildingMongoDB.md) #### Step 3. Build Robomongo Download Robomongo: $ git clone https://github.com/paralect/robomongo $ cd robomongo Run configuration step: $ bin/configure And finally, build Robomongo: $ bin/build To run robomongo: $ bin/run **Helper commands** Clean build files (in order to start build from scratch): $ bin/clean Install Robomongo to `build/release/install` folder: $ bin/install Create package for your OS in `build/release/package` folder: $ bin/pack **Important Notes** - For Ubuntu 16.04 builds, it has been reported that Robomongo /bin/configure step might fail and installing mesa-common-dev package solves it. Details: https://github.com/paralect/robomongo/issues/1268 - For Centos builds, Robomongo /bin/configure step might fail due to error: `failed to find gl/gl.h`. In this case, the following packages must be installed: ```sh $ sudo yum install mesa-libGL $ sudo yum install mesa-libGL-devel ``` ================================================ FILE: docs/BuildingRobomongoOnWindows.md ================================================ Building Robomongo (Windows) ============================== **Important Note:** This document is for versions 1.0 and earlier and for MongoDB 3.2. See latest documents here: https://github.com/Studio3T/robomongo/wiki A. Prerequisites ------------- #### 0. Git Download Git installer from http://git-scm.com (we are using Git-2.7.0-64-bit). When you'll see "Adjusting your PATH environment" wizard page, select "Use Git from the Windows Command Prompt" option. This will allow to execute Git from both Windows Command Prompt and Bash. This is required, because both MongoDB and Robomongo uses `git` command during build process which must be perfomed from Windows Command Prompt (not Bash). On "Configuring the line ending conversion" wizard page, select "Checkout Windows-style, commit Unix-style line ending" option. This is a common mode for cross-platform projects and required by both MongoDB and Robomongo. All other Git installer options are not important (use defaults). If you are using SSH clone url, start SSH agent and add your private key with this commands in Git Bash: $ eval `ssh-agent` # starts ssh agent $ ssh-add ~/.ssh/mykey # add your key (which can be in any folder) #### 1. Visual Studio 2013 (i.e. MSVC12) Update 4 or newer We use Visual Studio 2013 Update 5, and recommend to use this version to save time for Qt compilation. Currently prebuilt binaries for Qt are available from official Qt site. But you can use any newer version and compile Qt library yourself. If you already have VS2013, you can download Update 5 here: https://www.microsoft.com/en-us/download/details.aspx?id=48129 Update 4 or newer version is required to compile MongoDB. #### 2. ActivePython 2.7 Download ActivePython from http://www.activestate.com/activepython/downloads Download MSI x64 version. Use default settings in installation wizard. This installer will add path to `python.exe` to your `PATH` variable. #### 3. Scons 2.3.4 Download Scons from http://scons.org/tag/releases.html Use ZIP archive, not MSI (we use scons-2.3.4.zip) Open Command Prompt, navigate to Scons directory and run: $ setup.py install This command will make `scons` command accessible from Command Prompt #### 4. CMake Download CMake from https://cmake.org/download (we use cmake-3.3.2-win32-x86.exe) Use installer package, it will allow you to configure your PATH variable and `cmake` command will be available from Command Prompt. For this set "Add CMake to the system PATH for current user" in the installer wizard page. #### 5. Qt 5.7 Download Qt from http://www.qt.io/download-open-source/ (we use Qt Online installer) When installing Qt, only one component is required by Robomongo: Qt 5.7 [x] msvc2013 64-bit. This components contains prebuilt 64-bit binaries for Visual Studio 2013. You may uncheck all other components to download as little as possible. #### 6. OpenSSL (1.0.1p) Download openssl-1.0.1p (ftp://ftp.openssl.org/source/old/1.0.1/openssl-1.0.1p.tar.gz) B. Building Robomongo and Dependencies ------------- #### 1. Build OpenSSL (1.0.1p) Steps to build OpenSSL on windows: ```sh Open Visual Studio tool x64 Cross Tools Command prompt cd to the directory where you have openssl sources cd c:\myPath\openssl perl Configure VC-WIN64A ms\do_win64a nmake -f ms\ntdll.mak ``` **Check Point:** After successful build, newly created sub directory out32dll should contain dynamic lib files libeay32.lib (and libeay32.dll) and ssleay32.lib (and ssleay32.dll); and associated include files will be in newly created folder "inc32". Helper Commands: ```sh // clean to start fresh build nmake -f ms\ntdll.mak clean ``` Refer to OpenSSL documentation for more information: https://wiki.openssl.org/index.php/Compilation_and_Installation#W64 #### 2. Build Robomongo Shell (fork of MongoDB) Clone Robomongo Shell and checkout to roboshell-v3.2 branch: ```sh $ git clone https://github.com/paralect/robomongo-shell.git $ cd robomongo-shell $ git checkout roboshell-v3.2 ``` Set environment variable `ROBOMONGO_CMAKE_PREFIX_PATH`, required by Robomongo-Shell and Robomongo build scripts, needs to be set according to the following directories: 1. Location of Qt SDK 2. Location of Robomongo Shell 3. Location of OpenSSL Separate directories by semicolon `;` (not colon). You can do this in Command Prompt: > setx ROBOMONGO_CMAKE_PREFIX_PATH "d:\Qt-5\5.7\msvc2013_64;d:\Projects\robomongo-shell;c:\myPath\openssl-1.0.1p" Open VS2013 x64 Native Tools Command Prompt and navigate to `robomongo-shell` folder. Build shell: > bin\build Note that backslash is used (`\`), and not forward slash (`/`). Clean build files: > bin\clean Refer to MongoDB documentation for additional information: https://docs.mongodb.org/manual/contributors/tutorial/build-mongodb-from-source/#windows-specific-instructions #### 3. Build Robomongo Clone Robomongo: ```sh $ git clone https://github.com/paralect/robomongo.git ``` Open VS2013 x64 Native Tools Command Prompt and navigate to `robomongo` folder. Run configuration step: > bin\configure And finally, build Robomongo: > bin\build **Run Robomongo** Install Robomongo to `build\Release\install` folder: > bin\install And run Robomongo > \robomongo\build\Release\install\Robomongo.exe **Helper commands** Clean build files (in order to start build from scratch): > bin\clean ================================================ FILE: docs/Debug.md ================================================ Runtime Dependencies ==================== This information is for maintainers. If you want to just use Robomongo - download prebuild packages for your platform. Runtime dependencies that are not privided by platforms but required by Robomongo are: 1. Qt libraries 2. Qt plugins Diagnostic of dependencies -------------------------- ### Windows GUI: http://dependencywalker.com/ CMD LINE: Open Visual Studio Native command prompt `dumpbin /dependents robo3t.exe` ### Linux #### a. View dependencies of executable or shared library: $ ldd robomongo $ readelf -d robomongo #### b. View dependency tree of executable or shared library: $ lddtree robomongo $ ldd -v robomongo #### c. View RPATH or RUNPATH records of executable or shared library: $ objdump -x bin/robomongo | grep "RPATH\|RUNPATH" $ readelf -d bin/robomongo | grep RPATH For Robomongo binary it should be `$ORIGIN/../lib` #### d. Enable extensive logging for run-time shared library search $ export LD_DEBUG=files $ bin/robomongo Source: http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html #### e. Modify RPATH using `chrpath` and the dynamic linker and RPATH using paths `patchelf` $ patchelf --set-rpath '$ORIGIN/../lib' robo More: https://nixos.org/patchelf.html $ chrpath -r "/somepath/lib/" robomongo More: https://linux.die.net/man/1/chrpath http://www.programering.com/a/MTOwcDNwATU.html // examples #### f. Show symbols of shared lib file Error: ``` /opt/robo-shell/src/mongo/util/net/ssl_manager_openssl.cpp:667: undefined reference to `SSL_library_init' ``` How to debug: ``` openssl-1.1.1f: nm -gD libssl.so | grep SSL_library_init // not found, since SSL_library_init() is deprecated in 1.1.1 openssl-1.0.2o: nm -gD libssl.so | grep SSL_library_init 0000000000055520 T SSL_library_init ``` #### g. Update path of a dependency of an executable ``` ldd exec_file | grep libcurlpp libcurlpp.so.1 => not found patchelf --replace-needed libcurlpp.so.1 /foo/bar/libcurlpp.so.1 exec_file ``` Another example (libcrypto.so.1.1 => not found): ``` cd /opt/openssl-1.1.1f ldd libssl.so linux-vdso.so.1 => (0x00007ffcaf911000) libcrypto.so.1.1 => not found libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fd4087b4000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd4083ea000) /lib64/ld-linux-x86-64.so.2 (0x00007fd408c65000) sudo cp libcrypto.so.1.1 /lib/x86_64-linux-gnu/ ldd libssl.so linux-vdso.so.1 => (0x00007fff0f5d8000) libcrypto.so.1.0.0 => /lib/x86_64-linux-gnu/libcrypto.so.1.1 (0x00007fd3dcc1e000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd3dc854000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fd3dc650000) /lib64/ld-linux-x86-64.so.2 (0x00007fd3dd2d6000) ``` ### Mac OS X #### a. Show dependencies of executable or shared library: Show dependencies of executable or shared library: $ otool -L robomongo View LC_RPATH records of executable or shared library: $ otool -l robomongo | grep -C2 RPATH (option `-C2` asks grep to output 2 context lines before and after the match) For Robomongo binary it should be `$executable_path/../Frameworks` More about `otool -l` ``` $ otool -l /opt/robo/build/release/src/robomongo/Robo\ 3T.app/Contents/MacOS/Robo\ 3T .... Load command 23 cmd LC_LOAD_DYLIB cmdsize 56 name @rpath/lib/libssl.1.1.dylib (offset 24) time stamp 2 Thu Jan 1 03:00:02 1970 current version 1.1.0 compatibility version 1.1.0 Load command 24 cmd LC_LOAD_DYLIB cmdsize 56 name @rpath/lib/libcrypto.1.1.dylib (offset 24) time stamp 2 Thu Jan 1 03:00:02 1970 current version 1.1.0 compatibility version 1.1.0 .... Load command 27 cmd LC_RPATH cmdsize 48 path /opt/Qt5.9.3/5.9.3/clang_64/lib (offset 12) Load command 28 cmd LC_RPATH cmdsize 32 path /opt/openssl-1.1.1f (offset 12) ``` #### b. Edit dependencies of executable or shared library: ``` // Before otool -L libssl.dylib libssl.dylib: /usr/local/lib/libssl.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) /usr/local/lib/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1) // Edit install_name_tool -id "@rpath/lib/libssl.1.1.dylib" libssl.dylib install_name_tool -change /usr/local/lib/libcrypto.1.1.dylib @rpath/lib/libcrypto.1.1.dylib libssl.dylib // After otool -L libssl.dylib libssl.dylib: @rpath/lib/libssl.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) @rpath/lib/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1) ``` Diagnostic of Qt plugins ------------------------ Run Robomongo in the following way to see how plugins are resolved: $ QT_DEBUG_PLUGINS=1 ./robomongo ================================================ FILE: install/linux/robomongo.sh ================================================ #!/bin/bash #ROBO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" SOURCE="$(readlink "$SOURCE")" [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located done ROBO_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" export LD_LIBRARY_PATH="$ROBO_DIR/../lib:$LD_LIBRARY_PATH" export QT_PLUGIN_PATH="$ROBO_DIR/../lib:$QT_PLUGIN_PATH" export QT_XKB_CONFIG_ROOT=/usr/share/X11/xkb export XKB_DEFAULT_RULES=base "$ROBO_DIR/robomongo" ================================================ FILE: install/macosx/Info.plist.in ================================================ NSPrincipalClass NSApplication CFBundleDevelopmentRegion English CFBundleExecutable Robo 3T CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier ${MACOSX_BUNDLE_GUI_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType APPL CFBundleShortVersionString ${MACOSX_BUNDLE_SHORT_VERSION_STRING} CFBundleSignature ???? CFBundleVersion ${MACOSX_BUNDLE_BUNDLE_VERSION} CSResourcesFileMapped LSRequiresCarbon NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} NSHighResolutionCapable True NSRequiresAquaSystemAppearance ================================================ FILE: install/macosx/README.md ================================================ How to generate "icns" icon file ================================ In this directory run: $ iconutil -c icns Robomongo.iconset File `Robomongo.icns` will be generated. ================================================ FILE: install/macosx/qt.conf ================================================ [Paths] Plugins = PlugIns/Qt ================================================ FILE: install/qt.conf.in ================================================ [Paths] Plugins = @qt_conf_plugins@ ================================================ FILE: install/windows/README.md ================================================ How to generate ICO file from the set of PNGs ============================================= Install ImageMagick (http://www.imagemagick.org/). Make sure that you have added its utils to your PATH (installer will give you such an option) In `icon-set` directory run: > convert 256x256.png 128x128.png 64x64.png 48x48.png 40x40.png 32x32.png 24x24.png 20x20.png 16x16.png robomongo.ico File `robomongo.ico` will be generated. Which icon sizes are used on Windows? ===================================== The most important sizes are the one that is small. Robo 3T should include 16, 20, 24, 40, 48. The bigger sizes are usually scaled well, without notable degrading in quality. The following statistic was provided in this answer: http://stackoverflow.com/a/12283978/407599 > After some testing with an icon with 8, 16, 20, 24, 32, 40, 48, 64, 96, 128 > and 256 pixels (256 in PNG) in Windows 7: > > At 100% resolution: > Explorer uses 16, 40, 48, and 256. Windows Photo Viewer uses 96. Paint uses 256. > > At 125% resolution: > Explorer uses 20, 40, and 256. Windows Photo Viewer uses 96. Paint uses 256. > > At 150% resolution: > Explorer uses 24, 48, and 256. Windows Photo Viewer uses 96. Paint uses 256. > > At 200% resolution: > Explorer uses 40, 64, 96, and 256. Windows Photo Viewer uses 128. Paint uses 256. > > So 8, 32 were never used (it's strange to me for 32) and 128 only by Windows Photo > Viewer with a very high dpi screen, i.e. almost never used. > > It means your icon should at least provide 16, 48 and 256 for Windows 7. For supporting > newer screens with high resolutions, you should provide 16, 20, 24, 40, 48, 64, 96, and 256. > For Windows 7, all pictures can be compressed using PNG but for backward compatibility with > Windows XP, 16 to 48 should not be compressed. ================================================ FILE: install/windows/winres.rc.in ================================================ #include "verrsrc.h" IDI_ICON1 ICON "@windows_icon@" #define VER_FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@ #define VER_FILEVERSION_STR "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@\0" #define VER_PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@ #define VER_PRODUCTVERSION_STR "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@\0" 1 VERSIONINFO FILEVERSION VER_FILEVERSION PRODUCTVERSION VER_PRODUCTVERSION FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_APP FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904e4" BEGIN VALUE "CompanyName", "3T Software Labs Ltd\0" VALUE "FileDescription", "Robo 3T, MongoDB management tool\0" VALUE "FileVersion", VER_FILEVERSION_STR VALUE "LegalCopyright", "Copyright 3T Software Labs Ltd, 2014 - 2017\0" VALUE "ProductName", "Robo 3T\0" VALUE "ProductVersion", VER_PRODUCTVERSION_STR VALUE "OriginalFilename", "robo3t.exe\0" VALUE "InternalName", "Robo 3T\0" END END BLOCK "VarFileInfo" BEGIN /* The following line should only be modified for localized versions. */ /* It consists of any number of WORD,WORD pairs, with each pair */ /* describing a language,codepage combination supported by the file. */ /* */ /* For example, a file might have values "0x409,1252" indicating that it */ /* supports English language (0x409) in the Windows ANSI codepage (1252). */ VALUE "Translation", 0x409, 1252 END END ================================================ FILE: shortcuts.txt ================================================ Robomongo shortcut key reference ================================ "3T", "Robomongo" ### Windows/Linux users Ctrl+Enter or F5 or Ctrl+R Execute query on current tab. If you have selection in query text, only selected text will be executed. Ctrl+M Minimize Robomongo Window Ctrl+N Open new connection dialog. Ctrl+O Open a Javascript file and load contents into active shell console (without executing). Ctrl+S Save contents of active shell console into a .js file. Note: updates existing file if contents were loaded via "Open", otherwise will confirm filename via "Save As" dialog. Shift+Ctrl+S Save contents of active shell console (prompts for filename). Shift+Ctrl+C or Ctrl+/ Toggle comment/uncomment mode for selected lines (or current line, if none are selected) Ctrl+T or Ctrl+Shift+Enter Open new tab, based on current (the same server and database). If you have selection in query text, selected text will be executed in new tab. Ctrl+Shift+T Open duplicate shell tab (same server, database, and query) Ctrl+F4 or Ctrl+W Closes current tab. Ctrl+L Toggle log window. Alt+1, Alt+2, ..., Alt+9 Connect to server with 'this' number (you can reorder connections in Connections menu via drag'n'drop) F2 Enter Tree Mode. Current tab will be immediately displayed in tree mode and all subsequent requests will be in tree mode. F3 Enter Table Mode. Current tab will be immediately displayed in table mode and all subsequent requests will be in table mode. F4 Enter Text Mode. Current tab will be immediately displayed in text mode and all subsequent requests will be in text mode. F5 or Ctrl+Enter Execute query on current tab. If you have selection in query text, only selected text will be executed. F6 Stop execution of currently running script. F10 Toggle result orientation (between vertical and horizontal) for two or more result panes. F11 Toggle fullscreen mode. Ctrl+F11 Toggle line numbers in Javascript editing views Ctrl+Tab Select next tab Ctrl+Shift+Tab Select previous tab F12 Opens connection popup menu. ### OS X users Cmd+Enter or F5 or Cmd+R Execute query on current tab. If you have selection in query text, only selected text will be executed. Cmd+M Minimize Robomongo Window Cmd+N Open new connection dialog. Cmd+O Open a Javascript (.js) file and load contents into active shell console (without executing) Cmd+S Save contents of active shell console into a .js file. Note: updates existing file if contents were loaded via "Open", otherwise will confirm filename via "Save As" dialog. Shift+Cmd+S Save contents of active shell console (prompts for filename). Shift+Ctrl+C or Ctrl+/ Toggle comment/uncomment mode for selected lines (or current line, if none are selected) Cmd+T or Cmd+Shift+Enter Open new tab, based on current (the same server and database). If you have selection in query text, selected text will be executed in new tab. Cmd+Shift+T Open duplicate shell tab (same server, database, and query) Cmd+W or Cmd+F4 Closes current tab. Cmd+L Toggle log window. F2 Enter Tree Mode. Current tab will be immediately displayed in tree mode and all subsequent requests will be in tree mode. F3 Enter Table Mode. Current tab will be immediately displayed in table mode and all subsequent requests will be in table mode. F4 Enter Text Mode. Current tab will be immediately displayed in text mode and all subsequent requests will be in text mode. F5 or Cmd+Enter Execute query on current tab. If you have selection in query text, only selected text will be executed. F6 Stop execution of currently running script. F10 Toggle result orientation (between vertical and horizontal) for two or more result panes. Cmd+F11 Toggle fullscreen mode. Cmd+} or Opt+Cmd+RightArrow Select next tab Cmd+{ or Opt+Cmd+LeftArrow Select previous tab Ctrl+F11 Toggle line numbers in Javascript editing views Cmd + Up/Down arrow Move to start/end of text in editing window Cmd + Left/Right arrow Move to start/end of current line in editing window ================================================ FILE: src/robomongo/.clang-format ================================================ # BasedOnStyle: Google AccessModifierOffset: -4 AlignOperands: false AlignAfterOpenBracket: true AlignEscapedNewlinesLeft: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: false AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true BinPackArguments: false BinPackParameters: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Attach BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 100 CommentPragmas: '' ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IndentCaseLabels: true IndentFunctionDeclarationAfterType: false IndentWidth: 4 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: false Language: Cpp MaxEmptyLinesToKeep: 2 NamespaceIndentation: None ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: false PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: true SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 4 UseTab: Never ================================================ FILE: src/robomongo/CMakeLists.txt ================================================ # Automatically generate code for Qt moc, uic and rcc files # Since CMake 2.8.6 set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) #set(sanitize "-fsanitize=address") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${sanitize}") #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${sanitize}") set(SOURCES # Isolated Scope #1 core/utils/QtUtils.cpp core/utils/StdUtils.cpp core/utils/Logger.cpp core/HexUtils.cpp core/utils/BsonUtils.cpp core/settings/CredentialSettings.cpp core/settings/ConnectionSettings.cpp core/Event.cpp core/Enums.cpp core/EventError.cpp core/EventBusSubscriber.cpp core/EventBusDispatcher.cpp core/EventWrapper.cpp core/EventBus.cpp core/KeyboardManager.cpp core/domain/MongoNamespace.cpp core/domain/MongoFunction.cpp core/domain/MongoUtils.cpp core/domain/MongoCollection.cpp core/domain/MongoCollectionInfo.cpp core/domain/MongoQueryInfo.cpp core/domain/CursorPosition.cpp core/domain/ScriptInfo.cpp core/events/MongoEventsInfo.cpp shell/db/ptimeutil.cpp shell/bson/json.cpp # Isolated Scope #2 core/engine/ScriptEngine.cpp core/events/MongoEvents.cpp core/domain/MongoDocument.cpp gui/AppStyle.cpp core/domain/MongoServer.cpp core/domain/MongoShell.cpp core/domain/MongoDatabase.cpp core/domain/App.cpp core/mongodb/MongoClient.cpp core/mongodb/MongoWorker.cpp core/mongodb/ReplicaSet.cpp core/settings/SettingsManager.cpp core/AppRegistry.cpp utils/StringOperations.cpp utils/common.cpp utils/SimpleCrypt.cpp utils/RoboCrypt.cpp # Isolated Scope #3 gui/GuiRegistry.cpp gui/dialogs/AboutDialog.cpp gui/dialogs/EulaDialog.cpp gui/dialogs/ConnectionAdvancedTab.cpp gui/dialogs/ConnectionAuthTab.cpp gui/dialogs/ConnectionBasicTab.cpp gui/dialogs/ConnectionDiagnosticDialog.cpp gui/dialogs/ConnectionDialog.cpp gui/dialogs/CopyCollectionDialog.cpp gui/widgets/workarea/IndicatorLabel.cpp gui/dialogs/CreateCollectionDialog.cpp gui/dialogs/CreateDatabaseDialog.cpp gui/dialogs/CreateUserDialog.cpp gui/utils/ComboBoxUtils.cpp gui/utils/DialogUtils.cpp # Isolated scope #4 gui/dialogs/PreferencesDialog.cpp gui/dialogs/ConnectionsDialog.cpp gui/dialogs/ExportDialog.cpp gui/dialogs/ChangeShellTimeoutDialog.cpp # Isolated scope #5 gui/editors/PlainJavaScriptEditor.cpp gui/editors/JSLexer.cpp gui/editors/FindFrame.cpp gui/widgets/explorer/AddEditIndexDialog.cpp gui/widgets/workarea/ScriptWidget.cpp # Isolated scope #6 gui/widgets/explorer/ExplorerCollectionTreeItem.cpp gui/widgets/explorer/ExplorerCollectionIndexesDir.cpp gui/widgets/explorer/ExplorerCollectionIndexItem.cpp gui/widgets/explorer/ExplorerTreeItem.cpp gui/widgets/explorer/ExplorerReplicaSetFolderItem.cpp gui/widgets/explorer/ExplorerReplicaSetTreeItem.cpp gui/widgets/explorer/ExplorerDatabaseTreeItem.cpp gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.cpp gui/widgets/explorer/ExplorerUserTreeItem.cpp gui/widgets/explorer/ExplorerFunctionTreeItem.cpp gui/dialogs/DocumentTextEditor.cpp gui/dialogs/FunctionTextEditor.cpp # Isolated scope #7 gui/widgets/explorer/ExplorerServerTreeItem.cpp gui/widgets/explorer/ExplorerTreeWidget.cpp gui/widgets/explorer/ExplorerWidget.cpp gui/widgets/workarea/BsonTableModel.cpp gui/widgets/workarea/BsonTableView.cpp gui/widgets/workarea/BsonTreeItem.cpp gui/widgets/workarea/BsonTreeModel.cpp gui/widgets/workarea/BsonTreeView.cpp core/domain/Notifier.cpp # Isolated scope #8 gui/widgets/workarea/CollectionStatsTreeItem.cpp gui/widgets/workarea/CollectionStatsTreeWidget.cpp gui/widgets/workarea/JsonPrepareThread.cpp gui/widgets/workarea/OutputItemContentWidget.cpp gui/widgets/workarea/OutputItemHeaderWidget.cpp gui/widgets/workarea/OutputWidget.cpp gui/widgets/workarea/PagingWidget.cpp gui/widgets/workarea/ProgressBarPopup.cpp gui/widgets/workarea/QueryWidget.cpp gui/widgets/workarea/WorkAreaTabBar.cpp gui/widgets/workarea/WorkAreaTabWidget.cpp gui/widgets/workarea/WelcomeTab.cpp # Final scope gui/widgets/LogWidget.cpp gui/MainWindow.cpp gui/dialogs/SSHTunnelTab.cpp gui/dialogs/SSLTab.cpp core/settings/SshSettings.cpp core/settings/SslSettings.cpp core/settings/ReplicaSetSettings.cpp core/mongodb/SshTunnelWorker.cpp resources/robo.qrc gui/resources/gui.qrc ) # Robomongo target add_executable(robomongo MACOSX_BUNDLE WIN32 app/main.cpp ${SOURCES}) # Disable WebEngineWidgets for Linux SET(WebEngineWidgets "Qt5::WebEngineWidgets") if(SYSTEM_LINUX) SET(WebEngineWidgets) endif() target_link_libraries(robomongo PRIVATE Qt5::Widgets Qt5::Network Qt5::Xml ${WebEngineWidgets} qjson qscintilla mongodb ssh Threads::Threads) # Target from FindThreads CMake module if(APPLE) find_library(SECURITY NAMES Security) find_library(CORE_FOUNDATION NAMES CoreFoundation) set(SSL_LIBRARIES ${SECURITY} ${CORE_FOUNDATION}) target_link_libraries(robomongo PRIVATE ${SSL_LIBRARIES} -lresolv) endif(APPLE) target_include_directories(robomongo PRIVATE ${CMAKE_HOME_DIRECTORY}/src) # Get build number find_package(Git) if(GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE BUILD_NUMBER OUTPUT_STRIP_TRAILING_WHITESPACE ) else(GIT_FOUND) MESSAGE( FATAL_ERROR "Unable to find Git" ) set(BUILD_NUMBER 0) endif(GIT_FOUND) if(BUILD_NUMBER STREQUAL "") MESSAGE( FATAL_ERROR "Failed to get BUILD_NUMBER" ) endif() set(BUILD_NUMBER ${BUILD_NUMBER} CACHE INTERNAL "") target_compile_definitions(robomongo PRIVATE BUILDING_ROBO PROJECT_NAME="${PROJECT_NAME}" PROJECT_NAME_TITLE="${PROJECT_NAME_TITLE}" PROJECT_COPYRIGHT="${PROJECT_COPYRIGHT}" PROJECT_DOMAIN="${PROJECT_DOMAIN}" PROJECT_COMPANYNAME="${PROJECT_COMPANYNAME}" PROJECT_COMPANYNAME_DOMAIN="${PROJECT_COMPANYNAME_DOMAIN}" PROJECT_GITHUB_FORK="${PROJECT_GITHUB_FORK}" PROJECT_GITHUB_ISSUES="${PROJECT_GITHUB_ISSUES}" PROJECT_VERSION="${PROJECT_VERSION}" PROJECT_VERSION_SHORT="${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" BUILD_NUMBER="${BUILD_NUMBER}" PROJECT_NAME_LOWERCASE="${PROJECT_NAME_LOWERCASE}" PROJECT_QT_VERSION="${Qt5Core_VERSION}" MongoDB_VERSION="${MongoDB_VERSION}" OPENSSL_VERSION="${OPENSSL_VERSION}" LIBSSH2_VERSION="${LIBSSH2_VERSION}" QJSON_VERSION="${QJSON_VERSION}" GOOGLE_TEST_VERSION="${GOOGLE_TEST_VERSION}" ESPRIMA_VERSION="${ESPRIMA_VERSION}" ) if(SYSTEM_WINDOWS) # Create Windows Resource file set(windows_icon "${CMAKE_SOURCE_DIR}/install/windows/robomongo.ico") configure_file( "${CMAKE_SOURCE_DIR}/install/windows/winres.rc.in" "${CMAKE_BINARY_DIR}/winres.rc") unset(windows_icon) # Add additional source file to "robomongo" target target_sources(robomongo PRIVATE "${CMAKE_BINARY_DIR}/winres.rc") # Rename output name for Windows set_target_properties(robomongo PROPERTIES OUTPUT_NAME "robo3t") # Suppress "warning C4477: 'sprintf'..." from robomongo\shell\db\ptimeutil.cpp # Suppress "warning C4291: ...no matching operator delete found" from third_party\mozjs-60 set_target_properties(robomongo PROPERTIES COMPILE_FLAGS "/wd4477 /wd4291") # Start debug program with console set_target_properties(robomongo PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") # Seems that we do not need to do this with recent versions of Qt target_link_libraries(robomongo PRIVATE Qt5::WinMain) elseif(SYSTEM_MACOSX) string(TIMESTAMP CURRENT_YEAR "%Y") set_target_properties(robomongo PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks" OUTPUT_NAME "Robo 3T" MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/install/macosx/Info.plist.in" MACOSX_BUNDLE_BUNDLE_NAME "Robo 3T" MACOSX_BUNDLE_GUI_IDENTIFIER "com.3tsoftwarelabs.robo3t" MACOSX_BUNDLE_INFO_STRING "Robo 3T ${PROJECT_VERSION}. Copyright 3T Software Labs Ltd, 2014 - ${CURRENT_YEAR}" MACOSX_BUNDLE_SHORT_VERSION "${PROJECT_VERSION}" MACOSX_BUNDLE_LONG_VERSION "${PROJECT_VERSION}" MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}" MACOSX_BUNDLE_COPYRIGHT "Copyright 3T Software Labs Ltd, 2014 - ${CURRENT_YEAR}" MACOSX_BUNDLE_ICON_FILE "robomongo") elseif(SYSTEM_LINUX) set_target_properties(robomongo PROPERTIES INSTALL_RPATH "$ORIGIN/../lib" OUTPUT_NAME "robo3t") endif() # Install include(RobomongoInstall) # # Tests targets (code below should be moved to separate file) add_executable(tests WIN32 EXCLUDE_FROM_ALL app/main_test.cpp gui/editors/JSLexer.cpp) target_link_libraries(tests Qt5::Widgets qjson qscintilla mongodb Threads::Threads) target_include_directories(tests PRIVATE ${CMAKE_HOME_DIRECTORY}/src) # Target that creates original MongoDB shell # Used to test compilation and linking add_executable(shell EXCLUDE_FROM_ALL shell/shell/dbshell.cpp) target_link_libraries(shell mongodb Threads::Threads) # Target from FindThreads CMake module ================================================ FILE: src/robomongo/app/main.cpp ================================================ #include #include #include // Header "mongo/util/net/sock" is needed for mongo::enableIPv6() // Header "mongo/platform/basic" is required by "sock.h" under Windows #include #include #include #include #include #include #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/gui/MainWindow.h" #include "robomongo/gui/AppStyle.h" #include "robomongo/gui/dialogs/EulaDialog.h" #include "robomongo/ssh/ssh.h" #include "robomongo/utils/RoboCrypt.h" int main(int argc, char *argv[], char** envp) { if (rbm_ssh_init()) return 1; // Please check, do we really need envp for other OSes? #ifdef Q_OS_WIN envp = NULL; #endif // Support for IPv6 is disabled by default. Enable it. mongo::enableIPv6(true); // Perform SSL-enabled mongo initialization mongo::sslGlobalParams.sslMode.store(mongo::SSLParams::SSLMode_allowSSL); // Cross Platform High DPI support - Qt 5.7 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // Initialization routine for MongoDB shell mongo::runGlobalInitializersOrDie(argc, argv, envp); mongo::setGlobalServiceContext(mongo::ServiceContext::make()); // Todo from mongo repo: This should use a TransportLayerManager or TransportLayerFactory auto serviceContext = mongo::getGlobalServiceContext(); mongo::transport::TransportLayerASIO::Options opts; // When true, it breaks connection to localhost, github #1757 opts.enableIPv6 = mongo::shellGlobalParams.enableIPv6; opts.mode = mongo::transport::TransportLayerASIO::Options::kEgress; serviceContext->setTransportLayer( std::make_unique(opts, nullptr) ); auto tlPtr = serviceContext->getTransportLayer(); uassertStatusOK(tlPtr->setup()); uassertStatusOK(tlPtr->start()); // Initialize Qt application QApplication app(argc, argv); // On Unix/Linux Qt is configured to use the system locale settings by default. // This can cause a conflict when using POSIX functions, for instance, when // converting between data types such as floats and strings, since the notation // may differ between locales. To get around this problem, call the POSIX // function setlocale(LC_NUMERIC, "C") right after initializing QApplication or // QCoreApplication to reset the locale that is used for number formatting to "C"-locale. // (http://doc.qt.io/qt-5/qcoreapplication.html#locale-settings) setlocale(LC_NUMERIC, "C"); #ifdef Q_OS_MAC app.setAttribute(Qt::AA_UseHighDpiPixmaps); #endif // EULA License Agreement auto const& settings { Robomongo::AppRegistry::instance().settingsManager() }; if (!settings->acceptedEulaVersions().contains(PROJECT_VERSION)) { bool const showFormPage { settings->programExitedNormally() && !settings->disableHttpsFeatures() }; Robomongo::EulaDialog eulaDialog(showFormPage); settings->setProgramExitedNormally(false); settings->save(); int const result = eulaDialog.exec(); settings->setProgramExitedNormally(true); settings->save(); if (QDialog::Rejected == result) { rbm_ssh_cleanup(); return 1; } // EULA accepted settings->addAcceptedEulaVersion(PROJECT_VERSION); settings->save(); } // Init GUI style Robomongo::AppStyleUtils::initStyle(); // To be set true at normal program exit settings->setProgramExitedNormally(false); settings->save(); // Application main window Robomongo::MainWindow mainWindow; mainWindow.show(); for(auto const& msgAndSeverity : Robomongo::RoboCrypt::roboCryptLogs()) Robomongo::LOG_MSG(msgAndSeverity.first, msgAndSeverity.second); int rc = app.exec(); rbm_ssh_cleanup(); return rc; } ================================================ FILE: src/robomongo/app/main_mongo.cpp ================================================ #include #include #include #include #include #include using namespace Robomongo; int main(int argc, char *argv[]) { QApplication app(argc, argv); mongo::BSONObjBuilder m; m.append("test", 56); const mongo::BSONObj &obj = m.obj(); std::string str = BsonUtils::jsonString(obj, mongo::TenGen, 1, DefaultEncoding, Utc); qDebug() << "Hello!"; qDebug() << QString::fromStdString(str); return app.exec(); } ================================================ FILE: src/robomongo/app/main_test.cpp ================================================ #undef NDEBUG #include #include #include #include #include #include namespace mongo { extern bool isShell; void logProcessDetailsForLogRotate() {} void exitCleanly(ExitCode code) {} } void testHostAndPort() { mongo::HostAndPort hp = mongo::HostAndPort("127.0.0.1", 20017); assert(hp.toString() == "127.0.0.1:20017"); hp = mongo::HostAndPort("2a03:b0c0:3:d0::f3:1001", 20017); assert(hp.toString() == "[2a03:b0c0:3:d0::f3:1001]:20017"); // toString() will wrap IPv6 address in brackets even if they // are already exist. This is true at least for MongoDB 3.2 hp = mongo::HostAndPort("[2a03:b0c0:3:d0::f3:1001]", 20017); assert(hp.toString() == "[[2a03:b0c0:3:d0::f3:1001]]:20017"); hp = mongo::HostAndPort("[2a03:b0c0:3:d0::f3:1001", 20017); assert(hp.toString() == "[[2a03:b0c0:3:d0::f3:1001]:20017"); } void precisionAssert(const std::string &text, double d) { std::stringstream s; s.precision(std::numeric_limits::digits10); s << d; if (d == (long long)d) s << ".0"; std::cout << "Checking " << text << " - "; assert(text == s.str()); std::cout << "Correct. " << std::endl; } void testPrecision() { precisionAssert("-9.987654321", -9.987654321); precisionAssert("-1.0", -1.0); precisionAssert("-0.0", -0.0); precisionAssert("0.0", 0.0); precisionAssert("9.0", 9.0); precisionAssert("9.8", 9.8); precisionAssert("9.9", 9.9); precisionAssert("9.98", 9.98); precisionAssert("9.987", 9.987); precisionAssert("9.9876", 9.9876); precisionAssert("9.98765", 9.98765); precisionAssert("9.987654", 9.987654); precisionAssert("9.9876543", 9.9876543); precisionAssert("9.98765432", 9.98765432); precisionAssert("9.987654321", 9.987654321); precisionAssert("9.987654321", 9.9876543210); precisionAssert("9.98765432109", 9.98765432109); precisionAssert("9.987654321098", 9.987654321098); precisionAssert("9.9876543210987", 9.9876543210987); precisionAssert("9.98765432109876", 9.98765432109876); precisionAssert("9.98765432109876", 9.987654321098765); precisionAssert("9.98765432109877", 9.9876543210987654); precisionAssert("3.1415", 3.1415); precisionAssert("1.1", 1.1); precisionAssert("9.7", 9.7); } int main(int argc, char *argv[], char** envp) { testHostAndPort(); testPrecision(); return 0; } ================================================ FILE: src/robomongo/core/AppRegistry.cpp ================================================ #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/domain/App.h" namespace Robomongo { AppRegistry::AppRegistry() : _bus(new EventBus()), _settingsManager(new SettingsManager()), _app(new App(_bus.get())) { } AppRegistry::~AppRegistry() { } } ================================================ FILE: src/robomongo/core/AppRegistry.h ================================================ #pragma once #include "robomongo/core/Core.h" #include "robomongo/core/utils/SingletonPattern.hpp" namespace Robomongo { class SettingsManager; class AppRegistry: public Patterns::LazySingleton { friend class Patterns::LazySingleton; public: SettingsManager *const settingsManager() const { return _settingsManager.get(); } App *const app() const { return _app.get(); } EventBus *const bus() const { return _bus.get(); } private: AppRegistry(); ~AppRegistry(); const EventBusScopedPtr _bus; const SettingsManagerScopedPtr _settingsManager; const AppScopedPtr _app; }; } ================================================ FILE: src/robomongo/core/Core.h ================================================ #pragma once #include #include namespace mongo { class DBClientConnection; } /* ** Smart pointers for Mongo* staff */ namespace Robomongo { class AppRegistry; typedef boost::scoped_ptr AppRegistryScopedPtr; class SettingsManager; typedef boost::scoped_ptr SettingsManagerScopedPtr; class App; typedef boost::scoped_ptr AppScopedPtr; class EventBus; typedef boost::scoped_ptr EventBusScopedPtr; class MongoCollection; typedef boost::shared_ptr MongoCollectionPtr; class MongoDocument; typedef boost::shared_ptr MongoDocumentPtr; // todo: Use enum class enum ConnectionType { // This type of connection is shown in Explorer and also opens SSH tunnel for secondary // connections (if needed) ConnectionPrimary = 0, // Never shown in Explorer and uses SSH tunnel from primary connection (if needed) ConnectionSecondary = 1, // The same as Primary, but is specifically for testing connections. ConnectionTest = 2, // Never shown in Explorer and can be used to refresh (via reconnecting) current connection view // (i.e. db version, storage engine, current replica set primary, status of replica set etc...) ConnectionRefresh = 3 }; } ================================================ FILE: src/robomongo/core/Enums.cpp ================================================ #include "robomongo/core/Enums.h" #include namespace { const char *viewModeAsoc[Robomongo::Custom+1] = {"Text mode", "Tree mode", "Table mode", "Custom mode"}; const char *timesAsoc[Robomongo::LocalTime+1] = {"UTC", "Local Timezone"}; const char *uuidAsoc[Robomongo::PythonLegacy+1] = {"Default encoding", "Java encoding", "CSharp encoding", "Python encoding"}; template inline type findTypeInArray(const char *(&arr)[size], const char *text) { for (int i = 0; i < size; ++i ) { if (strcmp(text, arr[i]) == 0) { return static_cast(i); } } return static_cast(0); } } namespace Robomongo { const char *convertUUIDEncodingToString(UUIDEncoding uuidCode) { return uuidAsoc[uuidCode]; } UUIDEncoding convertStringToUUIDEncoding(const char *text) { return findTypeInArray(uuidAsoc, text); } const char *convertTimesToString(SupportedTimes time) { return timesAsoc[time]; } SupportedTimes convertStringToTimes(const char *text) { return findTypeInArray(timesAsoc, text); } const char *convertViewModeToString(ViewMode mode) { return viewModeAsoc[mode]; } ViewMode convertStringToViewMode(const char *text) { return findTypeInArray(viewModeAsoc, text); } } ================================================ FILE: src/robomongo/core/Enums.h ================================================ #pragma once namespace Robomongo { enum UUIDEncoding { DefaultEncoding = 0, JavaLegacy = 1, CSharpLegacy = 2, PythonLegacy = 3 }; enum SupportedTimes { Utc = 0, LocalTime = 1 }; enum ViewMode { Text = 0, Tree = 1, Table = 2, Custom = 3 }; enum AutocompletionMode { AutocompleteNone = 0, AutocompleteAll = 1, AutocompleteNoCollectionNames = 2 }; const char *convertUUIDEncodingToString(UUIDEncoding uuidCode); UUIDEncoding convertStringToUUIDEncoding(const char *text); const char *convertTimesToString(SupportedTimes time); SupportedTimes convertStringToTimes(const char *text); const char *convertViewModeToString(ViewMode mode); ViewMode convertStringToViewMode(const char *text); } ================================================ FILE: src/robomongo/core/Event.cpp ================================================ #include "robomongo/core/Event.h" ================================================ FILE: src/robomongo/core/Event.h ================================================ #pragma once #include #include #include #include #include "robomongo/core/EventError.h" namespace Robomongo { /** * @brief Abstract base class for all events in Robomongo. */ class Event { public: /** * @brief Creates "non-error" event. */ Event(QObject *sender) : _sender(sender) {} /** * @brief Creates "error-event" that highlights that state of this * event is invalid because of error. */ Event(QObject *sender, const EventError &error) : _sender(sender), _error(error) { } virtual ~Event() { } /** * @brief Type identifier of event. We are using QEvent::Type in order to * support usage of events as QEvent with the help of EventWrapper. */ virtual QEvent::Type type() = 0; /** * @brief typeString * @return */ virtual const char *typeString() = 0; /** * @brief Sender that emits this event. */ QObject *sender() const { return _sender; } /** * @brief Tests whether this event is "error-event". */ bool isError() const { return !_error.isNull(); } /** * @brief Returns Error object. */ const EventError &error() const { return _error; } private: /** * @brief Sender that emits this event. */ QObject *_sender; /** * @brief Possible error. */ const EventError _error; }; } /** * @brief R_EVENT macro declares several event's members that are used by * Robomongo event subsystem. It should be used like Q_OBJECT macro: * * class SampleEvent : public Event * { * R_EVENT * * SampleEvent(QObject *sender, const QString &text) : * Event(sender), * server(server) { } * * QString text; * }; * * Warning: for convinience, this macro makes all subsequent declarations to * be public by default. */ #define R_EVENT \ public: \ const static QEvent::Type Type; \ const static int metatype; \ virtual const char *typeString(); \ virtual QEvent::Type type(); /** * @brief R_REGISTER_EVENT macro defines several event's members that are used * by Robomongo simple event subsystem. This macro should be placed in *.cpp file. * @param EVENT_CLASS: class name of Event. * * For SampleEvent shown above, registration will look like this: * R_REGISTER_EVENT(SampleEvent) */ #define R_REGISTER_EVENT(EVENT_CLASS) \ const QEvent::Type EVENT_CLASS::Type = static_cast(QEvent::registerEventType()); \ const char *EVENT_CLASS::typeString() { return #EVENT_CLASS"*"; } \ QEvent::Type EVENT_CLASS::type() { return EVENT_CLASS::Type; } \ const int EVENT_CLASS::metatype = qRegisterMetaType(#EVENT_CLASS"*"); ================================================ FILE: src/robomongo/core/EventBus.cpp ================================================ #include "robomongo/core/EventBus.h" #include #include #include #include #include "robomongo/core/EventBusDispatcher.h" #include "robomongo/core/EventBusSubscriber.h" #include "robomongo/core/Event.h" #include "robomongo/core/EventWrapper.h" #include "robomongo/core/utils/QtUtils.h" namespace { struct RemoveIfReciver { RemoveIfReciver(QObject *receiver) : _receiver(receiver) {} bool operator()(const Robomongo::EventBus::EventTypeAndSubscriber& item) const { if (item.second->receiver == _receiver) { delete item.second; return true; } return false; } QObject *_receiver; }; struct FindIfReciver { FindIfReciver(QThread *thread) : _thread(thread) {} bool operator()(const Robomongo::EventBus::ThreadAndDispatcher &item) const { if (item.first == _thread) return true; return false; } QThread *_thread; }; } namespace Robomongo { EventBus::EventBus() : QObject(), _lock(QMutex::Recursive) { } EventBus::~EventBus() { for (auto const& dispatchersByThread : _dispatchersByThread) delete dispatchersByThread.second; } void EventBus::publish(Event *event) { QMutexLocker lock(&_lock); QList theReceivers; EventBusDispatcher *dis = nullptr; for (auto const& item : _subscribersByEventType) { if (event->type() == item.first) { EventBusSubscriber *subscriber = item.second; if (!subscriber->sender || subscriber->sender == event->sender()) { theReceivers.append(subscriber->receiver); if (dis && dis != subscriber->dispatcher) throw "You cannot publish events to subscribers from more than one thread."; dis = subscriber->dispatcher; } } } if (dis) sendEvent(dis, new EventWrapper(event, theReceivers)); } void EventBus::send(QObject *receiver, Event *event) { QMutexLocker lock(&_lock); if (!receiver) return; QThread *thread = receiver->thread(); EventBusDispatcher *dis = dispatcher(thread); sendEvent(dis, new EventWrapper(event, receiver)); } void EventBus::send(QList receivers, Event *event) { QMutexLocker lock(&_lock); if (receivers.count() == 0) return; QThread *thread = receivers.last()->thread(); EventBusDispatcher *dis = dispatcher(thread); sendEvent(dis, new EventWrapper(event, receivers)); } void EventBus::subscribe(QObject *receiver, QEvent::Type type, QObject *sender /* = NULL */) { QMutexLocker lock(&_lock); QThread *currentThread = QThread::currentThread(); EventBusDispatcher *dis = dispatcher(currentThread); // subscribe to destroyed signal in order to remove // listener (receiver) from list of subscribers VERIFY(connect(receiver, SIGNAL(destroyed(QObject*)), this, SLOT(unsubscibe(QObject*)))); // add subscriber _subscribersByEventType.push_back(EventTypeAndSubscriber(type, new EventBusSubscriber(dis, receiver, sender))); } void EventBus::unsubscibe(QObject *receiver) { QMutexLocker lock(&_lock); _subscribersByEventType.erase(std::remove_if(_subscribersByEventType.begin(), _subscribersByEventType.end(), RemoveIfReciver(receiver)), _subscribersByEventType.end()); } /** * @brief Returns dispatcher for specified thread. If there is no dispatcher * for this thread registered, it will be created and moved to 'thread' thread. */ EventBusDispatcher *EventBus::dispatcher(QThread *thread) { auto const& disIt = std::find_if( _dispatchersByThread.begin(), _dispatchersByThread.end(), FindIfReciver(thread) ); if (disIt != _dispatchersByThread.end()) { return (*disIt).second; } else { EventBusDispatcher *dis = new EventBusDispatcher(); dis->moveToThread(thread); _dispatchersByThread.push_back(ThreadAndDispatcher(thread, dis)); return dis; } } /** * @brief Sends event synchronousely, if current thread and dispatcher thread are * the same. Sends asynchronousely, if this is cross-thread communication; */ void EventBus::sendEvent(EventBusDispatcher *dispatcher, EventWrapper *wrapper) { if (dispatcher->thread() == QThread::currentThread()) { QCoreApplication::sendEvent(dispatcher, wrapper); delete wrapper; } else { QCoreApplication::postEvent(dispatcher, wrapper); } } } ================================================ FILE: src/robomongo/core/EventBus.h ================================================ #pragma once #include #include #include #include namespace Robomongo { class Event; class EventWrapper; class EventBusDispatcher; struct EventBusSubscriber; /** * @brief The EventBus class * @threadsafe */ class EventBus : public QObject { Q_OBJECT public: typedef std::pair EventTypeAndSubscriber; typedef std::pair ThreadAndDispatcher; EventBus(); ~EventBus(); /** * @brief Publishes specified event. * @param sender - object, that emits this event */ void publish(Event *event); /** * @brief Sends 'event' to 'receiver'. */ void send(QObject *receiver, Event *event); /** * @brief Sends 'event' to list of 'receivers'. */ void send(QList receivers, Event *event); /** * @brief Subscribes 'receiver' to event of specified 'type'. * Optionally you can specify exact send */ void subscribe(QObject *receiver, QEvent::Type type, QObject *sender = NULL); public Q_SLOTS: void unsubscibe(QObject *receiver); private: /** * @brief Returns dispatcher for specified thread. If there is no dispatcher * for this thread registered, new dispatcher will be created and moved * to 'thread' thread. */ EventBusDispatcher *dispatcher(QThread *thread); void sendEvent(EventBusDispatcher *dispatcher, EventWrapper *wrapper); private: QMutex _lock; std::vector _subscribersByEventType; std::vector _dispatchersByThread; }; } ================================================ FILE: src/robomongo/core/EventBusDispatcher.cpp ================================================ #include "robomongo/core/EventBusDispatcher.h" #include "robomongo/core/EventWrapper.h" namespace Robomongo { EventBusDispatcher::EventBusDispatcher(QObject *parent) : QObject(parent) { } bool EventBusDispatcher::event(QEvent *qevent) { EventWrapper *wrapper = dynamic_cast(qevent); if (!wrapper) return false; Event *event = wrapper->event(); const char *typeName = event->typeString(); const QList &recivers = wrapper->receivers(); for (QList::const_iterator it = recivers.begin(); it != recivers.end(); ++it) { QMetaObject::invokeMethod(*it, "handle", QGenericArgument(typeName, &event)); } return true; } } ================================================ FILE: src/robomongo/core/EventBusDispatcher.h ================================================ #pragma once #include namespace Robomongo { /** * @brief The EventBusDispatcher class */ class EventBusDispatcher : public QObject { Q_OBJECT public: EventBusDispatcher(QObject *parent = 0); protected: virtual bool event(QEvent *qevent); }; } ================================================ FILE: src/robomongo/core/EventBusSubscriber.cpp ================================================ #include "robomongo/core/EventBusSubscriber.h" #include "robomongo/core/EventBusDispatcher.h" namespace Robomongo { EventBusSubscriber::EventBusSubscriber(EventBusDispatcher *dispatcher, QObject *receiver, QObject *sender) : receiver(receiver), dispatcher(dispatcher), sender(sender) {} } ================================================ FILE: src/robomongo/core/EventBusSubscriber.h ================================================ #pragma once #include namespace Robomongo { class EventBusDispatcher; struct EventBusSubscriber { EventBusSubscriber(EventBusDispatcher *dispatcher, QObject *receiver, QObject *sender = 0); EventBusDispatcher *const dispatcher; QObject *const receiver; QObject *const sender; }; } ================================================ FILE: src/robomongo/core/EventError.cpp ================================================ #include "robomongo/core/EventError.h" #include "robomongo/utils/StringOperations.h" namespace Robomongo { EventError::EventError() : _isNull(true) {} EventError::EventError(const std::string &errorMessage, ErrorCode errorCode /*= Unknown*/, bool showErrorWindow /* = true */) : _errorMessage(captilizeFirstChar(errorMessage)), _errorCode(errorCode), _showErrorWindow(showErrorWindow), _isNull(false) {} EventError::EventError(const std::string &errorMessage, ReplicaSet const& replicaSetInfo, bool showErrorWindow /* = true */ ) : _errorMessage(errorMessage), _errorCode(SetPrimaryUnreachable), _replicaSetInfo(replicaSetInfo), _showErrorWindow(showErrorWindow), _isNull(false) {} bool EventError::isNull() const { return _isNull; } const std::string &EventError::errorMessage() const { return _errorMessage; } } ================================================ FILE: src/robomongo/core/EventError.h ================================================ #pragma once #include #include "robomongo/core/mongodb/ReplicaSet.h" namespace Robomongo { /** * @brief Error object is a part of every Event in Robomongo. Before the * use of any event you should check whether error is detected by * calling Event::isError() method. */ class EventError { public: enum ErrorCode { Unknown, SetPrimaryUnreachable, ServerHasDifferentMembers }; /** * @brief Creates "null" (or, in other words, empty) error. * Subsequent call of isNull() on this object will return true. */ EventError(); /** * @brief Creates error object with specified error message. * Subsequent call of isNull() on this object will return false. */ explicit EventError(const std::string &errorMessage, ErrorCode errorCode = Unknown, bool showErrorWindow = true); /** * @brief Creates error object only for replica set connection failures */ explicit EventError(const std::string &errorMessage, ReplicaSet const& replicaSetInfo, bool showErrorWindow = true); /** * @brief Tests whether error is "null" (or, in other words, empty). * Because Error object should be created on stack, this is the only * way to support "null" semantic for value objects. * @return true, if null or false otherwise. */ bool isNull() const; /** * @brief Returns error message that describes this error. */ const std::string &errorMessage() const; ErrorCode errorCode() const { return _errorCode; } ReplicaSet replicaSetInfo() const { return _replicaSetInfo; } bool showErrorWindow() const { return _showErrorWindow; } private: /** * @brief Error message */ std::string const _errorMessage; ErrorCode const _errorCode = Unknown; ReplicaSet const _replicaSetInfo; bool const _showErrorWindow = true; bool const _isNull; }; } ================================================ FILE: src/robomongo/core/EventWrapper.cpp ================================================ #include "robomongo/core/EventWrapper.h" namespace Robomongo { EventWrapper::EventWrapper(Event *event, QList receivers) : QEvent(event->type()), _event(event), _receivers(receivers) {} EventWrapper::EventWrapper(Event *event, QObject * receiver) : QEvent(event->type()), _event(event), _receivers(QList() << receiver ) {} Event *EventWrapper::event() const { return _event.get(); } const QList &EventWrapper::receivers() const { return _receivers; } } ================================================ FILE: src/robomongo/core/EventWrapper.h ================================================ #pragma once #include #include "robomongo/core/Event.h" namespace Robomongo { class EventWrapper : public QEvent { public: EventWrapper(Event *event, QList receivers); EventWrapper(Event *event, QObject * receiver); Event *event() const; const QList &receivers() const; private: const boost::scoped_ptr _event; const QList _receivers; }; } ================================================ FILE: src/robomongo/core/HexUtils.cpp ================================================ #include "robomongo/core/HexUtils.h" #include #include #include namespace Robomongo { namespace HexUtils { bool isHexString(const std::string &str) { std::size_t i; for (i = 0; i < str.size(); i++) { if (!isxdigit(str[i])) { return false; } } return true; } std::string toStdHexLower(const char *raw, int len) { const void* in = reinterpret_cast(raw); std::string stdstr = mongo::toHexLower(in, len); return stdstr; } const char *fromHex(const std::string &s, int *outBytes) { const int size = s.size(); if (size % 2 != 0) return NULL; const int bytes = size / 2; // number of bytes char *data = new char[bytes]; const char *p = s.c_str(); for (size_t i = 0; i < bytes; i++) { data[i] = mongo::fromHex(p).getValue(); p += 2; } *outBytes = bytes; return data; } std::string hexToUuid(const std::string &hex, UUIDEncoding encoding) { switch(encoding) { case DefaultEncoding: return hexToUuid(hex); case JavaLegacy: return hexToJavaUuid(hex); case CSharpLegacy: return hexToCSharpUuid(hex); case PythonLegacy: return hexToPythonUuid(hex); default: return hexToUuid(hex); } } std::string hexToUuid(const std::string &hex) { std::string uuid = hex.substr(0, 8) + '-' + hex.substr(8, 4) + '-' + hex.substr(12, 4) + '-' + hex.substr(16, 4) + '-' + hex.substr(20, 12); return uuid; } std::string hexToCSharpUuid(const std::string &hex) { std::string a = hex.substr(6, 2) + hex.substr(4, 2) + hex.substr(2, 2) + hex.substr(0, 2); std::string b = hex.substr(10, 2) + hex.substr(8, 2); std::string c = hex.substr(14, 2) + hex.substr(12, 2); std::string d = hex.substr(16, 16); std::string temp = a + b + c + d; std::string uuid = temp.substr(0, 8) + '-' + temp.substr(8, 4) + '-' + temp.substr(12, 4) + '-' + temp.substr(16, 4) + '-' + temp.substr(20, 12); return uuid; } std::string hexToJavaUuid(const std::string &hex) { std::string msb = hex.substr(0, 16); std::string lsb = hex.substr(16, 16); msb = msb.substr(14, 2) + msb.substr(12, 2) + msb.substr(10, 2) + msb.substr(8, 2) + msb.substr(6, 2) + msb.substr(4, 2) + msb.substr(2, 2) + msb.substr(0, 2); lsb = lsb.substr(14, 2) + lsb.substr(12, 2) + lsb.substr(10, 2) + lsb.substr(8, 2) + lsb.substr(6, 2) + lsb.substr(4, 2) + lsb.substr(2, 2) + lsb.substr(0, 2); std::string temp = msb + lsb; std::string uuid = temp.substr(0, 8) + '-' + temp.substr(8, 4) + '-' + temp.substr(12, 4) + '-' + temp.substr(16, 4) + '-' + temp.substr(20, 12); return uuid; } std::string hexToPythonUuid(const std::string &hex) { return hexToUuid(hex); } std::string uuidToHex(const std::string &uuid, Robomongo::UUIDEncoding encoding) { switch(encoding) { case DefaultEncoding: return uuidToHex(uuid); case JavaLegacy: return javaUuidToHex(uuid); case CSharpLegacy: return csharpUuidToHex(uuid); case PythonLegacy: return pythonUuidToHex(uuid); default: return uuidToHex(uuid); } } std::string uuidToHex(const std::string &uuid) { // remove extra characters std::string hex = uuid; pcrecpp::RE re("[{}-]"); re.GlobalReplace("", &hex); if (hex.size() != 32) return ""; return hex; } std::string csharpUuidToHex(const std::string &uuid) { // remove extra characters std::string hex = uuid; pcrecpp::RE re("[{}-]"); re.GlobalReplace("", &hex); if (hex.size() != 32) return ""; std::string a = hex.substr(6, 2) + hex.substr(4, 2) + hex.substr(2, 2) + hex.substr(0, 2); std::string b = hex.substr(10, 2) + hex.substr(8, 2); std::string c = hex.substr(14, 2) + hex.substr(12, 2); std::string d = hex.substr(16, 16); std::string result = a + b + c + d; return result; } std::string javaUuidToHex(const std::string &uuid) { // remove extra characters std::string hex = uuid; pcrecpp::RE re("[{}-]"); re.GlobalReplace("", &hex); if (hex.size() != 32) return ""; std::string msb = hex.substr(0, 16); std::string lsb = hex.substr(16, 16); msb = msb.substr(14, 2) + msb.substr(12, 2) + msb.substr(10, 2) + msb.substr(8, 2) + msb.substr(6, 2) + msb.substr(4, 2) + msb.substr(2, 2) + msb.substr(0, 2); lsb = lsb.substr(14, 2) + lsb.substr(12, 2) + lsb.substr(10, 2) + lsb.substr(8, 2) + lsb.substr(6, 2) + lsb.substr(4, 2) + lsb.substr(2, 2) + lsb.substr(0, 2); std::string result = msb + lsb; return result; } std::string pythonUuidToHex(const std::string &uuid) { return uuidToHex(uuid); } std::string formatUuid(const mongo::BSONElement &element, Robomongo::UUIDEncoding encoding) { mongo::BinDataType binType = element.binDataType(); if (binType != mongo::newUUID && binType != mongo::bdtUUID) throw std::invalid_argument("Binary subtype should be 3 (bdtUUID) or 4 (newUUID)"); int len; const char *data = element.binData(len); std::string hex = HexUtils::toStdHexLower(data, len); if (binType == mongo::bdtUUID) { std::string uuid = HexUtils::hexToUuid(hex, encoding); switch(encoding) { case DefaultEncoding: return "LUUID(\"" + uuid + "\")"; case JavaLegacy: return "JUUID(\"" + uuid + "\")"; case CSharpLegacy: return "NUUID(\"" + uuid + "\")"; case PythonLegacy: return "PYUUID(\"" + uuid + "\")"; default: return "LUUID(\"" + uuid + "\")"; } } else { std::string uuid = HexUtils::hexToUuid(hex, DefaultEncoding); return "UUID(\"" + uuid + "\")"; } } } } ================================================ FILE: src/robomongo/core/HexUtils.h ================================================ #pragma once #include "robomongo/core/Enums.h" #include namespace Robomongo { /** * @brief HexUtils * * Usage: * * std::string csuuid = HexUtils::hexToCSharpUuid(hex.toStdString()); * std::string cshex = HexUtils::csharpUuidToHex(csuuid); * * std::string juuid = HexUtils::hexToJavaUuid(hex.toStdString()); * std::string jhex = HexUtils::javaUuidToHex(juuid); * * std::string puuid = HexUtils::hexToPythonUuid(hex.toStdString()); * std::string phex = HexUtils::pythonUuidToHex(puuid);* * */ namespace HexUtils { bool isHexString(const std::string &hex); std::string toStdHexLower(const char *raw, int len); /** * @param str: data in hex format. * @param outBytes: out param - number of bytes in array. * @return array of bytes, with "outBytes" length. */ const char *fromHex(const std::string &str, int *outBytes); std::string hexToUuid(const std::string &hex, UUIDEncoding encoding); std::string hexToUuid(const std::string &hex); std::string hexToCSharpUuid(const std::string &hex); std::string hexToJavaUuid(const std::string &hex); std::string hexToPythonUuid(const std::string &hex); /** * @return empty string, if invalid UUID. */ std::string uuidToHex(const std::string &uuid, UUIDEncoding encoding); std::string uuidToHex(const std::string &uuid); std::string csharpUuidToHex(const std::string &uuid); std::string javaUuidToHex(const std::string &uuid); std::string pythonUuidToHex(const std::string &uuid); std::string formatUuid(const mongo::BSONElement &element, UUIDEncoding encoding); } } ================================================ FILE: src/robomongo/core/HexUtils_test.cpp ================================================ #include "gtest/gtest.h" #include "HexUtils.h" #include /* Example Test: * * TEST( [Test_Case_Name], [Test_Name] ) * TEST( [Test_Case_Name], [UnitOfWorkName_ScenarioUnderTest_ExpectedBehavior] ) * TEST( StringParserTests, NumberLeftOf_StringWithoutNumber_ReturnsFalse) { // ... } */ TEST(hex_utils_tests, test_1) { EXPECT_TRUE(Robomongo::HexUtils::isHexString("a")); } ================================================ FILE: src/robomongo/core/KeyboardManager.cpp ================================================ #include "robomongo/core/KeyboardManager.h" namespace Robomongo { bool KeyboardManager::isNewTabShortcut(QKeyEvent *keyEvent) { bool ctrlShiftReturn = (keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::ShiftModifier) && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter); // TODO: compare with QKeySequence::AddTab rather than Ctrl+T bool ctrlT = (keyEvent->modifiers() & Qt::ControlModifier) && !(keyEvent->modifiers() & Qt::ShiftModifier) && (keyEvent->key() == Qt::Key_T); return ctrlShiftReturn || ctrlT; } bool KeyboardManager::isDuplicateTabShortcut(QKeyEvent *keyEvent) { bool ctrlShiftT = (keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::ShiftModifier) && (keyEvent->key() == Qt::Key_T); return ctrlShiftT; } bool KeyboardManager::isSetFocusOnQueryLineShortcut(QKeyEvent *keyEvent) { return keyEvent->key() == Qt::Key_F6; } bool KeyboardManager::isExecuteScriptShortcut(QKeyEvent *keyEvent) { return (keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter); } bool KeyboardManager::isAutoCompleteShortcut(QKeyEvent *keyEvent) { return (keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->key() == Qt::Key_Space); } bool KeyboardManager::isHideAutoCompleteShortcut(QKeyEvent *keyEvent) { return (keyEvent->key() == Qt::Key_Escape); } bool KeyboardManager::isNextTabShortcut(QKeyEvent *keyEvent) { return (keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::AltModifier) && (keyEvent->key() == Qt::Key_Right); } bool KeyboardManager::isPreviousTabShortcut(QKeyEvent *keyEvent) { return (keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::AltModifier) && (keyEvent->key() == Qt::Key_Left); } bool KeyboardManager::isToggleCommentsShortcut(QKeyEvent *keyEvent) { return ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->key() == Qt::Key_Slash)) || ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::ShiftModifier) && (keyEvent->key() == Qt::Key_C)); } } ================================================ FILE: src/robomongo/core/KeyboardManager.h ================================================ #pragma once #include namespace Robomongo { namespace KeyboardManager { bool isNewTabShortcut(QKeyEvent *keyEvent); bool isDuplicateTabShortcut(QKeyEvent *keyEvent); bool isSetFocusOnQueryLineShortcut(QKeyEvent *keyEvent); bool isExecuteScriptShortcut(QKeyEvent *keyEvent); bool isAutoCompleteShortcut(QKeyEvent *keyEvent); bool isHideAutoCompleteShortcut(QKeyEvent *keyEvent); bool isNextTabShortcut(QKeyEvent *keyEvent); bool isPreviousTabShortcut(QKeyEvent *keyEvent); bool isToggleCommentsShortcut(QKeyEvent *keyEvent); } } ================================================ FILE: src/robomongo/core/domain/App.cpp ================================================ #include "robomongo/core/domain/App.h" #include #include #include #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/domain/MongoShell.h" #include "robomongo/core/domain/MongoCollection.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/core/settings/SshSettings.h" #include "robomongo/core/settings/SslSettings.h" #include "robomongo/core/mongodb/SshTunnelWorker.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/utils/StdUtils.h" #include "robomongo/core/utils/Logger.h" namespace Robomongo { namespace detail { QString buildCollectionQuery(const std::string &collectionName, const QString &postfix) { QString qCollectionName = QtUtils::toQString(collectionName); QString pattern; // Use db.getCollection() to avoid having to enumerate and special case "reserved" names pattern = "db.getCollection(\'%1\').%2"; // Escape '\' symbol qCollectionName.replace(QChar('\\'), "\\\\"); return pattern.arg(qCollectionName).arg(postfix); } } App::~App() {} App::App(EventBus *const bus) : QObject(), _bus(bus), _lastServerHandle(0) { _bus->subscribe(this, EstablishSshConnectionResponse::Type); _bus->subscribe(this, ListenSshConnectionResponse::Type); _bus->subscribe(this, LogEvent::Type); } std::unique_ptr App::continueOpenServer(int serverHandle, ConnectionSettings* connSettings, ConnectionType type, int localport) { ConnectionSettings* connSettingsClone = connSettings->clone(); // Modify connection settings when SSH tunnel is used if ((type == ConnectionPrimary || type == ConnectionTest) && !connSettingsClone->isReplicaSet() && connSettingsClone->sshSettings()->enabled() ) { connSettingsClone->setServerHost("127.0.0.1"); connSettingsClone->setServerPort(localport); } auto server { std::make_unique(serverHandle, connSettingsClone, type) }; server->runWorkerThread(); auto replicaSetStr = QString::fromStdString(connSettings->connectionName()) + " [Replica Set]"; replicaSetStr = (connSettings->replicaSetSettings()->members().size() > 0) ? replicaSetStr + QString::fromStdString(connSettings->replicaSetSettings()->members()[0]) : replicaSetStr + ""; QString serverAddress = connSettings->isReplicaSet() ? replicaSetStr : QString::fromStdString(connSettings->getFullAddress()); LOG_MSG(QString("Connecting to %1...").arg(serverAddress), mongo::logger::LogSeverity::Info()); server->tryConnect(); return server; } /** * Creates and opens new server connection. * @param connection: ConnectionSettings, that will be owned by MongoServer. * @param visible: should this server be visible in UI (explorer) or not. */ std::unique_ptr App::openServerInternal(ConnectionSettings* connSettings, ConnectionType type) { ++_lastServerHandle; if (type == ConnectionPrimary) _bus->publish(new ConnectingEvent(this)); // When connection is SECONDARY or SSH not enabled or replica set, // then continue without SSH Tunnel if (type == ConnectionSecondary || !connSettings->sshSettings()->enabled() || connSettings->isReplicaSet() ) { return continueOpenServer(_lastServerHandle, connSettings, type); } // Open SSH channel and only after that open connection LOG_MSG(QString("Creating SSH tunnel to %1:%2...") .arg(QtUtils::toQString(connSettings->sshSettings()->host())) .arg(connSettings->sshSettings()->port()), mongo::logger::LogSeverity::Info()); ConnectionSettings* settingsCopy = connSettings->clone(); SshTunnelWorker* sshWorker = new SshTunnelWorker(settingsCopy); _bus->send(sshWorker, new EstablishSshConnectionRequest(this, _lastServerHandle, sshWorker, settingsCopy, type)); return nullptr; } bool App::openServer(ConnectionSettings *connection, ConnectionType type) { SshSettings *ssh = connection->sshSettings(); if (!connection->isReplicaSet() && ssh->enabled() && ssh->askPassword() && (type == ConnectionPrimary || type == ConnectionTest)) { bool ok = false; bool isByKey = ssh->authMethod() == "publickey"; std::string passText = isByKey ? "passphrase" : "password"; std::stringstream s; s << "In order to continue, please provide the " << passText; if (isByKey) s << " for the key file"; s << "." << std::endl << std::endl; if (ssh->authMethod() == "publickey") s << "Private Key: " << ssh->privateKeyFile() << std::endl; s << "Server: " << ssh->host() << std::endl; s << "User: " << ssh->userName() << std::endl; s << std::endl << "Enter your " << passText << " that will never be stored:"; QString userInput = QInputDialog::getText(NULL, tr("SSH Authentication"), QtUtils::toQString(s.str()), QLineEdit::Password, "", &ok); if (!ok) return false; ssh->setAskedPassword(QtUtils::toStdString(userInput)); } SslSettings *sslSettings = connection->sslSettings(); if (sslSettings->sslEnabled() && sslSettings->usePemFile() && sslSettings->askPassphrase() && (type == ConnectionPrimary || type == ConnectionTest)) { if (!askSslPassphrasePromptDialog(connection)) { return false; } } _servers.push_back(move(openServerInternal(connection, type))); return true; } /** * @brief Closes MongoServer connection and frees all resources, owned * by MongoServer. Finally, specified MongoServer will also be deleted. */ void App::closeServer(MongoServer *server) { _servers.erase(std::remove_if(_servers.begin(), _servers.end(), [&](auto const& el) { return el.get() == server; }), _servers.end()); } void App::openShell(MongoCollection *collection, const QString &filePathToSave) { ConnectionSettings *connection = collection->database()->server()->connectionRecord(); auto const& dbname = collection->database()->name(); connection->setDefaultDatabase(dbname); QString const& script = detail::buildCollectionQuery(collection->name(), "find({})"); openShell(collection->database()->server(), connection, ScriptInfo(script, true, dbname, CursorPosition(0, -2), QtUtils::toQString(dbname), filePathToSave) ); } void App::openShell(MongoServer *server, const QString &script, const std::string &dbName, bool execute, const QString &shellName, const CursorPosition &cursorPosition, const QString &filePathToSave) { ConnectionSettings *connection = server->connectionRecord(); if (!dbName.empty()) connection->setDefaultDatabase(dbName); openShell(server, connection, ScriptInfo(script, execute, dbName, cursorPosition, shellName, filePathToSave) ); } void App::openShell(MongoDatabase *database, const QString &script, bool execute, const QString &shellName, const CursorPosition &cursorPosition, const QString &filePathToSave) { ConnectionSettings *connection = database->server()->connectionRecord(); connection->setDefaultDatabase(database->name()); openShell(database->server(), connection, ScriptInfo(script, execute, database->name(), cursorPosition, shellName, filePathToSave)); } void App::openShell(MongoServer* server, ConnectionSettings* connection, const ScriptInfo &scriptInfo) { auto serverClone{ openServerInternal(connection, ConnectionSecondary) }; if (!serverClone || !server) return; auto shell{ std::make_unique(serverClone.get(), scriptInfo) }; _servers.push_back(move(serverClone)); // Connection between explorer's server and tab's MongoShells _bus->subscribe(server, ReplicaSetRefreshed::Type, shell.get()); _bus->publish(new OpeningShellEvent(this, shell.get())); shell->execute(); _shells.push_back(move(shell)); return; } /** * @brief Closes MongoShell and frees all resources, owned by specified MongoShell. * Finally, specified MongoShell will also be deleted. */ void App::closeShell(MongoShell *shell) { auto const itr = std::find_if(_shells.begin(), _shells.end(), [&](auto const& el) { return el.get() == shell; } ); // Do nothing, if this shell not owned by this App. if (itr == _shells.end()) return; closeServer(shell->server()); _shells.erase(itr); } void App::handle(EstablishSshConnectionResponse *event) { if (event->isError()) { _bus->publish(new ConnectionFailedEvent( this, event->serverHandle, event->connectionType, event->error().errorMessage(), ConnectionFailedEvent::SshConnection)); return; } LOG_MSG(QString("SSH tunnel created successfully"), mongo::logger::LogSeverity::Info()); _servers.push_back(move( continueOpenServer(event->serverHandle, event->settings, event->connectionType, event->localport) )); _bus->send(event->worker, new ListenSshConnectionRequest(this, event->serverHandle, event->connectionType)); } void App::handle(LogEvent *event) { LOG_MSG(event->message, event->mongoLogSeverity()); if (!event->informUser) return; QMessageBox( event->qMessageBoxIcon(), QString::fromStdString(event->severity()), QtUtils::toQString(event->severity() + ": " + event->message) ).exec(); } void App::handle(ListenSshConnectionResponse *event) { if (event->isError()) { _bus->publish( new ConnectionFailedEvent(this, event->serverHandle, event->connectionType, event->error().errorMessage(), ConnectionFailedEvent::SshChannel) ); return; } LOG_MSG(QString("SSH tunnel closed."), mongo::logger::LogSeverity::Error()); } void App::fireConnectionFailedEvent(int serverHandle, ConnectionType type, std::string errormsg, ConnectionFailedEvent::Reason reason) { _bus->publish(new ConnectionFailedEvent(this, serverHandle, type, errormsg, reason)); } bool App::askSslPassphrasePromptDialog(ConnectionSettings *connSettings) const { auto sslSettings = connSettings->sslSettings(); bool ok = false; std::stringstream s; s << "In order to continue, please provide the passphrase"; s << "." << std::endl << std::endl; s << "Server: " << connSettings->serverHost() << ":" << connSettings->serverPort() << std::endl; s << "PEM file: " << sslSettings->pemKeyFile() << std::endl; s << std::endl << "Enter your PEM key passphrase (will never be stored):"; QString userInput = QInputDialog::getText(NULL, tr("TLS Authentication"), QtUtils::toQString(s.str()), QLineEdit::Password, "", &ok); if (!ok) { return false; } sslSettings->setPemPassPhrase(QtUtils::toStdString(userInput)); return ok; } } ================================================ FILE: src/robomongo/core/domain/App.h ================================================ #pragma once #include #include #include #include "robomongo/core/domain/ScriptInfo.h" namespace Robomongo { class EventBus; class MongoServer; class ConnectionSettings; class MongoCollection; class MongoShell; class MongoDatabase; class EstablishSshConnectionResponse; class LogEvent; namespace detail { /** * @brief Builds single collection query (i.e. db.my_col.find()) from * string that doesn't contain "db.my_col." prefix. * * If you'll call buildCollectionQuery("test", "find()") you'll receive: * db.test.find() * * If you'll call buildCollectionQuery("1234", "find()") you'll receive: * db['1234'].find() * * @param script: query part (without "db.my_col." prefix" */ QString buildCollectionQuery(const std::string &collectionName, const QString &postfix); } class App : public QObject { Q_OBJECT public: App(EventBus *const bus); ~App(); /** * @brief Creates and opens new server connection. * @param connection: ConnectionSettings, that will be owned by MongoServer. * @param visible: should this server be visible in UI (explorer) or not. * @return Succeeded or not */ bool openServer(ConnectionSettings *connection, ConnectionType type); /** * @brief Closes MongoServer connection and frees all resources, owned * by specified MongoServer. Finally, specified MongoServer will also be deleted. */ void closeServer(MongoServer *server); /** * @brief Open new shell based on specified collection */ void openShell(MongoCollection *collection, const QString &filePathToSave = QString()); void openShell(MongoServer *server, const QString &script, const std::string &dbName = std::string(), bool execute = true, const QString &shellName = QString(), const CursorPosition &cursorPosition = CursorPosition(), const QString &file = QString()); void openShell(MongoDatabase *database, const QString &script, bool execute = true, const QString &shellName = QString(), const CursorPosition &cursorPosition = CursorPosition(), const QString &filePathToSave = QString()); /** * @brief Open new shell using explorer's MongoServer (ExplorerServerTreeItem's _server) */ void openShell(MongoServer* server, ConnectionSettings* connSettings, const ScriptInfo &scriptInfo); auto const& getServers() const { return _servers; }; /** * @brief Closes MongoShell and frees all resources, owned by specified MongoShell. * Finally, specified MongoShell will also be deleted. */ void closeShell(MongoShell *shell); void fireConnectionFailedEvent(int serverHandle, ConnectionType type, std::string errormsg, ConnectionFailedEvent::Reason reason); int getLastServerHandle() const { return _lastServerHandle; }; public Q_SLOTS: void handle(EstablishSshConnectionResponse *event); void handle(ListenSshConnectionResponse *event); void handle(LogEvent *event); private: std::unique_ptr openServerInternal(ConnectionSettings* connSettings, ConnectionType type); std::unique_ptr continueOpenServer(int serverHandle, ConnectionSettings* connSettings, ConnectionType type, int localport = 0); /** * @brief Create prompt dialog to enter SSL PEM key passphrase and save passphrase into SSL settings * @param connection Pointer to active connection settings * @return true on success, false otherwise */ bool askSslPassphrasePromptDialog(ConnectionSettings *connSettings) const; /** * MongoServers, owned by this App. */ std::vector> _servers; /** * MongoShells, owned by this App. */ std::vector> _shells; EventBus *const _bus; // Increase monotonically when new MongoServer is created // Never decreases. int _lastServerHandle; }; } ================================================ FILE: src/robomongo/core/domain/CursorPosition.cpp ================================================ #include "robomongo/core/domain/CursorPosition.h" namespace Robomongo { CursorPosition::CursorPosition() : _isNull(true), _line(-1), _column(-1) {} CursorPosition::CursorPosition(int line, int column) : _isNull(false), _line(line), _column(column) {} } ================================================ FILE: src/robomongo/core/domain/CursorPosition.h ================================================ #pragma once namespace Robomongo { class CursorPosition { public: CursorPosition(); CursorPosition(int line, int column); bool isNull() const { return _isNull; } int line() const { return _line; } int column() const { return _column; } private: const bool _isNull; const int _line; const int _column; }; } ================================================ FILE: src/robomongo/core/domain/MongoAggregateInfo.h ================================================ #pragma once namespace Robomongo { struct AggrInfo { AggrInfo() {} AggrInfo(const std::string& collectionName, int skip, int batchSize, mongo::BSONObj const& pipeline, mongo::BSONObj const& options, int resultIndex) : collectionName(collectionName), skip(skip), batchSize(batchSize), pipeline(pipeline), options(options), isValid(true), resultIndex(resultIndex) {} std::string collectionName = ""; int skip = 0; int batchSize = 0; mongo::BSONObj pipeline; mongo::BSONObj options; bool isValid = false; int resultIndex = -1; }; } ================================================ FILE: src/robomongo/core/domain/MongoCollection.cpp ================================================ #include "robomongo/core/domain/MongoCollection.h" #include "robomongo/core/domain/MongoUtils.h" namespace Robomongo { MongoCollection::MongoCollection(MongoDatabase *database, const MongoCollectionInfo &info) : _ns(info.ns()), _database(database), _info(info), _system(false) { // System databases starts from system.* std::string collectionName = _ns.collectionName(); std::string prefix = "system."; // Checking whether `collectionName` starts from `system` if (collectionName.compare(0, prefix.length(), prefix) == 0) _system = true; } /* std::string MongoCollection::sizeString() const { return MongoUtils::buildNiceSizeString(_info.sizeBytes()).toStdString(); } QString MongoCollection::storageSizeString() const { return MongoUtils::buildNiceSizeString(_info.storageSizeBytes()); }*/ } ================================================ FILE: src/robomongo/core/domain/MongoCollection.h ================================================ #pragma once #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/domain/MongoCollectionInfo.h" namespace Robomongo { class MongoCollection { public: MongoCollection(MongoDatabase *database, const MongoCollectionInfo &info); bool isSystem() const { return _system; } std::string name() const { return _ns.collectionName(); } const MongoCollectionInfo info() const { return _info; } std::string fullName() const { return _ns.toString(); } MongoDatabase *database() const { return _database; } // std::string sizeString() const; // QString storageSizeString() const; private: MongoDatabase *_database; bool _system; MongoCollectionInfo _info; MongoNamespace _ns; }; } ================================================ FILE: src/robomongo/core/domain/MongoCollectionInfo.cpp ================================================ #include "MongoCollectionInfo.h" #include "robomongo/core/utils/BsonUtils.h" #include namespace Robomongo { MongoCollectionInfo::MongoCollectionInfo(const std::string &ns) : _ns(ns) {} /* MongoCollectionInfo::MongoCollectionInfo(mongo::BSONObj stats) : _ns(stats.getStringField("ns")) { // if "size" and "storageSize" are of type Int32 or Int64, they // will be converted to double by "numberDouble()" function. _sizeBytes = BsonUtils::getField(stats,"size"); _storageSizeBytes = BsonUtils::getField(stats,"storageSize"); // NumberLong because of mongodb can have very big collections _count = BsonUtils::getField(stats,"count"); }*/ } ================================================ FILE: src/robomongo/core/domain/MongoCollectionInfo.h ================================================ #pragma once #include #include "robomongo/core/domain/MongoNamespace.h" namespace Robomongo { class MongoCollectionInfo { public: MongoCollectionInfo() {} MongoCollectionInfo(const std::string &ns); // MongoCollectionInfo(mongo::BSONObj stats); std::string name() const { return _ns.collectionName(); } std::string fullName() const { return _ns.toString(); } MongoNamespace ns() const { return _ns; } /** * @brief Size in bytes * It is double, because db.stats()'s "size" field may be double * for large values, while Int32 for small. */ // double sizeBytes() const { return _sizeBytes; } /** * @brief Storage size in bytes * It is double, because db.stats()'s "storageSize" field may be double * for large values, while Int32 for small. */ // double storageSizeBytes() const { return _storageSizeBytes; } // long long count() const { return _count; } private: MongoNamespace _ns; /** * @brief Size in bytes * It is double, because db.stats()'s "size" field may be double * for large values, while Int32 for small. */ double _sizeBytes; /** * @brief Storage size in bytes * It is double, because db.stats()'s "storageSize" field may be double * for large values, while Int32 for small. */ double _storageSizeBytes; long long _count; }; } ================================================ FILE: src/robomongo/core/domain/MongoDatabase.cpp ================================================ #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/domain/MongoCollection.h" #include "robomongo/core/mongodb/MongoWorker.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/utils/common.h" namespace Robomongo { R_REGISTER_EVENT(MongoDatabaseCollectionListLoadedEvent) R_REGISTER_EVENT(MongoDatabaseUsersLoadedEvent) R_REGISTER_EVENT(MongoDatabaseFunctionsLoadedEvent) R_REGISTER_EVENT(MongoDatabaseUsersLoadingEvent) R_REGISTER_EVENT(MongoDatabaseFunctionsLoadingEvent) R_REGISTER_EVENT(MongoDatabaseCollectionsLoadingEvent) const std::string MongoDatabase::StorageEngineType::WIRED_TIGER = "wiredTiger"; const std::string MongoDatabase::StorageEngineType::MMAPV1 = "mmapv1"; const float MongoDatabase::DBVersion::MONGODB_2_6 = 2.6f; const float MongoDatabase::DBVersion::MONGODB_3_0 = 3.0f; const float MongoDatabase::DBVersion::MONGODB_3_2 = 3.2f; MongoDatabase::MongoDatabase(MongoServer *server, const std::string &name) : QObject(), _system(name == "admin" || name == "local"), _server(server), _bus(AppRegistry::instance().bus()), _name(name) {} MongoDatabase::~MongoDatabase() { clearCollections(); } void MongoDatabase::loadCollections() { _bus->publish(new MongoDatabaseCollectionsLoadingEvent(this)); _bus->send(_server->worker(), new LoadCollectionNamesRequest(this, _name)); } void MongoDatabase::loadUsers() { _bus->publish(new MongoDatabaseUsersLoadingEvent(this)); _bus->send(_server->worker(), new LoadUsersRequest(this, _name)); } void MongoDatabase::loadFunctions() { _bus->publish(new MongoDatabaseFunctionsLoadingEvent(this)); _bus->send(_server->worker(), new LoadFunctionsRequest(this, _name)); } void MongoDatabase::createCollection(const std::string &collection, long long size, bool capped, int maxDocNum, const mongo::BSONObj& extraOptions) { _bus->send(_server->worker(), new CreateCollectionRequest(this, MongoNamespace(_name, collection), extraOptions, size, capped, maxDocNum)); } void MongoDatabase::dropCollection(const std::string &collection) { _bus->send(_server->worker(), new DropCollectionRequest(this, MongoNamespace(_name, collection))); } void MongoDatabase::renameCollection(const std::string &collection, const std::string &newCollection) { _bus->send(_server->worker(), new RenameCollectionRequest(this, MongoNamespace(_name, collection), newCollection)); } void MongoDatabase::duplicateCollection(const std::string &collection, const std::string &newCollection) { _bus->send(_server->worker(), new DuplicateCollectionRequest(this, MongoNamespace(_name, collection), newCollection)); } void MongoDatabase::copyCollection(MongoServer *server, const std::string &sourceDatabase, const std::string &collection) { _bus->send(_server->worker(), new CopyCollectionToDiffServerRequest(this, server->worker(), sourceDatabase, collection, _name)); } void MongoDatabase::createUser(const MongoUser &user) { _bus->send(_server->worker(), new CreateUserRequest(this, _name, user)); } void MongoDatabase::dropUser(std::string const& userName) { _bus->send(_server->worker(), new DropUserRequest(this, _name, userName)); } void MongoDatabase::createFunction(const MongoFunction &fun) { _bus->send(_server->worker(), new CreateFunctionRequest(this, _name, _server->version(), fun)); } void MongoDatabase::updateFunction(const std::string &name, const MongoFunction &fun) { _bus->send(_server->worker(), new CreateFunctionRequest(this, _name, _server->version(), fun, name)); } void MongoDatabase::dropFunction(const std::string &name) { _bus->send(_server->worker(), new DropFunctionRequest(this, _name, _server->version(), name)); } void MongoDatabase::handle(LoadCollectionNamesResponse *event) { if (event->isError()) { _bus->publish(new MongoDatabaseCollectionListLoadedEvent(this, event->error())); genericEventErrorHandler(event, "Failed to refresh 'Collections'.", _bus, this); return; } clearCollections(); for (auto const& collectionInfo : event->collectionInfos()) addCollection(new MongoCollection(this, collectionInfo)); _bus->publish(new MongoDatabaseCollectionListLoadedEvent(this, _collections)); LOG_MSG("'Collections' refreshed.", mongo::logger::LogSeverity::Info()); } void MongoDatabase::handle(CreateFunctionResponse *event) { if (event->isError()) { handleIfReplicaSetUnreachable(event); genericEventErrorHandler(event, "Failed to create function \'" + event->functionName + "\'.", _bus, this); } else { loadFunctions(); LOG_MSG("Function \'" + event->functionName + "\' created.", mongo::logger::LogSeverity::Info()); } } void MongoDatabase::handle(CreateUserResponse *event) { if (event->isError()) { handleIfReplicaSetUnreachable(event); genericEventErrorHandler(event, "Failed to create user \'" + event->userName + "\'.", _bus, this); } else { loadUsers(); LOG_MSG("User \'" + event->userName + "\' created.", mongo::logger::LogSeverity::Info()); } } void MongoDatabase::handle(LoadUsersResponse *event) { if (event->isError()) { _bus->publish(new MongoDatabaseUsersLoadedEvent(this, event->error())); if (_server->connectionRecord()->isReplicaSet()) // replica set handleIfReplicaSetUnreachable(event); genericEventErrorHandler(event, "Failed to refresh 'Users'.", _bus, this); return; } _bus->publish(new MongoDatabaseUsersLoadedEvent(this, this, event->users())); LOG_MSG("'Users' refreshed.", mongo::logger::LogSeverity::Info()); } void MongoDatabase::handle(LoadFunctionsResponse *event) { if (event->isError()) { _bus->publish(new MongoDatabaseFunctionsLoadedEvent(this, event->error())); if (_server->connectionRecord()->isReplicaSet()) // replica set handleIfReplicaSetUnreachable(event); genericEventErrorHandler(event, "Failed to refresh 'Functions'.", _bus, this); return; } _bus->publish(new MongoDatabaseFunctionsLoadedEvent(this, this, event->functions())); LOG_MSG("'Functions' refreshed.", mongo::logger::LogSeverity::Info()); } void MongoDatabase::clearCollections() { qDeleteAll(_collections); _collections.clear(); } void MongoDatabase::addCollection(MongoCollection *collection) { _collections.push_back(collection); } void MongoDatabase::handle(CreateCollectionResponse *event) { if (event->isError()) { handleIfReplicaSetUnreachable(event); genericEventErrorHandler(event, "Failed to create collection \'" + event->collection + "\'.", _bus, this); } else { loadCollections(); LOG_MSG("Collection \'" + event->collection + "\' created.", mongo::logger::LogSeverity::Info()); } } void MongoDatabase::handle(DropCollectionResponse *event) { if (event->isError()) { handleIfReplicaSetUnreachable(event); genericEventErrorHandler(event, "Failed to drop collection \'" + event->collection + "\'.", _bus, this); } else { loadCollections(); LOG_MSG("Collection \'" + event->collection + "\' dropped", mongo::logger::LogSeverity::Info()); } } void MongoDatabase::handle(DropFunctionResponse *event) { if (event->isError()) { handleIfReplicaSetUnreachable(event); genericEventErrorHandler(event, "Failed to remove function \'" + event->functionName + "\'.", _bus, this); } else { loadFunctions(); LOG_MSG("Function \'" + event->functionName + "\' removed", mongo::logger::LogSeverity::Info()); } } void MongoDatabase::handle(DropUserResponse *event) { if (event->isError()) { handleIfReplicaSetUnreachable(event); genericEventErrorHandler(event, "Failed to drop user \'" + event->username + "\'.", _bus, this); } else { loadUsers(); LOG_MSG("User \'" + event->username + "\' dropped", mongo::logger::LogSeverity::Info()); } } void MongoDatabase::handle(RenameCollectionResponse *event) { if (event->isError()) { handleIfReplicaSetUnreachable(event); genericEventErrorHandler(event, "Failed to rename collection.", _bus, this); } else { loadCollections(); LOG_MSG("Collection \'" + event->oldCollection + "\' renamed to \'" + event->newCollection +"\'." , mongo::logger::LogSeverity::Info()); } } void MongoDatabase::handle(DuplicateCollectionResponse *event) { if (event->isError()) { handleIfReplicaSetUnreachable(event); genericEventErrorHandler(event, "Failed to duplicate collection \'" + event->sourceCollection + "\'.", _bus, this); } else { loadCollections(); LOG_MSG("Collection \'" + event->sourceCollection + "\' duplicated as \'" + event->duplicateCollection + "\'.", mongo::logger::LogSeverity::Info()); } } void MongoDatabase::handleIfReplicaSetUnreachable(Event *event) { if (!_server->connectionRecord()->isReplicaSet()) return; if (EventError::SetPrimaryUnreachable == event->error().errorCode()) { auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo()); _server->handle(&refreshEvent); } } } ================================================ FILE: src/robomongo/core/domain/MongoDatabase.h ================================================ #pragma once #include #include #include "robomongo/core/Core.h" #include "robomongo/core/events/MongoEvents.h" namespace Robomongo { class EventBus; /** * @brief Represents MongoDB database. */ class MongoDatabase : public QObject { Q_OBJECT public: /** * @brief Database storage engine type */ struct StorageEngineType { static const std::string WIRED_TIGER; static const std::string MMAPV1; }; /** * @brief MongoDB version */ struct DBVersion { static const float MONGODB_2_6; static const float MONGODB_3_0; static const float MONGODB_3_2; }; /** * @brief MongoDatabase * @param server: pointer to parent MongoServer */ MongoDatabase(MongoServer *server, const std::string &name); ~MongoDatabase(); /** * @brief Initiate listCollection asynchronous operation. */ void loadCollections(); /** * @brief Initiate loadUsers asynchronous operation. */ void loadUsers(); void loadFunctions(); void createCollection(const std::string &collection, long long size, bool capped, int maxDocNum, const mongo::BSONObj& extraOptions); void dropCollection(const std::string &collection); void renameCollection(const std::string &collection, const std::string &newCollection); void duplicateCollection(const std::string &collection, const std::string &newCollection); void copyCollection(MongoServer *server, const std::string &sourceDatabase, const std::string &collection); void createUser(const MongoUser &user); void dropUser(std::string const& userName); void createFunction(const MongoFunction &fun); void updateFunction(const std::string &name, const MongoFunction &fun); void dropFunction(const std::string &name); const std::string &name() const { return _name; } /** * @brief Checks that this is a system database. * @return true if system, false otherwise. */ bool isSystem() const { return _system; } MongoServer *server() const { return _server; } protected Q_SLOTS: void handle(LoadCollectionNamesResponse *event); void handle(LoadUsersResponse *event); void handle(LoadFunctionsResponse *event); void handle(CreateFunctionResponse *event); void handle(CreateUserResponse *event); void handle(CreateCollectionResponse *event); void handle(DropCollectionResponse *event); void handle(DropFunctionResponse *event); void handle(DropUserResponse *event); void handle(RenameCollectionResponse *event); void handle(DuplicateCollectionResponse *event); private: void clearCollections(); void addCollection(MongoCollection *collection); void handleIfReplicaSetUnreachable(Event *event); private: MongoServer *_server; std::vector _collections; const std::string _name; const bool _system; EventBus *_bus; }; class MongoDatabaseCollectionListLoadedEvent : public Event { R_EVENT MongoDatabaseCollectionListLoadedEvent(QObject *sender, const std::vector &list) : Event(sender), collections(list) { } MongoDatabaseCollectionListLoadedEvent(QObject *sender, const EventError &error) : Event(sender, error) {} std::vector collections; }; class MongoDatabaseUsersLoadedEvent : public Event { R_EVENT MongoDatabaseUsersLoadedEvent(QObject *sender, MongoDatabase *database, const std::vector &list) : Event(sender), _users(list), _database(database) {} MongoDatabaseUsersLoadedEvent(QObject *sender, const EventError &error) : Event(sender, error) {} std::vector users() const { return _users; } MongoDatabase *database() const { return _database; } private: std::vector _users; MongoDatabase *_database; }; class MongoDatabaseFunctionsLoadedEvent : public Event { R_EVENT MongoDatabaseFunctionsLoadedEvent(QObject *sender, MongoDatabase *database, const std::vector &list) : Event(sender), _functions(list), _database(database) {} MongoDatabaseFunctionsLoadedEvent(QObject *sender, const EventError &error) : Event(sender, error) {} std::vector functions() const { return _functions; } MongoDatabase *database() const { return _database; } private: std::vector _functions; MongoDatabase *_database; }; class MongoDatabaseUsersLoadingEvent : public Event { R_EVENT MongoDatabaseUsersLoadingEvent(QObject *sender) : Event(sender) {} }; class MongoDatabaseFunctionsLoadingEvent : public Event { R_EVENT MongoDatabaseFunctionsLoadingEvent(QObject *sender) : Event(sender) {} }; class MongoDatabaseCollectionsLoadingEvent : public Event { R_EVENT MongoDatabaseCollectionsLoadingEvent(QObject *sender) : Event(sender) {} }; } ================================================ FILE: src/robomongo/core/domain/MongoDocument.cpp ================================================ #include "robomongo/core/domain/MongoDocument.h" #include #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/utils/BsonUtils.h" namespace Robomongo { MongoDocument::MongoDocument() { } MongoDocument::~MongoDocument() { } /* ** Create MongoDocument from BsonObj. It will take owned version of BSONObj */ MongoDocument::MongoDocument(mongo::BSONObj bsonObj) :_bsonObj(bsonObj) { } /* ** Create MongoDocument from BsonObj. It will take owned version of BSONObj */ MongoDocumentPtr MongoDocument::fromBsonObj(const mongo::BSONObj &bsonObj) { MongoDocument *doc = new MongoDocument(bsonObj); return MongoDocumentPtr(doc); } /* ** Create list of MongoDocuments from QList. It will take owned version of BSONObj */ std::vector MongoDocument::fromBsonObj(const std::vector &bsonObjs) { std::vector list; for (std::vector::const_iterator it = bsonObjs.begin(); it != bsonObjs.end(); ++it) { list.push_back(fromBsonObj(*it)); } return list; } } ================================================ FILE: src/robomongo/core/domain/MongoDocument.h ================================================ #pragma once #include #include #include "robomongo/core/Core.h" namespace Robomongo { /* ** Represents MongoDB object. */ class MongoDocument { /* ** Owned BSONObj */ const mongo::BSONObj _bsonObj; public: /* ** Constructs empty Document, i.e. { } */ MongoDocument(); ~MongoDocument(); /* ** Create MongoDocument from BsonObj. It will take owned version of BSONObj */ MongoDocument(mongo::BSONObj bsonObj); /* ** Create MongoDocument from BsonObj. It will take owned version of BSONObj */ static MongoDocumentPtr fromBsonObj(const mongo::BSONObj &bsonObj); /* ** Create list of MongoDocuments from QList. It will take owned version of BSONObj */ static std::vector fromBsonObj(const std::vector &bsonObj); /* ** Return "native" BSONObj */ mongo::BSONObj bsonObj() const { return _bsonObj; } }; } ================================================ FILE: src/robomongo/core/domain/MongoFunction.cpp ================================================ #include "robomongo/core/domain/MongoFunction.h" #include "robomongo/core/utils/BsonUtils.h" #include namespace Robomongo { MongoFunction::MongoFunction(const mongo::BSONObj &obj) { _name = BsonUtils::getField(obj, "_id"); _code = obj.getField("value")._asCode(); } mongo::BSONObj MongoFunction::toBson() const { mongo::BSONObjBuilder builder; mongo::BSONCode code = mongo::BSONCode(_code); builder.append("_id", _name); builder.append("value", code); mongo::BSONObj obj = builder.obj(); return obj; } } ================================================ FILE: src/robomongo/core/domain/MongoFunction.h ================================================ #pragma once #include namespace Robomongo { class MongoFunction { public: /** * @brief Creates function from "system.js" document */ MongoFunction(const mongo::BSONObj &obj); /** * @brief Creates new function with empty attributes */ MongoFunction() {} std::string name() const { return _name; } std::string code() const { return _code; } void setCode(const std::string &code) { _code = code; } void setName(const std::string &name) { _name = name; } mongo::BSONObj toBson() const; private: std::string _name; std::string _code; }; } ================================================ FILE: src/robomongo/core/domain/MongoNamespace.cpp ================================================ #include "robomongo/core/domain/MongoNamespace.h" #include namespace Robomongo { MongoNamespace::MongoNamespace(const std::string &ns) : _ns(ns) { size_t dot = ns.find_first_of('.'); _collectionName = ns.substr(dot + 1); _databaseName = ns.substr(0, dot); } MongoNamespace::MongoNamespace(const std::string &database, const std::string &collection) : _databaseName(database), _collectionName(collection) { _ns = _databaseName + "."; _ns += _collectionName; } } ================================================ FILE: src/robomongo/core/domain/MongoNamespace.h ================================================ #pragma once #include namespace Robomongo { class MongoNamespace { public: MongoNamespace(const std::string &ns); MongoNamespace(const std::string &database, const std::string &collection); MongoNamespace() {} std::string toString() const { return _ns; } std::string databaseName() const { return _databaseName; } std::string collectionName() const { return _collectionName; } bool isValid() const { return !_ns.empty(); } private: std::string _ns; std::string _databaseName; std::string _collectionName; }; } ================================================ FILE: src/robomongo/core/domain/MongoQueryInfo.cpp ================================================ #include "robomongo/core/domain/MongoQueryInfo.h" #include namespace Robomongo { namespace detail { std::string prepareServerAddress(const std::string &address) { size_t pos = address.find_first_of("{"); if (pos != std::string::npos) { return address.substr(0, pos); } return address; } } CollectionInfo::CollectionInfo() {} CollectionInfo::CollectionInfo(const std::string &server, const std::string &database, const std::string &collection) :_serverAddress(server), _ns(database, collection) {} bool CollectionInfo::isValid() const { return !_serverAddress.empty() && _ns.isValid(); } MongoQueryInfo::MongoQueryInfo() {} MongoQueryInfo::MongoQueryInfo(const CollectionInfo &info, mongo::BSONObj query, mongo::BSONObj fields, int limit, int skip, int batchSize, int options, bool special) : _info(info), _query(query), _fields(fields), _limit(limit), _skip(skip), _batchSize(batchSize), _options(options), _special(special) {} } ================================================ FILE: src/robomongo/core/domain/MongoQueryInfo.h ================================================ #pragma once #include #include "robomongo/core/domain/MongoNamespace.h" namespace Robomongo { namespace detail { std::string prepareServerAddress(const std::string &address); } struct CollectionInfo { CollectionInfo(); CollectionInfo(const std::string &server, const std::string &database, const std::string &collection); bool isValid() const; std::string _serverAddress; MongoNamespace _ns; }; struct MongoQueryInfo { MongoQueryInfo(); MongoQueryInfo(const CollectionInfo &info, mongo::BSONObj query, mongo::BSONObj fields, int limit, int skip, int batchSize, int options, bool special); CollectionInfo _info; mongo::BSONObj _query; mongo::BSONObj _fields; int _limit; int _skip; int _batchSize; int _options; bool _special; // flag, indicating that `query` contains special fields on // first level, and query data in `query` field. }; } ================================================ FILE: src/robomongo/core/domain/MongoServer.cpp ================================================ #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/SshSettings.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/core/mongodb/MongoWorker.h" #include "robomongo/core/mongodb/SshTunnelWorker.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/events/MongoEventsInfo.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/MainWindow.h" #include "robomongo/utils/common.h" #include "robomongo/utils/StringOperations.h" #include namespace Robomongo { R_REGISTER_EVENT(MongoServerLoadingDatabasesEvent) MongoServer::MongoServer(int handle, ConnectionSettings *settings, ConnectionType connectionType) : QObject(), _version(0.0f), _connectionType(connectionType), _worker(nullptr), _isConnected(false), _connSettings(settings), _handle(handle), _bus(AppRegistry::instance().bus()), _app(AppRegistry::instance().app()), _replicaSetInfo(nullptr) {} bool MongoServer::isConnected() const { return _isConnected; } ConnectionSettings *MongoServer::connectionRecord() const { return _connSettings.get(); } MongoServer::~MongoServer() { clearDatabases(); if (_worker) { _worker->stopAndDelete(); } // MongoWorker "_worker" is not deleted here, because it is now owned by // another thread (call to moveToThread() made in MongoWorker constructor). // It will be deleted by this thread by means of "deleteLater()", which // is also specified in MongoWorker constructor. } void MongoServer::tryConnect() { _bus->send(_worker, new EstablishConnectionRequest(this, _connectionType, _connSettings->uuid().toStdString())); } void MongoServer::tryRefresh() { _bus->send(_worker, new EstablishConnectionRequest(this, ConnectionRefresh, _connSettings->uuid().toStdString())); } void MongoServer::tryRefreshReplicaSetConnection() { _bus->send(_worker, new EstablishConnectionRequest(this, ConnectionRefresh, _connSettings->uuid().toStdString())); } void MongoServer::tryRefreshReplicaSetFolder(bool expanded, bool showLoading /*= true*/) { if (!_connSettings->isReplicaSet()) return; if (showLoading) _bus->publish(new ReplicaSetFolderLoading(this)); _bus->send(_worker, new RefreshReplicaSetFolderRequest(this, expanded)); } QStringList MongoServer::getDatabasesNames() const { QStringList result; for (QList::const_iterator it = _databases.begin(); it != _databases.end(); ++it) { MongoDatabase *datab = *it; result.append(QtUtils::toQString(datab->name())); } return result; } void MongoServer::addDatabase(MongoDatabase *database) { _databases.append(database); } void MongoServer::createDatabase(const std::string &dbName) { _bus->send(_worker, new CreateDatabaseRequest(this, dbName)); } MongoDatabase *MongoServer::findDatabaseByName(const std::string &dbName) const { for (QList::const_iterator it = _databases.begin(); it != _databases.end(); ++it) { MongoDatabase *datab = *it; if (datab->name() == dbName) { return datab; } } return NULL; } void MongoServer::dropDatabase(const std::string &dbName) { _bus->send(_worker, new DropDatabaseRequest(this, dbName)); } void MongoServer::insertDocuments(const std::vector &objCont, const MongoNamespace &ns) { for (std::vector::const_iterator it = objCont.begin(); it != objCont.end(); it++) { insertDocument(*it, ns); } } void MongoServer::insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) { _bus->send(_worker, new InsertDocumentRequest(this, obj, ns)); } void MongoServer::saveDocuments(const std::vector &objCont, const MongoNamespace &ns) { for (std::vector::const_iterator it = objCont.begin(); it != objCont.end(); it++) { saveDocument(*it, ns); } } void MongoServer::saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) { _bus->send(_worker, new InsertDocumentRequest(this, obj, ns, true)); } void MongoServer::removeDocuments(mongo::Query query, const MongoNamespace &ns, RemoveDocumentCount removeCount, int index) { _bus->send(_worker, new RemoveDocumentRequest(this, query, ns, removeCount, index)); } void MongoServer::loadDatabases() { _bus->publish(new MongoServerLoadingDatabasesEvent(this)); if (_connSettings->isReplicaSet()) { tryRefreshReplicaSetConnection(); } else { // single server _bus->send(_worker, new LoadDatabaseNamesRequest(this)); } } void MongoServer::clearDatabases() { qDeleteAll(_databases); _databases.clear(); } void MongoServer::handle(EstablishConnectionResponse *event) { _connectionType = event->connectionType; // In any case, replica set info must be updated, there might be reachable secondary(ies). // Also cached set name must be updated or cleared for failed connections. updateReplicaSetSettings(event); // --- Connection Failed if (event->isError()) { handleConnectionFailure(event); return; } // --- Connections Successful // Save various information after successful connection const ConnectionInfo &info = event->info; _version = info._version; _storageEngineType = info._storageEngineType; _isConnected = true; // ConnectionRefresh is used just to update connection view (_version, _storageEngineType, _repPrimary etc..) // So we return here after updating(refreshing) information related to connection view. if (ConnectionRefresh == event->connectionType) { if (_connSettings->isReplicaSet()) { // If it is replica set connection, do not return yet. LOG_MSG("Replica set refreshed. [" + _connSettings->connectionName() + ']', mongo::logger::LogSeverity::Info()); } else { // single server LOG_MSG("Server refreshed.", mongo::logger::LogSeverity::Info()); return; } } // Only for single servers if (!_connSettings->isReplicaSet()) { _bus->publish(new ConnectionEstablishedEvent(this, _connectionType, info)); // Do nothing if this is not a "primary" connection if (ConnectionPrimary != _connectionType) return; } if (ConnectionPrimary == _connectionType) LOG_MSG("Establish connection successful. Connection: " + _connSettings->connectionName(), mongo::logger::LogSeverity::Info()); clearDatabases(); for (auto const& dbname : info._databases) { MongoDatabase *db = new MongoDatabase(this, dbname); addDatabase(db); // todo: serverClones for replica sets should not do this } if (_connSettings->isReplicaSet()) { _bus->publish(new ConnectionEstablishedEvent(this, event->connectionType, info)); // In order to do first connection much faster, time consuming refresh // "repSetMonitor->startOrContinueRefresh(). refreshAll()" is being requested after // successful connection. if (ConnectionPrimary == event->connectionType) _bus->send(_worker, new RefreshReplicaSetFolderRequest(this, false)); } // Save connected db version if not saved before and if this is primary connection. QString const versionStr = QString::fromStdString(info._dbVersionStr); auto const& settingsManager = AppRegistry::instance().settingsManager(); if (ConnectionPrimary == _connectionType && !settingsManager->dbVersionsConnected().contains(versionStr)) { settingsManager->addDbVersionConnected(versionStr); settingsManager->save(); } } void MongoServer::handle(RefreshReplicaSetFolderResponse *event) { handleReplicaSetRefreshEvents(event->isError(), event->error(), event->replicaSet, event->expanded); } void MongoServer::handle(LoadDatabaseNamesResponse *event) { if (event->isError()) { _bus->publish(new DatabaseListLoadedEvent(this, event->error())); return; } clearDatabases(); for (auto const& dbname : event->databaseNames) addDatabase(new MongoDatabase(this, dbname)); _bus->publish(new DatabaseListLoadedEvent(this, _databases)); LOG_MSG("Database list refreshed. Connection: " + _connSettings->connectionName(), mongo::logger::LogSeverity::Info()); } void MongoServer::handle(InsertDocumentResponse *event) { if (event->isError()) { hideProgressBar(); if (_connSettings->isReplicaSet()) { if (ConnectionPrimary == _connectionType) { // Insert document from explorer context menu if (EventError::SetPrimaryUnreachable == event->error().errorCode()) { auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo()); handle(&refreshEvent); } } else { // Insert document from tab results window (Notifier, OutputWindow widget) _bus->publish(new InsertDocumentResponse(this, event->error())); } } genericEventErrorHandler(event, "Failed to insert document.", _bus, this); } else { _bus->publish(new InsertDocumentResponse(this, event->error())); LOG_MSG("Document inserted.", mongo::logger::LogSeverity::Info()); } } void MongoServer::handle(RemoveDocumentResponse *event) { if (event->removeCount == RemoveDocumentCount::MULTI && event->index > 0) return; std::string subStr; switch (event->removeCount) { case RemoveDocumentCount::ONE: subStr = "document."; break; case RemoveDocumentCount::MULTI: subStr = "documents."; break; case RemoveDocumentCount::ALL: subStr = "all documents."; break; default: subStr = "(logic error)."; break; } if (event->isError()) { hideProgressBar(); if (_connSettings->isReplicaSet() && EventError::SetPrimaryUnreachable == event->error().errorCode()) { auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo()); handle(&refreshEvent); } genericEventErrorHandler(event, "Failed to remove " + subStr, _bus, this); } else { // success _bus->publish(new RemoveDocumentResponse(this, event->error(), event->removeCount, event->index)); LOG_MSG("Removed " + subStr, mongo::logger::LogSeverity::Info()); } } void MongoServer::runWorkerThread() { _worker = new MongoWorker(_connSettings->clone(), AppRegistry::instance().settingsManager()->loadMongoRcJs(), AppRegistry::instance().settingsManager()->batchSize(), AppRegistry::instance().settingsManager()->mongoTimeoutSec(), AppRegistry::instance().settingsManager()->shellTimeoutSec()); } void MongoServer::handle(CreateDatabaseResponse *event) { if (event->isError()) { if (_connSettings->isReplicaSet() && EventError::SetPrimaryUnreachable == event->error().errorCode()) { auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo()); handle(&refreshEvent); } genericEventErrorHandler(event, "Failed to create database \'" + event->database + "\'.", _bus, this); } else { loadDatabases(); LOG_MSG("Database \'" + event->database + "\' created.", mongo::logger::LogSeverity::Info()); } } void MongoServer::handle(DropDatabaseResponse *event) { if (event->isError()) { if (_connSettings->isReplicaSet() && EventError::SetPrimaryUnreachable == event->error().errorCode()) { auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo()); handle(&refreshEvent); } genericEventErrorHandler(event, "Failed to drop database \'" + event->database + "\'.", _bus, this); } else { loadDatabases(); LOG_MSG("Database \'" + event->database + "\' dropped.", mongo::logger::LogSeverity::Info()); } } void MongoServer::handle(ReplicaSetRefreshed *event) { handleReplicaSetRefreshEvents(event->isError(), event->error(), event->replicaSet, false); } void MongoServer::changeWorkerShellTimeout(int newTimeout) { _worker->changeTimeout(newTimeout); } void MongoServer::handleReplicaSetRefreshEvents(bool isError, EventError eventError, ReplicaSet const& replicaSet, bool expanded) { if (isError) { // Primary is unreachable _replicaSetInfo.reset(new ReplicaSet(replicaSet)); LOG_MSG("Replica set folder refreshed with error. " + eventError.errorMessage() + ". Connection: " + _connSettings->connectionName(), mongo::logger::LogSeverity::Error()); _bus->publish(new ReplicaSetFolderRefreshed(this, eventError, true)); return; } // Primary is reachable // Update replica set settings and mongo server _replicaSetInfo _replicaSetInfo.reset(new ReplicaSet(replicaSet)); _connSettings->setServerHost(_replicaSetInfo->primary.host()); _connSettings->setServerPort(_replicaSetInfo->primary.port()); _connSettings->replicaSetSettings()->setCachedSetName( _connSettings->replicaSetSettings()->setNameUserEntered().empty() ? _replicaSetInfo->setName : ""); LOG_MSG("Replica set folder refreshed. Connection: " + _connSettings->connectionName(), mongo::logger::LogSeverity::Info()); _bus->publish(new ReplicaSetFolderRefreshed(this, expanded)); } void MongoServer::updateReplicaSetSettings(EstablishConnectionResponse* event) { if (!_connSettings->isReplicaSet()) return; _replicaSetInfo.reset(new ReplicaSet(event->replicaSet)); _connSettings->setServerHost(_replicaSetInfo->primary.host()); _connSettings->setServerPort(_replicaSetInfo->primary.port()); _connSettings->replicaSetSettings()->setCachedSetName( _connSettings->replicaSetSettings()->setNameUserEntered().empty() ? _replicaSetInfo->setName : ""); if (_connSettings->replicaSetSettings()->setNameUserEntered().empty()) { // Cache replica set name for 2 times faster first connection if (ConnectionPrimary == _connectionType) { ConnectionSettings* originalConnSettings = AppRegistry::instance().settingsManager() ->getConnectionSettingsByUuid(event->info._uuid); if (originalConnSettings) { auto setName = event->isError() ? "" : _replicaSetInfo->setName; originalConnSettings->replicaSetSettings()->setCachedSetName(setName); AppRegistry::instance().settingsManager()->save(); LOG_MSG("Replica set name cached as \"" + setName + "\".", mongo::logger::LogSeverity::Info()); } else LOG_MSG("Failed to cache the replica set name.", mongo::logger::LogSeverity::Warning()); } } else { // User entered set name is not empty, clear cached set name just in case ConnectionSettings* originalConnSettings = AppRegistry::instance().settingsManager() ->getConnectionSettingsByUuid(event->info._uuid); if (originalConnSettings) { originalConnSettings->replicaSetSettings()->setCachedSetName(""); AppRegistry::instance().settingsManager()->save(); LOG_MSG("Replica set's cached set name cleared. Using user entered set name.", mongo::logger::LogSeverity::Info()); } } } void MongoServer::handleConnectionFailure(EstablishConnectionResponse* event) { _isConnected = false; std::stringstream ss("Unknown error"); auto eventErrorReason = event->errorReason; if (_connSettings->isReplicaSet()) { ss.clear(); std::string server = ""; if (_connSettings->replicaSetSettings()->members().size() > 0) server = "[" + _connSettings->replicaSetSettings()->members().front() + "]"; if (event->error().errorCode() == EventError::ErrorCode::ServerHasDifferentMembers) { ss << "Cannot connect to replica set \"" << _connSettings->connectionName() << "\"" << server << ". \n\nA primary with different host name [" << event->replicaSet.primary << "] found in server side. " "Please double check if same host names and ports are used as in server's replica set" " configuration. \nIf same set name is used for different replica sets, this configuration" " is supported only on different instances of Robomongo. " " Please open a new Robomongo instance for each replica set which has the same set name." "\n\nReason:\n" << event->error().errorMessage(); } else { ss << "Cannot connect to replica set \"" << _connSettings->connectionName() << "\"" << server << ". \nSet's primary is unreachable.\n\nReason:\n" << event->error().errorMessage(); } _bus->publish(new ConnectionFailedEvent(this, _handle, event->connectionType, ss.str(), ConnectionFailedEvent::MongoConnection)); _app->fireConnectionFailedEvent(_handle, event->connectionType, ss.str(), ConnectionFailedEvent::MongoConnection); LOG_MSG("Establish connection failed. " + event->error().errorMessage() + ". Connection: " + _connSettings->connectionName(), mongo::logger::LogSeverity::Error()); } else // single server { if (EstablishConnectionResponse::ErrorReason::MongoSslConnection == eventErrorReason) { auto reason = ConnectionFailedEvent::SslConnection; ss.clear(); ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress() << ".\n\nError:\n" << "TLS connection failure: " << event->error().errorMessage(); _app->fireConnectionFailedEvent(_handle, _connectionType, ss.str(), reason); } else { auto reason = (EstablishConnectionResponse::ErrorReason::MongoAuth == eventErrorReason) ? ConnectionFailedEvent::MongoAuth : ConnectionFailedEvent::MongoConnection; ss.clear(); ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress() << ".\n\nError:\n" << event->error().errorMessage(); _app->fireConnectionFailedEvent(_handle, _connectionType, ss.str(), reason); } // When connection cannot be established, we should cleanup this instance of MongoServer if it wasn't // shown in UI (i.e. it is not a Secondary connection that is used for shells tab) if (_connectionType == ConnectionPrimary || _connectionType == ConnectionTest) { LOG_MSG("Establish connection failed. " + event->error().errorMessage() + ". Connection: " + _connSettings->connectionName(), mongo::logger::LogSeverity::Error()); _app->closeServer(this); } } } void MongoServer::hideProgressBar() const { MainWindow* mainWindow = nullptr; for (auto wid : QApplication::topLevelWidgets()) { if ((mainWindow = qobject_cast(wid))) break; } if(mainWindow) mainWindow->hideQueryWidgetProgressBar(); } } // namespace Robomongo ================================================ FILE: src/robomongo/core/domain/MongoServer.h ================================================ #pragma once #include #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/events/MongoEvents.h" namespace Robomongo { class MongoWorker; class MongoDatabase; class EventBus; class App; // Messages struct EstablishConnectionResponse; struct RefreshReplicaSetFolderResponse; class LoadDatabaseNamesResponse; class InsertDocumentResponse; struct CreateDatabaseResponse; struct DropDatabaseResponse; /** * @brief MongoServer represents active connection to MongoDB server. * MongoServer is an Aggregate Root, that manages three internal entities: * MongoDatabase, MongoCollection and MongoWorker. */ class MongoServer : public QObject { Q_OBJECT public: /** * @brief MongoServer * @param connectionRecord: MongoServer will own this ConnectionSettings. * @param visible * @param defaultDatabase */ MongoServer(int handle, ConnectionSettings *connectionRecord, ConnectionType connectionType); ~MongoServer(); void runWorkerThread(); /** * @brief Try to connect to MongoDB single server or replica set. * @throws MongoException, if fails */ void tryConnect(); /** * @brief Try to re-connect to MongoDB server in order to refresh connection view. * Never shown in Explorer and can be used to refresh (via reconnecting) current connection view. * (i.e. db version, storage engine etc...) * @throws MongoException, if fails */ void tryRefresh(); /** * @brief Try to re-connect to MongoDB replica set in order to refresh connection view. * It is used to refresh (via reconnecting) current connection view. * (i.e. db version, storage engine, current replica set primary, status of replica set etc...) * It will update Explorer widgets depending on replica set status (online if primary reachable and * offline otherwise) */ void tryRefreshReplicaSetConnection(); /** * @brief Try to re-connect to MongoDB replica set in order to refresh connection view. * It is used to refresh (via reconnecting) current connection view. * (i.e. db version, storage engine, current replica set primary, status of replica set etc...) * It will update only 'Replica Set' folder widgets depending on replica set status * (online if primary reachable and offline otherwise) */ void tryRefreshReplicaSetFolder(bool expanded, bool showLoading = true); bool isConnected() const; void addDatabase(MongoDatabase *database); void createDatabase(const std::string &dbName); void dropDatabase(const std::string &dbName); QStringList getDatabasesNames() const; QList const& databases() const { return _databases; }; MongoDatabase *findDatabaseByName(const std::string &dbName) const; void insertDocuments(const std::vector &objCont, const MongoNamespace &ns); void insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns); void saveDocuments(const std::vector &objCont, const MongoNamespace &ns); void saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns); void removeDocuments(mongo::Query query, const MongoNamespace &ns, RemoveDocumentCount removeCount, int index = 0); float version() const{ return _version; } const std::string& getStorageEngineType() const { return _storageEngineType; } /** * @brief Returns associated connection record */ ConnectionSettings *connectionRecord() const; /** * @brief Loads databases of this server asynchronously. */ void loadDatabases(); MongoWorker *const worker() const { return _worker; } ReplicaSet* replicaSetInfo() const { return _replicaSetInfo.get(); } void handle(ReplicaSetRefreshed *event); void changeWorkerShellTimeout(int newTimeout); protected Q_SLOTS: void handle(EstablishConnectionResponse *event); void handle(RefreshReplicaSetFolderResponse *event); void handle(LoadDatabaseNamesResponse *event); void handle(InsertDocumentResponse *event); void handle(RemoveDocumentResponse *event); void handle(CreateDatabaseResponse *event); void handle(DropDatabaseResponse *event); private: void clearDatabases(); void handleReplicaSetRefreshEvents(bool isError, EventError eventError, ReplicaSet const& replicaSet, bool expanded); void updateReplicaSetSettings(EstablishConnectionResponse* event); void handleConnectionFailure(EstablishConnectionResponse* event); void hideProgressBar() const; MongoWorker *_worker; std::unique_ptr _connSettings; EventBus *_bus; App *_app; float _version; std::string _storageEngineType; ConnectionType _connectionType; bool _isConnected; int _handle; QList _databases; std::unique_ptr _replicaSetInfo; }; class MongoServerLoadingDatabasesEvent : public Event { R_EVENT MongoServerLoadingDatabasesEvent(QObject *sender) : Event(sender) {} }; } ================================================ FILE: src/robomongo/core/domain/MongoShell.cpp ================================================ #include "robomongo/core/domain/MongoShell.h" #include "mongo/scripting/engine.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/mongodb/MongoWorker.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/settings/SettingsManager.h" namespace Robomongo { auto const& eventBus = []() { return AppRegistry::instance().bus(); }; MongoShell::MongoShell(MongoServer *server, ScriptInfo scriptInfo) : QObject(), _scriptInfo(scriptInfo), _server(server) { } void MongoShell::open(const std::string &script, const std::string &dbName) { eventBus()->publish(new ScriptExecutingEvent(this)); _scriptInfo.setScript(QtUtils::toQString(script)); eventBus()->send(_server->worker(), new ExecuteScriptRequest(this, query(), dbName)); LOG_MSG(_scriptInfo.script(), mongo::logger::LogSeverity::Info()); } std::string MongoShell::query() const { return QtUtils::toStdString(_scriptInfo.script()); } void MongoShell::execute(const std::string &script /* = "" */, const std::string &dbName /* = "" */) { if (!_scriptInfo.execute()) return; std::string const finalScript = script.empty() ? query() : script; eventBus()->publish(new ScriptExecutingEvent(this)); eventBus()->send(_server->worker(), new ExecuteScriptRequest(this, finalScript, dbName, _aggrInfo)); if (!_scriptInfo.script().isEmpty()) LOG_MSG(_scriptInfo.script(), mongo::logger::LogSeverity::Info()); } void MongoShell::query(int resultIndex, const MongoQueryInfo &info) { eventBus()->send(_server->worker(), new ExecuteQueryRequest(this, resultIndex, info)); } void MongoShell::autocomplete(const std::string &prefix) { AutocompletionMode autocompletionMode { AppRegistry::instance().settingsManager()->autocompletionMode() }; if (autocompletionMode == AutocompleteNone) return; eventBus()->send(_server->worker(), new AutocompleteRequest(this, prefix, autocompletionMode) ); } void MongoShell::stop() { // _server->worker()->interrupt(); // mongo::Scope::setInterruptFlag(true); } bool MongoShell::loadFromFile() { return _scriptInfo.loadFromFile(); } bool MongoShell::saveToFileAs() { return _scriptInfo.saveToFileAs(); } bool MongoShell::saveToFile() { return _scriptInfo.saveToFile(); } void MongoShell::handle(ExecuteQueryResponse *event) { if (event->isError()) { eventBus()->publish(new DocumentListLoadedEvent(this, event->error())); return; } eventBus()->publish( new DocumentListLoadedEvent(this, event->resultIndex, event->queryInfo, query(), event->documents) ); } void MongoShell::handle(ExecuteScriptResponse *event) { if (!event->isError()) { eventBus()->publish( new ScriptExecutedEvent(this, event->result, event->empty, event->timeoutReached()) ); return; } if (_server->connectionRecord()->isReplicaSet()) { eventBus()->publish( new ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo()) ); eventBus()->publish( new ScriptExecutedEvent(this, event->error(), event->timeoutReached()) ); return; } else { // single server eventBus()->publish( new ScriptExecutedEvent(this, event->error(), event->timeoutReached()) ); return; } } void MongoShell::handle(AutocompleteResponse *event) { if (event->isError()) { eventBus()->publish(new AutocompleteResponse(this, event->error())); return; } eventBus()->publish(new AutocompleteResponse(this, event->list, event->prefix)); } } ================================================ FILE: src/robomongo/core/domain/MongoShell.h ================================================ #pragma once #include #include "robomongo/core/events/MongoEvents.h" #include "robomongo/core/domain/ScriptInfo.h" #include "robomongo/core/domain/MongoAggregateInfo.h" namespace Robomongo { struct AggrInfo; class MongoShell : public QObject { Q_OBJECT public: MongoShell(MongoServer *server, ScriptInfo scriptInfo); void open(const std::string &script, const std::string &dbName = std::string()); void query(int resultIndex, const MongoQueryInfo &info); void autocomplete(const std::string &prefix); void stop(); MongoServer *server() const { return _server; } std::string query() const; void execute(const std::string &script = "", const std::string &dbName = ""); bool isExecutable() const { return _scriptInfo.execute(); } const QString &title() const { return _scriptInfo.title(); } std::string dbname() const { return _scriptInfo.dbname(); } const CursorPosition &cursor() const { return _scriptInfo.cursor(); } void setScript(const QString &script) { return _scriptInfo.setScript(script); } void setScriptExecutable(bool execute) { _scriptInfo.setExecutable(execute); } void setAggrInfo(AggrInfo const& aggrInfo) { _aggrInfo = aggrInfo; } QString filePath() const { return _scriptInfo.filePath(); } bool saveToFile(); bool saveToFileAs(); bool loadFromFile(); protected Q_SLOTS: void handle(ExecuteQueryResponse *event); void handle(ExecuteScriptResponse *event); void handle(AutocompleteResponse *event); private: ScriptInfo _scriptInfo; AggrInfo _aggrInfo; MongoServer *_server; }; } ================================================ FILE: src/robomongo/core/domain/MongoShellResult.h ================================================ #pragma once #include "robomongo/core/domain/MongoQueryInfo.h" #include "robomongo/core/domain/MongoAggregateInfo.h" #include "robomongo/core/domain/MongoDocument.h" namespace Robomongo { /* -------------- MongoShellResult Class --------- */ class MongoShellResult { public: MongoShellResult( const std::string &type, const std::string &response, const std::vector &documents, const MongoQueryInfo &queryInfo, const std::string &statement, qint64 elapsedms, AggrInfo aggrInfo = AggrInfo()) : _type(type), _response(response), _documents(documents), _queryInfo(queryInfo), _statement(statement), _elapsedms(elapsedms), _aggrInfo(aggrInfo) { } std::string response() const { return _response; } std::string type() const { return _type; } std::vector documents() const { return _documents; } MongoQueryInfo queryInfo() const { return _queryInfo; } std::string statement() const { return _statement; } std::string statementShort() const { std::size_t const LEN = _statement.size() < 10 ? _statement.size() : 10; std::string statementShort { _statement, 0, LEN }; statementShort.append((_statement.size() > 10) ? ".." : ""); return statementShort; } qint64 elapsedMs() const { return _elapsedms; } AggrInfo const& aggrInfo() const { return _aggrInfo; } private: std::string _type; std::string _response; std::vector _documents; MongoQueryInfo _queryInfo; std::string const _statement; qint64 _elapsedms; AggrInfo _aggrInfo = AggrInfo(); }; /* -------------- MongoShellExecResult Class --------- */ class MongoShellExecResult { public: MongoShellExecResult() { } MongoShellExecResult( const std::vector &results, const std::string ¤tServer, bool isCurrentServerValid, const std::string ¤tDatabase, bool isCurrentDatabaseValid, bool timeoutReached = false) : _results(results), _currentServer(currentServer), _currentDatabase(currentDatabase), _isCurrentServerValid(isCurrentServerValid), _isCurrentDatabaseValid(isCurrentDatabaseValid), _timeoutReached(timeoutReached) { } MongoShellExecResult(bool error, std::string const& errorMsg = "", bool timeoutReached = false) : _error(error), _errorMessage(errorMsg), _timeoutReached(timeoutReached) { } std::vector const& results() const { return _results; } std::string currentServer() const { return _currentServer; } void setCurrentServer(std::string const& server) { _currentServer = server; } std::string currentDatabase() const { return _currentDatabase; } bool isCurrentServerValid() const { return _isCurrentServerValid; } bool isCurrentDatabaseValid() const { return _isCurrentDatabaseValid; } std::string errorMessage() const { return _errorMessage; } bool error() const { return _error; } bool timeoutReached() const { return _timeoutReached; } private: std::vector _results; std::string _currentServer; std::string _currentDatabase; bool _isCurrentServerValid; bool _isCurrentDatabaseValid; std::string _errorMessage; bool _error = false; bool _timeoutReached = false; }; } ================================================ FILE: src/robomongo/core/domain/MongoUser.cpp ================================================ ================================================ FILE: src/robomongo/core/domain/MongoUser.h ================================================ #pragma once #include #include #include "robomongo/core/utils/BsonUtils.h" namespace Robomongo { class MongoUser { public: typedef std::vector RolesVector; explicit MongoUser(const float version, const mongo::BSONObj &obj) : _version(version), _readOnly(false), _name(BsonUtils::getField(obj, "user")) {}; // Creates new user with empty attributes MongoUser(const float version) : _version(version), _readOnly(false), _roles() {} std::string name() const { return _name; } std::string password() const { return _password; } std::vector roles() const { return _roles; } void setName(const std::string &name) { _name = name; } void setPassword(const std::string &pwd) { _password = pwd; } void setRoles(const std::vector &roles) { _roles = roles; } std::string userSource() const { return _userSource; } void setUserSource(const std::string &source) { _userSource = source; } bool readOnly() const { return _readOnly; } void setReadOnly(bool readonly) { _readOnly = readonly; } float version() const { return _version; } static constexpr float minimumSupportedVersion = 2.4f; private: float _version; std::string _name; bool _readOnly; RolesVector _roles; std::string _password; std::string _userSource; }; } ================================================ FILE: src/robomongo/core/domain/MongoUtils.cpp ================================================ #include "robomongo/core/domain/MongoUtils.h" #include using namespace std; #include "mongo/util/md5.hpp" namespace Robomongo { namespace MongoUtils { QString buildNiceSizeString(double sizeBytes) { if (sizeBytes < 1024 * 100) { double kb = ((double) sizeBytes) / 1024; return QString("%1 kb").arg(kb, 2, 'f', 2); } double mb = ((double) sizeBytes) / 1024 / 1024; return QString("%1 mb").arg(mb, 2, 'f', 2); } std::string buildPasswordHash(const std::string &username, const std::string &password) { std::string sum = username + ":mongo:" + password; const char * s = sum.c_str(); mongo::md5digest d; md5_state_t st; md5_init(&st); md5_append( &st , (const md5_byte_t*)s , strlen( s ) ); md5_finish(&st, d); return mongo::digestToString(d); } } } ================================================ FILE: src/robomongo/core/domain/MongoUtils.h ================================================ #pragma once #include namespace Robomongo { namespace MongoUtils { QString buildNiceSizeString(double sizeBytes); std::string buildPasswordHash(const std::string &username, const std::string &password); } } ================================================ FILE: src/robomongo/core/domain/Notifier.cpp ================================================ #include "robomongo/core/domain/Notifier.h" #include #include #include #include #include #include #include "robomongo/core/domain/MongoShell.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/utils/BsonUtils.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/events/MongoEvents.h" #include "robomongo/shell/db/ptimeutil.h" #include "robomongo/gui/MainWindow.h" #include "robomongo/gui/widgets/workarea/BsonTreeItem.h" #include "robomongo/gui/dialogs/DocumentTextEditor.h" #include "robomongo/gui/utils/DialogUtils.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/EventBus.h" namespace Robomongo { namespace detail { bool isSimpleType(Robomongo::BsonTreeItem const *item) { return BsonUtils::isSimpleType(item->type()) || BsonUtils::isUuidType(item->type(), item->binType()); } bool isObjectIdType(Robomongo::BsonTreeItem *item) { return mongo::jstOID == item->type(); } bool isMultiSelection(const QModelIndexList &indexes) { return indexes.count() > 1; } bool isDocumentType(BsonTreeItem const *item) { return BsonUtils::isDocument(item->type()); } bool isArrayChild(BsonTreeItem const *item) { return BsonUtils::isArray(dynamic_cast(item->parent())->type()); } bool isDocumentRoot(BsonTreeItem const *item) { return ( item == item->superParent() ); } /** * * @param QModelIndexList indexes * @param bool returnSuperParents If TRUE, only indexes of super-parents will be in result list * @return QModelIndexList */ QModelIndexList uniqueRows(QModelIndexList indexes, bool returnSuperParents) { QModelIndexList result; for (QModelIndexList::const_iterator it = indexes.begin(); it != indexes.end(); ++it) { QModelIndex isUnique = *it; Robomongo::BsonTreeItem *item = Robomongo::QtUtils::item(isUnique); if (item) { for (QModelIndexList::const_iterator jt = result.begin(); jt != result.end(); ++jt) { Robomongo::BsonTreeItem *jItem = Robomongo::QtUtils::item(*jt); if (jItem && jItem->superParent() == item->superParent()) { isUnique = QModelIndex(); break; } } if (isUnique.isValid()) { if (returnSuperParents) { // Move index onto "super parent" element before pushing it into result set QModelIndex parent = isUnique.parent(); while (parent != QModelIndex()) { isUnique = parent; parent = parent.parent(); } } result.append(isUnique); } } } return result; } } Notifier::Notifier(INotifierObserver *const observer, MongoShell *shell, const MongoQueryInfo &queryInfo, QObject *parent) : BaseClass(parent), _observer(observer), _shell(shell), _queryInfo(queryInfo) { QWidget *wid = dynamic_cast(_observer); AppRegistry::instance().bus()->subscribe(this, InsertDocumentResponse::Type, _shell->server()); AppRegistry::instance().bus()->subscribe(this, RemoveDocumentResponse::Type, _shell->server()); _deleteDocumentAction = new QAction("Delete Document...", wid); VERIFY(connect(_deleteDocumentAction, SIGNAL(triggered()), SLOT(onDeleteDocument()))); _deleteDocumentsAction = new QAction("Delete Documents...", wid); VERIFY(connect(_deleteDocumentsAction, SIGNAL(triggered()), SLOT(onDeleteDocuments()))); _editDocumentAction = new QAction("Edit Document...", wid); VERIFY(connect(_editDocumentAction, SIGNAL(triggered()), SLOT(onEditDocument()))); _viewDocumentAction = new QAction("View Document...", wid); VERIFY(connect(_viewDocumentAction, SIGNAL(triggered()), SLOT(onViewDocument()))); _insertDocumentAction = new QAction("Insert Document...", wid); VERIFY(connect(_insertDocumentAction, SIGNAL(triggered()), SLOT(onInsertDocument()))); _copyValueAction = new QAction("Copy Value", wid); VERIFY(connect(_copyValueAction, SIGNAL(triggered()), SLOT(onCopyDocument()))); _copyValueNameAction = new QAction("Copy Name", wid); VERIFY(connect(_copyValueNameAction, SIGNAL(triggered()), SLOT(onCopyNameDocument()))); _copyValuePathAction = new QAction("Copy Path", wid); VERIFY(connect(_copyValuePathAction, SIGNAL(triggered()), SLOT(onCopyPathDocument()))); _copyTimestampAction = new QAction("Copy Timestamp from ObjectId", wid); VERIFY(connect(_copyTimestampAction, SIGNAL(triggered()), SLOT(onCopyTimestamp()))); _copyJsonAction = new QAction("Copy JSON", wid); VERIFY(connect(_copyJsonAction, SIGNAL(triggered()), SLOT(onCopyJson()))); } void Notifier::initMenu(QMenu *const menu, BsonTreeItem *const item) { bool const isProjection = !_queryInfo._fields.isEmpty(); bool const isEditable = _queryInfo._info.isValid() && !isProjection; bool const onItem = item ? true : false; bool isSimple = false; bool isDocument = false; bool isObjectId = false; bool isNotArrayChild = false; bool isRoot = false; if (item) { isSimple = detail::isSimpleType(item); isDocument = detail::isDocumentType(item); isObjectId = detail::isObjectIdType(item); isNotArrayChild = !detail::isArrayChild(item); isRoot = detail::isDocumentRoot(item); } if (onItem && isEditable) menu->addAction(_editDocumentAction); if (onItem) menu->addAction(_viewDocumentAction); if (isEditable) menu->addAction(_insertDocumentAction); if (onItem && (isSimple || isDocument)) menu->addSeparator(); if (onItem && isSimple) menu->addAction(_copyValueAction); if (onItem && (isSimple || isDocument) && isNotArrayChild && !isRoot) menu->addAction(_copyValueNameAction); if (onItem && (isSimple || isDocument) && !isRoot) menu->addAction(_copyValuePathAction); if (onItem && isObjectId) menu->addAction(_copyTimestampAction); if (onItem && isDocument) menu->addAction(_copyJsonAction); if (onItem && isEditable) menu->addSeparator(); if (onItem && isEditable) menu->addAction(_deleteDocumentAction); } void Notifier::initMultiSelectionMenu(QMenu *const menu) { bool isEditable = _queryInfo._info.isValid(); if (isEditable) menu->addAction(_insertDocumentAction); if (isEditable) menu->addAction(_deleteDocumentsAction); } void Notifier::deleteDocuments(std::vector const& items, bool force) { bool isNeededRefresh = false; int index = 0; for (auto const * const documentItem : items) { if (!documentItem) break; mongo::BSONObj obj = documentItem->superRoot(); mongo::BSONElement id = obj.getField("_id"); if (id.eoo()) { QMessageBox::warning(dynamic_cast(_observer), "Cannot delete", "Selected document doesn't have _id field. \n" "Maybe this is a system document that should be managed in a special way?"); break; } mongo::BSONObjBuilder builder; builder.append(id); mongo::BSONObj bsonQuery = builder.obj(); mongo::Query query(bsonQuery); if (!force) { // Ask user int answer = utils::questionDialog(dynamic_cast(_observer), "Delete", "Document", "%1 %2 with id:
%3?", QtUtils::toQString(id.toString(false))); if (answer != QMessageBox::Yes) break; } isNeededRefresh = true; RemoveDocumentCount removeCount = items.size() == 1 ? RemoveDocumentCount::ONE : RemoveDocumentCount::MULTI; _shell->server()->removeDocuments(query, _queryInfo._info._ns, removeCount, index); ++index; mainWindow()->showQueryWidgetProgressBar(); } } void Notifier::handle(InsertDocumentResponse *event) { if (event->isError()) { // Error mainWindow()->hideQueryWidgetProgressBar(); if (_shell->server()->connectionRecord()->isReplicaSet()) { // Insert document from tab results window (Notifier, OutputWindow widget) if (EventError::SetPrimaryUnreachable == event->error().errorCode()) { AppRegistry::instance().bus()->publish( new ReplicaSetRefreshed(_shell, event->error(), event->error().replicaSetInfo())); } } else // single server QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage())); return; } // Success std::this_thread::sleep_for(std::chrono::milliseconds(100)); _shell->query(0, _queryInfo); } void Notifier::handle(RemoveDocumentResponse *event) { if (event->isError()) { if (!(event->removeCount == RemoveDocumentCount::MULTI && event->index > 0)) QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage())); } else { // Success std::this_thread::sleep_for(std::chrono::milliseconds(100)); _shell->query(0, _queryInfo); } } void Notifier::onCopyNameDocument() { QModelIndex const& selectedInd = _observer->selectedIndex(); if (!selectedInd.isValid()) return; BsonTreeItem const *documentItem = QtUtils::item(selectedInd); if (!documentItem) return; if (!(detail::isSimpleType(documentItem) || detail::isDocumentType(documentItem) || !detail::isArrayChild(documentItem) || !detail::isDocumentRoot(documentItem))) return; QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(QString::fromStdString(documentItem->fieldName())); } void Notifier::onCopyPathDocument() { QModelIndex const& selectedInd = _observer->selectedIndex(); if (!selectedInd.isValid()) return; BsonTreeItem const *documentItem = QtUtils::item(selectedInd); if (!documentItem) return; if (!(detail::isSimpleType(documentItem) || detail::isDocumentType(documentItem) || !detail::isDocumentRoot(documentItem))) return; QStringList namesList; BsonTreeItem const *documentItemHelper = documentItem; while (!detail::isDocumentRoot(documentItemHelper)) { if (!detail::isArrayChild(documentItemHelper)) { namesList.push_front(QString::fromStdString(documentItemHelper->fieldName())); } documentItemHelper = dynamic_cast(documentItemHelper->parent()); } QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(namesList.join(".")); } MainWindow* Notifier::mainWindow() const { MainWindow* mainWindow; for (auto wid : QApplication::topLevelWidgets()) { if ((mainWindow = qobject_cast(wid))) break; } return mainWindow; } void Notifier::handleDeleteCommand() { if (_observer->selectedIndexes().count() > 1) onDeleteDocuments(); else onDeleteDocument(); } void Notifier::onDeleteDocuments() { if (!_queryInfo._info.isValid()) return; QModelIndexList selectedIndexes = _observer->selectedIndexes(); if (!detail::isMultiSelection(selectedIndexes)) return; int const answer = QMessageBox::question(dynamic_cast(_observer), "Delete", QString("Do you want to delete %1 selected documents?"). arg(selectedIndexes.count())); if (QMessageBox::Yes == answer) { std::vector items; for (auto index : selectedIndexes) items.push_back(QtUtils::item(index)); deleteDocuments(items, true); } } void Notifier::onDeleteDocument() { if (!_queryInfo._info.isValid()) return; QModelIndex selectedIndex = _observer->selectedIndex(); if (!selectedIndex.isValid()) return; BsonTreeItem *documentItem = QtUtils::item(selectedIndex); std::vector vec; vec.push_back(documentItem); return deleteDocuments(vec, false); } void Notifier::onEditDocument() { if (!_queryInfo._info.isValid()) return; QModelIndex selectedInd = _observer->selectedIndex(); if (!selectedInd.isValid()) return; BsonTreeItem *documentItem = QtUtils::item(selectedInd); if (!documentItem) return; std::string str = BsonUtils::jsonString(documentItem->superRoot(), mongo::TenGen, 1, AppRegistry::instance().settingsManager()->uuidEncoding(), AppRegistry::instance().settingsManager()->timeZone()); const QString &json = QtUtils::toQString(str); DocumentTextEditor editor(_queryInfo._info, json, false, dynamic_cast(_observer)); editor.setWindowTitle("Edit Document"); int result = editor.exec(); if (result == QDialog::Accepted) { _shell->server()->saveDocuments(editor.bsonObj(), _queryInfo._info._ns); mainWindow()->showQueryWidgetProgressBar(); } } void Notifier::onViewDocument() { QModelIndex selectedIndex = _observer->selectedIndex(); if (!selectedIndex.isValid()) return; BsonTreeItem *documentItem = QtUtils::item(selectedIndex); if (!documentItem) return; mongo::BSONObj obj = documentItem->superRoot(); std::string str = BsonUtils::jsonString(obj, mongo::TenGen, 1, AppRegistry::instance().settingsManager()->uuidEncoding(), AppRegistry::instance().settingsManager()->timeZone()); const QString &json = QtUtils::toQString(str); DocumentTextEditor *editor = new DocumentTextEditor(_queryInfo._info, json, true, dynamic_cast(_observer)); editor->setWindowTitle("View Document"); editor->show(); } void Notifier::onInsertDocument() { if (!_queryInfo._info.isValid()) return; DocumentTextEditor editor(_queryInfo._info, "{\n \n}", false, dynamic_cast(_observer)); editor.setCursorPosition(1, 4); editor.setWindowTitle("Insert Document"); int result = editor.exec(); if (result != QDialog::Accepted) return; DocumentTextEditor::ReturnType obj = editor.bsonObj(); for (DocumentTextEditor::ReturnType::const_iterator it = obj.begin(); it != obj.end(); ++it) { _shell->server()->insertDocument(*it, _queryInfo._info._ns); mainWindow()->showQueryWidgetProgressBar(); } } void Notifier::onCopyDocument() { QModelIndex selectedInd = _observer->selectedIndex(); if (!selectedInd.isValid()) return; BsonTreeItem *documentItem = QtUtils::item(selectedInd); if (!documentItem) return; if (!detail::isSimpleType(documentItem)) return; QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(documentItem->value()); } void Notifier::onCopyTimestamp() { QModelIndex selectedInd = _observer->selectedIndex(); if (!selectedInd.isValid()) return; BsonTreeItem *documentItem = QtUtils::item(selectedInd); if (!documentItem) return; if (!detail::isObjectIdType(documentItem)) return; QClipboard *clipboard = QApplication::clipboard(); // new Date(parseInt(this.valueOf().slice(0,8), 16)*1000); QString hexTimestamp = documentItem->value().mid(10, 8); bool ok; long long milliTimestamp = (long long)hexTimestamp.toLongLong(&ok, 16)*1000; bool isSupportedDate = (miutil::minDate < milliTimestamp) && (milliTimestamp < miutil::maxDate); boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); boost::posix_time::time_duration diff = boost::posix_time::millisec(milliTimestamp); boost::posix_time::ptime time = epoch + diff; if (isSupportedDate) { std::string date = miutil::isotimeString(time, false, false); clipboard->setText("ISODate(\""+QString::fromStdString(date)+"\")"); } else { clipboard->setText("Error extracting ISODate()"); } } void Notifier::onCopyJson() { QModelIndex selectedInd = _observer->selectedIndex(); if (!selectedInd.isValid()) return; BsonTreeItem *documentItem = QtUtils::item(selectedInd); if (!documentItem) return; if (!detail::isDocumentType(documentItem)) return; QClipboard *clipboard = QApplication::clipboard(); mongo::BSONObj obj = documentItem->root(); if (documentItem != documentItem->superParent()) { obj = obj[documentItem->fieldName()].Obj(); } bool isArray = BsonUtils::isArray(documentItem->type()); std::string str = BsonUtils::jsonString(obj, mongo::TenGen, 1, AppRegistry::instance().settingsManager()->uuidEncoding(), AppRegistry::instance().settingsManager()->timeZone(), isArray); const QString &json = QtUtils::toQString(str); clipboard->setText(json); } } ================================================ FILE: src/robomongo/core/domain/Notifier.h ================================================ #pragma once #include #include "robomongo/core/domain/MongoQueryInfo.h" QT_BEGIN_NAMESPACE class QAction; class QMenu; QT_END_NAMESPACE namespace Robomongo { class MainWindow; class MongoShell; class BsonTreeItem; class InsertDocumentResponse; struct RemoveDocumentResponse; namespace detail { bool isSimpleType(BsonTreeItem const *item); bool isObjectIdType(BsonTreeItem *item); bool isMultiSelection(const QModelIndexList &indexes); bool isDocumentType(BsonTreeItem const *item); QModelIndexList uniqueRows(QModelIndexList indexes, bool returnSuperParents = false); } class INotifierObserver { public: virtual QModelIndex selectedIndex() const = 0; virtual QModelIndexList selectedIndexes() const = 0; protected: INotifierObserver() {} }; class Notifier : public QObject { Q_OBJECT public: typedef QObject BaseClass; Notifier(INotifierObserver *const observer, MongoShell *shell, const MongoQueryInfo &queryInfo, QObject *parent = NULL); void initMenu(QMenu *const menu, BsonTreeItem *const item); void initMultiSelectionMenu(QMenu *const menu); void deleteDocuments(std::vector const& items, bool force); void handleDeleteCommand(); public Q_SLOTS: void onDeleteDocument(); void onDeleteDocuments(); void onEditDocument(); void onViewDocument(); void onInsertDocument(); void onCopyDocument(); void onCopyTimestamp(); void onCopyJson(); void handle(InsertDocumentResponse *event); void handle(RemoveDocumentResponse *event); private Q_SLOTS: void onCopyNameDocument(); void onCopyPathDocument(); private: MainWindow* mainWindow() const; QAction *_deleteDocumentAction; QAction *_deleteDocumentsAction; QAction *_editDocumentAction; QAction *_viewDocumentAction; QAction *_insertDocumentAction; QAction *_copyValueAction; QAction *_copyValueNameAction; QAction *_copyValuePathAction; QAction *_copyTimestampAction; QAction *_copyJsonAction; const MongoQueryInfo _queryInfo; MongoShell *_shell; INotifierObserver *const _observer; }; } ================================================ FILE: src/robomongo/core/domain/ScriptInfo.cpp ================================================ #include "robomongo/core/domain/ScriptInfo.h" #include #include #include #include namespace { const QString filterForScripts = QObject::tr("JavaScript (*.js);; All Files (*.*)"); bool loadFromFileText(const QString &filePath, QString &text) { bool result = false; QFile file(filePath); if (file.open(QFile::ReadOnly | QFile::Text)) { QTextStream in(&file); QApplication::setOverrideCursor(Qt::WaitCursor); text = in.readAll(); QApplication::restoreOverrideCursor(); result = true; } else { QMessageBox::critical(QApplication::activeWindow(), QString("Error"), QObject::tr(PROJECT_NAME" can't read from %1:\n%2.") .arg(filePath) .arg(file.errorString())); } return result; } bool saveToFileText(QString filePath, const QString &text) { if (filePath.isEmpty()) return false; #ifdef Q_OS_LINUX if (QFileInfo(filePath).suffix().isEmpty()) { filePath += ".js"; } #endif bool result = false; QFile file(filePath); if (file.open(QFile::WriteOnly | QFile::Text)) { QTextStream out(&file); QApplication::setOverrideCursor(Qt::WaitCursor); out << text; QApplication::restoreOverrideCursor(); result = true; } else { QMessageBox::critical(QApplication::activeWindow(), QString("Error"), QObject::tr(PROJECT_NAME" can't save to %1:\n%2.") .arg(filePath) .arg(file.errorString())); } return result; } } namespace Robomongo { ScriptInfo::ScriptInfo(const QString &script, bool execute, const std::string &dbname, const CursorPosition &position, const QString &title, const QString &filePath) : _script(script), _execute(execute), _dbname(dbname), _title(title), _cursor(position), _filePath(filePath) {} bool ScriptInfo::loadFromFile(const QString &filePath) { bool result = false; QString filepath = QFileDialog::getOpenFileName(QApplication::activeWindow(), filePath, QString(), filterForScripts); if (!filepath.isEmpty()) { QString out; if (loadFromFileText(filepath, out)) { _script = out; _filePath = filepath; result = true; } } return result; } bool ScriptInfo::loadFromFile() { return loadFromFile(_filePath); } bool ScriptInfo::saveToFileAs() { QString filepath = QFileDialog::getSaveFileName(QApplication::activeWindow(), QObject::tr("Save As"), _filePath, filterForScripts); if (saveToFileText(filepath, _script)) { _filePath = filepath; return true; } return false; } bool ScriptInfo::saveToFile() { return _filePath.isEmpty() ? saveToFileAs() : saveToFileText(_filePath, _script); } } ================================================ FILE: src/robomongo/core/domain/ScriptInfo.h ================================================ #pragma once #include #include "robomongo/core/domain/CursorPosition.h" namespace Robomongo { class ScriptInfo { public: ScriptInfo(const QString &script, bool execute = false, const std::string &dbname = "", const CursorPosition &position = CursorPosition(), const QString &title = QString(), const QString &filePath = QString()); bool execute() const { return _execute; } void setExecutable(bool execute) { _execute = execute; } QString script() const { return _script; } std::string dbname() const { return _dbname; } const QString &title() const { return _title; } const CursorPosition &cursor() const { return _cursor; } void setScript(const QString &script) { _script = script; } QString filePath() const { return _filePath; } bool loadFromFile(const QString &filePath); bool loadFromFile(); bool saveToFileAs(); bool saveToFile(); private: QString _script; std::string _dbname; bool _execute; const QString _title; const CursorPosition _cursor; QString _filePath; }; } ================================================ FILE: src/robomongo/core/engine/ScriptEngine.cpp ================================================ #include "robomongo/core/engine/ScriptEngine.h" #include // unable to put this include below. doesn't compile on GCC 4.7.2 and Qt 4.8 #include #include #include #include #include #include // v0.9 //#include //#include //#include //#include #include #include #include // v0.9 //#include #include #include #include #include #include #include "robomongo/core/events/MongoEvents.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/CredentialSettings.h" #include "robomongo/core/domain/MongoDocument.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/utils/QtUtils.h" namespace { std::vector split(const std::string &s, char seperator) { std::vector output; std::string::size_type prev_pos = 0, pos = 0; while ((pos = s.find(seperator, pos)) != std::string::npos) { std::string substring(s.substr(prev_pos, pos-prev_pos)); output.push_back(substring); prev_pos = ++pos; } output.push_back(s.substr(prev_pos, pos-prev_pos)); // Last word return output; } } namespace mongo { extern bool isShell; void logProcessDetailsForLogRotate() {} } namespace Robomongo { ScriptEngine::ScriptEngine(ConnectionSettings *connection, int timeoutSec) : _connection(connection), _scope(nullptr), _engine(NULL), _timeoutSec(timeoutSec), _initialized(false), _mutex(QMutex::Recursive) { } ScriptEngine::~ScriptEngine() { } void ScriptEngine::init(bool isLoadMongoRcJs, const std::string& serverAddr, const std::string& dbName) { QMutexLocker lock(&_mutex); std::string connectDatabase = dbName.empty() ? "test" : dbName; if (_connection->hasEnabledPrimaryCredential()) connectDatabase = _connection->primaryCredential()->databaseName(); std::stringstream ss; auto hostAndPort = serverAddr.empty() ? _connection->hostAndPort().toString() : serverAddr; ss << "db = connect('" << hostAndPort << "/" << connectDatabase; // v0.9 // ss << "db = connect('" << _connection->serverHost() << ":" << _connection->serverPort() << _connection->sslInfo() << _connection->sshInfo() << "/" << connectDatabase; if (!_connection->hasEnabledPrimaryCredential()) ss << "')"; else ss << "', '" << _connection->primaryCredential()->userName() << "', '" << _connection->primaryCredential()->userPassword() << "')"; { mongo::shell_utils::dbConnect = ss.str(); // v0.9 // mongo::isShell = true; mongo::ScriptEngine::setConnectCallback( mongo::shell_utils::onConnect ); mongo::ScriptEngine::setup(); mongo::getGlobalScriptEngine()->setScopeInitCallback(mongo::shell_utils::initScope); mongo::getGlobalScriptEngine()->enableJIT(true); _scope.reset(mongo::getGlobalScriptEngine()->newScope()); _engine = mongo::getGlobalScriptEngine(); // Load '.mongorc.js' from user's home directory if (isLoadMongoRcJs) { QString mongorcPath = QString("%1/.mongorc.js").arg(QDir::homePath()); if (QFile::exists(mongorcPath)) { _scope->execFile(QtUtils::toStdString(mongorcPath), false, false); } } // Load '.robomongorc.js' QString robomongorcPath = QString("%1/.robomongorc.js").arg(QDir::homePath()); if (QFile::exists(robomongorcPath)) { _scope->execFile(QtUtils::toStdString(robomongorcPath), false, false); } _failedScope = false; } // Esprima ECMAScript parser: http://esprima.org/ std::string esprima = loadFile(":/robomongo/scripts/esprima.js", true); _scope->exec(esprima, "(esprima)", false, true, true); // UUID helpers std::string uuidhelpers = loadFile(":/robomongo/scripts/uuidhelpers.js", true); _scope->exec(uuidhelpers, "(uuidhelpers)", false, true, true); // Enable verbose shell reporting _scope->exec("_verboseShell = true;", "(verboseShell)", false, false, false); // Save original autocomplete function so it can be restored if overwritten by user preference _scope->exec("DB.autocompleteOriginal = DB.autocomplete;", "(saveOriginalAutocomplete)", false, false, false); // Cache result of original "DB.autocomplete" // Cache invalidated by the invalidateDbCollectionsCache() method. std::string const cacheAutocompletion = "__robomongoAutocompletionCache = null;" "DB.autocompleteCached = function(obj) { " " if (__robomongoAutocompletionCache == null) {" " __robomongoAutocompletionCache = DB.autocompleteOriginal(obj);" " }" " return __robomongoAutocompletionCache;" "}"; _scope->exec(cacheAutocompletion, "", false, false, false); // Capture aggregate parameters: pipeline, options std::string const aggregateInterceptor = "__robomongoAggregateUsed = false;" "__robomongoAggregate = DBCollection.prototype.aggregate;" "__robomongoAggregatePipeline = null;" "__robomongoAggregateOptions = null;" "DBCollection.prototype.aggregate = function(pipeline, options) { " " __robomongoAggregateUsed = true;" " __robomongoAggregatePipeline = pipeline;" " __robomongoAggregateOptions = options;" " return __robomongoAggregate.call(this, pipeline, options);" "}"; _scope->exec(aggregateInterceptor, "", false, false, false); _initialized = true; } MongoShellExecResult ScriptEngine::exec(const std::string &originalScript, const std::string &dbName, AggrInfo aggrInfo /* = AggrInfo() */) { QMutexLocker lock(&_mutex); if (!_scope) { _failedScope = true; return MongoShellExecResult(true, "Connection error. Uninitialized mongo scope."); } // robomongo shell timeout bool timeoutReached = false; /* * Replace all commands ('show dbs', 'use db' etc.) with call * to shellHelper('show', 'dbs') and so on. */ std::string stdstr(originalScript); pcrecpp::RE re("^(show|use|set) (\\w+)$", pcrecpp::RE_Options(PCRE_CASELESS|PCRE_MULTILINE|PCRE_NEWLINE_ANYCRLF)); re.GlobalReplace("shellHelper('\\1', '\\2');", &stdstr); /* * Statementize (i.e. extract all JavaScript statements from script) and * execute each statement one by one */ std::vector statements; std::string error; bool result = statementize(stdstr, statements, error); if (!result && statements.size() == 0) statements.push_back("print(__robomongoResult.error)"); std::vector results; use(dbName); for (auto const& statement : statements) { // clear global objects __objects.clear(); __type = ""; __finished = false; __logs.str(""); if (true /* ! wascmd */) { try { bool failed = false; QElapsedTimer timer; timer.start(); if ( _scope->exec( statement , "(shell)" , false , true , false, _timeoutSec * 1000) ) { _scope->exec( "__robomongoLastRes = __lastres__; shellPrintHelper( __lastres__ );", "(shell2)" , true , true , false, _timeoutSec * 1000); } else // failed to run script failed = true; qint64 elapsed = timer.elapsed(); // milliseconds if (elapsed > _timeoutSec * 1000) timeoutReached = true; std::string logs = __logs.str(); std::string answer = logs.c_str(); std::string type = __type.c_str(); if (failed && !timeoutReached) return MongoShellExecResult(true, answer); std::vector docs = MongoDocument::fromBsonObj(__objects); if (!answer.empty() || docs.size() > 0) results.push_back( prepareResult(type, answer, docs, elapsed, statement, aggrInfo) ); } catch (const std::exception &e) { std::cout << "error:" << e.what() << std::endl; } } } return prepareExecResult(results, timeoutReached); } void ScriptEngine::interrupt() { // This operation crash Robomongo // static_cast(_scope)->kill(); // v0.9 //mongo::Scope::_interruptFlag = true; } void ScriptEngine::use(const std::string &dbName) { QMutexLocker lock(&_mutex); if (!dbName.empty()) { std::stringstream ss; // Switch to database ss << "shellHelper.use('" << dbName << "');" << std::endl; // Always allow to read from slave ss << "rs.slaveOk();" << std::endl; _scope->exec(ss.str(), "(usedb)", false, true, false); } } void ScriptEngine::setBatchSize(int batchSize) { QMutexLocker lock(&_mutex); char buff[64] = {0}; sprintf(buff, "DBQuery.shellBatchSize = %d", batchSize); _scope->exec(buff, "(shellBatchSize)", false, true, true); } void ScriptEngine::ping() { if (!_scope) return; QMutexLocker lock(&_mutex); _scope->exec("if (db) { db.runCommand({ping:1}); }", "(ping)", false, false, false, 3000); } QStringList ScriptEngine::complete(const std::string &prefix, const AutocompletionMode mode) { //if ( prefix.find( '"' ) != string::npos ) // return; try { if (mode == AutocompleteAll) _scope->exec("DB.autocomplete = DB.autocompleteCached;", "", false, false, false); else if (mode == AutocompleteNoCollectionNames) _scope->exec("DB.autocomplete = function(obj){return [];}", "", false, false, false); QStringList results; mongo::BSONObj args = BSON( "0" << prefix ); _scope->invokeSafe( "function callShellAutocomplete(x) {shellAutocomplete(x)}", &args, 0, 1000 ); mongo::BSONObjBuilder b; _scope->append( b , "" , "__autocomplete__" ); mongo::BSONObj res = b.obj(); mongo::BSONObj arr = res.firstElement().Obj(); mongo::BSONObjIterator i( arr ); while ( i.more() ) { mongo::BSONElement e = i.next(); results.append(QtUtils::toQString(e.String())); } return results; } catch ( ... ) { return QStringList(); } return QStringList(); } MongoShellResult ScriptEngine::prepareResult(const std::string &type, const std::string &output, const std::vector &objects, qint64 elapsedms, const std::string &statement, AggrInfo aggrInfo /*= AggrInfo()*/) { const char *script = "__robomongoQuery = false; \n" "__robomongoIsAggregate = false; \n" "__robomongoDbName = '[invalid database]'; \n" "__robomongoServerAddress = '[invalid connection]'; \n" "__robomongoCollectionName = '[invalid collection]'; \n" "if (typeof __robomongoLastRes == 'object' && __robomongoLastRes != null \n" " && __robomongoLastRes instanceof DBQuery) { \n" " __robomongoQuery = true; \n" " __robomongoDbName = __robomongoLastRes._db.getName();\n " " __robomongoServerAddress = __robomongoLastRes._mongo.host; \n" " __robomongoCollectionName = __robomongoLastRes._collection._shortName; \n" " __robomongoQuery = __robomongoLastRes._query; \n" " __robomongoFields = __robomongoLastRes._fields; \n" " __robomongoLimit = __robomongoLastRes._limit; \n" " __robomongoSkip = __robomongoLastRes._skip; \n" " __robomongoBatchSize = __robomongoLastRes._batchSize; \n" " __robomongoOptions = __robomongoLastRes._options; \n" " __robomongoSpecial = __robomongoLastRes._special; \n" "} \n" "else if (typeof __robomongoLastRes == 'object' && __robomongoLastRes != null \n" " && __robomongoLastRes instanceof DBCommandCursor \n" " && __robomongoAggregateUsed) { \n" " __robomongoAggregateUsed = false; \n" " __robomongoIsAggregate = true; \n" " __robomongoDbName = __robomongoLastRes._db.getName();\n " " __robomongoServerAddress = __robomongoLastRes._db._mongo.host; \n" " __robomongoCollectionName = __robomongoLastRes._collName; \n" "} \n" ; _scope->exec(script, "(getresultinfo)", false, false, false); bool const isQuery = _scope->getBoolean("__robomongoQuery"); bool const isAggregate = _scope->getBoolean("__robomongoIsAggregate"); if (isQuery) { std::string serverAddress = getString("__robomongoServerAddress"); std::string dbName = getString("__robomongoDbName"); std::string collectionName = getString("__robomongoCollectionName"); mongo::BSONObj query = _scope->getObject("__robomongoQuery"); mongo::BSONObj fields = _scope->getObject("__robomongoFields"); int limit = _scope->getNumberInt("__robomongoLimit"); int skip = _scope->getNumberInt("__robomongoSkip"); int batchSize = _scope->getNumberInt("__robomongoBatchSize"); int options = _scope->getNumberInt("__robomongoOptions"); bool special = _scope->getBoolean("__robomongoSpecial"); MongoQueryInfo const info{ CollectionInfo(serverAddress, dbName, collectionName), query, fields, limit, skip, batchSize, options, special }; return MongoShellResult(type, output, objects, info, statement, elapsedms); } else if (isAggregate) { std::string const serverAddress = getString("__robomongoServerAddress"); std::string const dbName = getString("__robomongoDbName"); std::string const collectionName = getString("__robomongoCollectionName"); mongo::BSONObj const pipeline = _scope->getObject("__robomongoAggregatePipeline"); mongo::BSONObj const options = _scope->getObject("__robomongoAggregateOptions"); // This query can be paging of an original aggr. query, we store the original/unpaged // pipeline object here. mongo::BSONObj const origPipeline = aggrInfo.isValid ? aggrInfo.pipeline : pipeline; int const skip = aggrInfo.isValid ? aggrInfo.skip : 0; int const batchSize = aggrInfo.isValid ? aggrInfo.batchSize : 50; int const resultIndex = aggrInfo.isValid ? aggrInfo.resultIndex : -1; AggrInfo const newAggrInfo { collectionName, skip, batchSize, origPipeline, options, resultIndex }; return MongoShellResult(type, output, objects, MongoQueryInfo(), statement, elapsedms, newAggrInfo); } return MongoShellResult(type, output, objects, MongoQueryInfo(), statement, elapsedms); } MongoShellExecResult ScriptEngine::prepareExecResult(const std::vector &results, bool timeoutReached /* = false */) { const char *script = "__robomongoServerAddress = '[invalid connection]'; \n" "__robomongoServerIsValid = false; \n" "__robomongoDbName = '[invalid database]'; \n" "__robomongoDbIsValid = false; \n" "if (typeof db == 'object' && db != null && db instanceof DB) { \n" " __robomongoServerAddress = db.getMongo().host; \n" " __robomongoServerIsValid = true; \n" " __robomongoDbName = db.getName();\n " " __robomongoDbIsValid = true; \n " "} \n"; _scope->exec(script, "(getdbname)", false, false, false); std::string serverName = getString("__robomongoServerAddress"); bool serverIsValid = _scope->getBoolean("__robomongoServerIsValid"); std::string dbName = getString("__robomongoDbName"); bool dbIsValid = _scope->getBoolean("__robomongoDbIsValid"); return MongoShellExecResult(results, serverName, serverIsValid, dbName, dbIsValid, timeoutReached); } std::string ScriptEngine::getString(const char *fieldName) { return _scope->getString(fieldName); } bool ScriptEngine::statementize( const std::string &script, std::vector &outVec, std::string &outError) { _scope->setString("__robomongoEsprima", script.c_str()); mongo::StringData const data { "var __robomongoResult = {};" "try {" "__robomongoResult.result = esprima.parse(__robomongoEsprima, { range: true, loc : true });" "} catch(e) {" "__robomongoResult.error = e.name + ': ' + e.message;" "}" "__robomongoResult;" }; if(!_scope->exec(data, "(esprima2)", false, true, false)) { sendLog(this, LogEvent::RBM_ERROR, "ScriptEngine: Scope failed. Resetting scope."); _scope->reset(); sendLog(this, LogEvent::RBM_INFO, "ScriptEngine: Scope reset complete."); _scope->exec(data, "(esprima2)", false, true, false); } mongo::BSONObj const obj = _scope->getObject("__lastres__"); if (obj.hasField("error")) { outError = obj.getField("error"); return false; } for (auto const& bsonElem : obj.getField("result").Obj().getField("body").Array()) { mongo::BSONObj const item = bsonElem.Obj(); std::vector const range = item.getField("range").Array(); auto const from = static_cast(range.at(0).number()); auto const till = static_cast(range.at(1).number()); QString const qScript = QtUtils::toQString(script); std::string statement = qScript.mid(from, till - from).toStdString(); outVec.push_back(statement); } return true; } void ScriptEngine::invalidateDbCollectionsCache() { if (!_initialized) return; _scope->exec("__robomongoAutocompletionCache = null;", "", false, false, false); } std::string ScriptEngine::loadFile(const QString &path, bool throwOnError) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { if (throwOnError) throw std::runtime_error("Unable to load file"); return ""; } QTextStream in(&file); QString content = in.readAll(); return QtUtils::toStdString(content); } } ================================================ FILE: src/robomongo/core/engine/ScriptEngine.h ================================================ #pragma once #include #include #include //#include #include "robomongo/core/domain/MongoShellResult.h" #include "robomongo/core/Enums.h" namespace Robomongo { class ConnectionSettings; class ScriptEngine : public QObject { Q_OBJECT public: ScriptEngine(ConnectionSettings *connection, int timeoutSec); ~ScriptEngine(); void init(bool isLoadMongoJs, const std::string& serverAddr = "", const std::string& dbName = ""); MongoShellExecResult exec(const std::string &script, const std::string &dbName = std::string(), AggrInfo aggrInfo = AggrInfo()); void interrupt(); void use(const std::string &dbName); void setBatchSize(int batchSize); void ping(); QStringList complete(const std::string &prefix, const AutocompletionMode mode); void invalidateDbCollectionsCache(); bool failedScope() const { return _failedScope; } void changeTimeout(int newTimeout) { _timeoutSec = newTimeout; } private: ConnectionSettings *_connection; MongoShellResult prepareResult(const std::string &type, const std::string &output, const std::vector &objects, qint64 elapsedms, const std::string &statement, AggrInfo aggrInfo = AggrInfo()); MongoShellExecResult prepareExecResult( const std::vector &results, bool timeoutReached = false); std::string loadFile(const QString &path, bool throwOnError); std::string getString(const char *fieldName); bool statementize( const std::string &script, std::vector &outVec, std::string &outError); int _timeoutSec; mongo::ScriptEngine *_engine; std::unique_ptr _scope; // MozJSProxyScope bool _failedScope = false; QMutex _mutex; bool _initialized; }; } ================================================ FILE: src/robomongo/core/events/MongoEvents.cpp ================================================ #include "robomongo/core/events/MongoEvents.h" #include "robomongo/core/Core.h" namespace Robomongo { R_REGISTER_EVENT(EstablishConnectionRequest) R_REGISTER_EVENT(EstablishConnectionResponse) R_REGISTER_EVENT(ReplicaSetRefreshed) R_REGISTER_EVENT(RefreshReplicaSetFolderRequest) R_REGISTER_EVENT(RefreshReplicaSetFolderResponse) R_REGISTER_EVENT(ReplicaSetFolderLoading) R_REGISTER_EVENT(ReplicaSetFolderRefreshed) R_REGISTER_EVENT(LoadDatabaseNamesRequest) R_REGISTER_EVENT(LoadDatabaseNamesResponse) R_REGISTER_EVENT(LoadCollectionNamesRequest) R_REGISTER_EVENT(LoadCollectionNamesResponse) R_REGISTER_EVENT(LoadUsersRequest) R_REGISTER_EVENT(LoadCollectionIndexesRequest) R_REGISTER_EVENT(LoadCollectionIndexesResponse) R_REGISTER_EVENT(AddEditIndexRequest) R_REGISTER_EVENT(AddEditIndexResponse) R_REGISTER_EVENT(DropCollectionIndexRequest) R_REGISTER_EVENT(DropCollectionIndexResponse) R_REGISTER_EVENT(LoadUsersResponse) R_REGISTER_EVENT(LoadFunctionsRequest) R_REGISTER_EVENT(LoadFunctionsResponse) R_REGISTER_EVENT(ConnectingEvent) R_REGISTER_EVENT(ConnectionFailedEvent) R_REGISTER_EVENT(ConnectionEstablishedEvent) R_REGISTER_EVENT(DatabaseListLoadedEvent) R_REGISTER_EVENT(OpeningShellEvent) R_REGISTER_EVENT(ExecuteQueryRequest) R_REGISTER_EVENT(ExecuteQueryResponse) R_REGISTER_EVENT(DocumentListLoadedEvent) R_REGISTER_EVENT(ExecuteScriptRequest) R_REGISTER_EVENT(ExecuteScriptResponse) R_REGISTER_EVENT(AutocompleteRequest) R_REGISTER_EVENT(AutocompleteResponse) R_REGISTER_EVENT(ScriptExecutedEvent) R_REGISTER_EVENT(ScriptExecutingEvent) R_REGISTER_EVENT(InsertDocumentRequest) R_REGISTER_EVENT(InsertDocumentResponse) R_REGISTER_EVENT(RemoveDocumentRequest) R_REGISTER_EVENT(RemoveDocumentResponse) R_REGISTER_EVENT(CreateDatabaseRequest) R_REGISTER_EVENT(CreateDatabaseResponse) R_REGISTER_EVENT(DropDatabaseRequest) R_REGISTER_EVENT(DropDatabaseResponse) R_REGISTER_EVENT(CreateCollectionRequest) R_REGISTER_EVENT(CreateCollectionResponse) R_REGISTER_EVENT(DropCollectionRequest) R_REGISTER_EVENT(DropCollectionResponse) R_REGISTER_EVENT(RenameCollectionRequest) R_REGISTER_EVENT(RenameCollectionResponse) R_REGISTER_EVENT(DuplicateCollectionRequest) R_REGISTER_EVENT(DuplicateCollectionResponse) R_REGISTER_EVENT(CopyCollectionToDiffServerRequest) R_REGISTER_EVENT(CopyCollectionToDiffServerResponse) R_REGISTER_EVENT(CreateUserRequest) R_REGISTER_EVENT(CreateUserResponse) R_REGISTER_EVENT(DropUserRequest) R_REGISTER_EVENT(DropUserResponse) R_REGISTER_EVENT(CreateFunctionRequest) R_REGISTER_EVENT(CreateFunctionResponse) R_REGISTER_EVENT(DropFunctionRequest) R_REGISTER_EVENT(DropFunctionResponse) R_REGISTER_EVENT(QueryWidgetUpdatedEvent) R_REGISTER_EVENT(EstablishSshConnectionRequest) R_REGISTER_EVENT(EstablishSshConnectionResponse) R_REGISTER_EVENT(ListenSshConnectionRequest) R_REGISTER_EVENT(ListenSshConnectionResponse) R_REGISTER_EVENT(LogEvent) R_REGISTER_EVENT(StopScriptRequest) R_REGISTER_EVENT(OperationFailedEvent) } ================================================ FILE: src/robomongo/core/events/MongoEvents.h ================================================ #pragma once #include #include #include #include #include #include "robomongo/core/domain/MongoShellResult.h" #include "robomongo/core/domain/CursorPosition.h" #include "robomongo/core/domain/MongoUser.h" #include "robomongo/core/domain/MongoFunction.h" #include "robomongo/core/events/MongoEventsInfo.h" #include "robomongo/core/domain/MongoAggregateInfo.h" #include "robomongo/core/Event.h" #include "robomongo/core/Enums.h" #include "robomongo/core/mongodb/ReplicaSet.h" namespace Robomongo { class MongoServer; class MongoShell; class MongoDatabase; class MongoWorker; class ConnectionSettings; class SshTunnelWorker; /** * @brief EstablishConnection */ struct EstablishConnectionRequest : public Event { R_EVENT EstablishConnectionRequest(QObject *sender, ConnectionType connectionType, std::string const& uuid) : Event(sender), connectionType(connectionType), uuid(uuid) {} ConnectionType const connectionType; std::string const uuid; }; struct EstablishConnectionResponse : public Event { R_EVENT enum ErrorReason { NoError = 0, MongoConnection = 1, MongoAuth = 2, MongoSslConnection = 3 }; EstablishConnectionResponse(QObject *sender, const ConnectionInfo &info, ConnectionType connectionType, const ReplicaSet& replicaSet) : Event(sender), info(info), connectionType(connectionType), replicaSet(replicaSet) {} EstablishConnectionResponse(QObject *sender, const EventError &error, ConnectionType connectionType, ConnectionInfo const& info, const ReplicaSet& replicaSet, ErrorReason errorReason) : Event(sender, error), info(info), connectionType(connectionType), errorReason(errorReason), replicaSet(replicaSet) {} ConnectionInfo const info; ConnectionType const connectionType; ErrorReason const errorReason = NoError; ReplicaSet const replicaSet; }; struct ReplicaSetRefreshed : public Event { R_EVENT ReplicaSetRefreshed(QObject *sender) : Event(sender) {} ReplicaSetRefreshed(QObject *sender, const EventError &error, ReplicaSet const& replicaSet) : Event(sender, error), replicaSet(replicaSet) {} ReplicaSet const replicaSet; }; struct RefreshReplicaSetFolderRequest : public Event { R_EVENT RefreshReplicaSetFolderRequest(QObject *sender, bool expanded) : Event(sender), expanded(expanded) {} bool const expanded = false; }; struct RefreshReplicaSetFolderResponse : public Event { R_EVENT // Primary is reachable RefreshReplicaSetFolderResponse(QObject *sender, ReplicaSet const& replicaSet, bool expanded) : Event(sender), replicaSet(replicaSet), expanded(expanded) {} // Primary is unreachable, secondary(ies) might be reachable RefreshReplicaSetFolderResponse(QObject *sender, ReplicaSet const& replicaSet, bool expanded, const EventError &error) : Event(sender, error), replicaSet(replicaSet), expanded(expanded) {} ReplicaSet const replicaSet; bool const expanded = false; }; struct ReplicaSetFolderLoading : public Event { R_EVENT ReplicaSetFolderLoading(QObject *sender) : Event(sender) {} }; struct ReplicaSetFolderRefreshed : public Event { R_EVENT ReplicaSetFolderRefreshed(QObject *sender, bool expanded) : Event(sender), expanded(expanded) {} ReplicaSetFolderRefreshed(QObject *sender, const EventError &error, bool expanded) : Event(sender, error), expanded(expanded) {} ReplicaSetFolderRefreshed(QObject *sender, const EventError &error, ReplicaSet const& replicaSet, bool expanded) : Event(sender, error), replicaSet(replicaSet), expanded(expanded) {} ReplicaSet const replicaSet; bool const expanded = false; }; /** * @brief LoadDatabaseNames */ class LoadDatabaseNamesRequest : public Event { R_EVENT LoadDatabaseNamesRequest(QObject *sender) : Event(sender) {} }; class LoadDatabaseNamesResponse : public Event { R_EVENT LoadDatabaseNamesResponse(QObject *sender, const std::vector &databaseNames) : Event(sender), databaseNames(databaseNames) {} LoadDatabaseNamesResponse(QObject *sender, const EventError &error) : Event(sender, error) {} std::vector databaseNames; }; /** * @brief LoadCollectionNames */ class LoadCollectionNamesRequest : public Event { R_EVENT public: LoadCollectionNamesRequest(QObject *sender, const std::string &databaseName) : Event(sender), _databaseName(databaseName) {} std::string databaseName() const { return _databaseName; } private: std::string _databaseName; }; class LoadCollectionNamesResponse : public Event { R_EVENT public: LoadCollectionNamesResponse(QObject *sender, const std::string &databaseName, const std::vector &collectionInfos) : Event(sender), _databaseName(databaseName), _collectionInfos(collectionInfos) { } LoadCollectionNamesResponse(QObject *sender, const EventError &error) : Event(sender, error) {} std::string databaseName() const { return _databaseName; } std::vector collectionInfos() const { return _collectionInfos; } private: std::string _databaseName; std::vector _collectionInfos; }; class LoadCollectionIndexesRequest : public Event { R_EVENT public: LoadCollectionIndexesRequest(QObject *sender, const MongoCollectionInfo &collection) : Event(sender), _collection(collection) {} MongoCollectionInfo collection() const { return _collection; } private: const MongoCollectionInfo _collection; }; class LoadCollectionIndexesResponse : public Event { R_EVENT public: LoadCollectionIndexesResponse(QObject *sender, const std::vector &indexes) : Event(sender), _indexes(indexes) {} LoadCollectionIndexesResponse(QObject *sender, const EventError &error) : Event(sender, error) {} std::vector indexes() const { return _indexes; } private: std::vector _indexes; }; class AddEditIndexRequest : public Event { R_EVENT AddEditIndexRequest(QObject *sender, const IndexInfo &oldInfo, const IndexInfo &newInfo) : Robomongo::Event(sender), oldInfo_(oldInfo), newInfo_(newInfo) {} const IndexInfo &oldInfo() const { return oldInfo_; } const IndexInfo &newInfo() const { return newInfo_; } private: const IndexInfo oldInfo_; const IndexInfo newInfo_; }; struct AddEditIndexResponse : public Event { R_EVENT AddEditIndexResponse(QObject *sender, const IndexInfo &oldIndex, const IndexInfo &newIndex) : Event(sender), oldIndex_(oldIndex), newIndex_(newIndex) {} AddEditIndexResponse(QObject *sender, const EventError &error, const IndexInfo &oldIndex, const IndexInfo &newIndex) : Event(sender, error), oldIndex_(oldIndex), newIndex_(newIndex) {} const IndexInfo oldIndex_; const IndexInfo newIndex_; }; class DropCollectionIndexRequest : public Event { R_EVENT public: DropCollectionIndexRequest(QObject *sender, const MongoCollectionInfo &collection, const std::string &index) : Event(sender), _collection(collection), _index(index) {} MongoCollectionInfo collection() const { return _collection; } std::string index() const { return _index; } private: const MongoCollectionInfo _collection; std::string _index; // todo: rename to index }; class DropCollectionIndexResponse: public Event { R_EVENT public: DropCollectionIndexResponse(QObject *sender, const MongoCollectionInfo &collection, const std::string &index) : Event(sender), _collection(collection), _index(index) {} DropCollectionIndexResponse(QObject *sender, const EventError &error, const std::string &index) : Event(sender, error), _index(index) {} MongoCollectionInfo collection() const { return _collection; } std::string index() const { return _index; } private: MongoCollectionInfo _collection; std::string _index; }; /** * @brief Load Users */ class LoadUsersRequest : public Event { R_EVENT public: LoadUsersRequest(QObject *sender, const std::string &databaseName) : Event(sender), _databaseName(databaseName) {} std::string databaseName() const { return _databaseName; } private: std::string _databaseName; }; class LoadUsersResponse : public Event { R_EVENT public: LoadUsersResponse(QObject *sender, const std::string &databaseName, const std::vector &users) : Event(sender), _databaseName(databaseName), _users(users) { } LoadUsersResponse(QObject *sender, const EventError &error) : Event(sender, error) {} std::string databaseName() const { return _databaseName; } std::vector users() const { return _users; } private: std::string _databaseName; std::vector _users; }; /** * @brief Load Functions */ class LoadFunctionsRequest : public Event { R_EVENT public: LoadFunctionsRequest(QObject *sender, const std::string &databaseName) : Event(sender), _databaseName(databaseName) {} std::string databaseName() const { return _databaseName; } private: std::string _databaseName; }; class LoadFunctionsResponse : public Event { R_EVENT public: LoadFunctionsResponse(QObject *sender, const std::string &databaseName, const std::vector &functions) : Event(sender), _databaseName(databaseName), _functions(functions) { } LoadFunctionsResponse(QObject *sender, const EventError &error) : Event(sender, error) {} std::string databaseName() const { return _databaseName; } std::vector functions() const { return _functions; } private: std::string _databaseName; std::vector _functions; }; /** * @brief InsertDocument */ class InsertDocumentRequest : public Event { R_EVENT public: InsertDocumentRequest(QObject *sender, const mongo::BSONObj &obj, const MongoNamespace &ns, bool overwrite = false) : Event(sender), _obj(obj), _ns(ns), _overwrite(overwrite) {} mongo::BSONObj obj() const { return _obj; } MongoNamespace ns() const { return _ns; } bool overwrite() const { return _overwrite; } private: mongo::BSONObj _obj; const MongoNamespace _ns; bool _overwrite; }; class InsertDocumentResponse : public Event { R_EVENT public: InsertDocumentResponse(QObject *sender) : Event(sender) {} InsertDocumentResponse(QObject *sender, EventError const& error) : Event(sender, error) {} }; /** * @brief Remove Document */ enum class RemoveDocumentCount { ONE, MULTI, ALL }; class RemoveDocumentRequest : public Event { R_EVENT public: RemoveDocumentRequest(QObject *sender, mongo::Query query, const MongoNamespace &ns, RemoveDocumentCount removeCount, int index) : Event(sender), _query(query), _ns(ns), _removeCount(removeCount), _index(index) {} mongo::Query query() const { return _query; } MongoNamespace ns() const { return _ns; } RemoveDocumentCount removeCount() const { return _removeCount; } int index() const { return _index; } private: mongo::Query const _query; MongoNamespace const _ns; RemoveDocumentCount const _removeCount; // if this is a multi remove, this is the index of current document deleted. 0 for first document. int const _index; }; struct RemoveDocumentResponse : public Event { R_EVENT RemoveDocumentResponse(QObject *sender, RemoveDocumentCount removeCount, int index) : Event(sender), removeCount(removeCount), index(index) {} RemoveDocumentResponse(QObject *sender, const EventError &error, RemoveDocumentCount removeCount, int index) : Event(sender, error), removeCount(removeCount), index(index) {} RemoveDocumentCount const removeCount; int const index; }; /** * @brief Create Database */ class CreateDatabaseRequest : public Event { R_EVENT public: CreateDatabaseRequest(QObject *sender, const std::string &database) : Event(sender), _database(database) {} std::string database() const { return _database; } private: std::string const _database; }; struct CreateDatabaseResponse : public Event { R_EVENT CreateDatabaseResponse(QObject *sender, const std::string &database) : Event(sender), database(database) {} CreateDatabaseResponse(QObject *sender, const std::string &database, const EventError &error) : Event(sender, error), database(database) {} std::string const database; }; /** * @brief Drop Database */ struct DropDatabaseRequest : public Event { R_EVENT DropDatabaseRequest(QObject *sender, const std::string &database) : Event(sender), database(database) {} std::string const database; }; struct DropDatabaseResponse : public Event { R_EVENT public: DropDatabaseResponse(QObject *sender, const std::string &database) : Event(sender), database(database) {} DropDatabaseResponse(QObject *sender, const std::string &database, const EventError &error) : Event(sender, error), database(database) {} std::string const database; }; /** * @brief Create Collection */ class CreateCollectionRequest : public Event { R_EVENT public: CreateCollectionRequest(QObject *sender, const MongoNamespace &ns, const mongo::BSONObj& extraOptions, long long size = 0, bool capped = false, int maxDocNum = 0 ) : Event(sender), _ns(ns), _extraOptions(extraOptions), _size(size), _capped(capped), _maxDocNum(maxDocNum) {} MongoNamespace ns() const { return _ns; } long long getSize() const { return _size; } bool getCapped() const { return _capped; } int getMaxDocNum() const { return _maxDocNum; } const mongo::BSONObj getExtraOptions() const { return _extraOptions; } private: MongoNamespace const _ns; long long const _size; bool const _capped; int const _maxDocNum; mongo::BSONObj const _extraOptions; }; struct CreateCollectionResponse : public Event { R_EVENT CreateCollectionResponse(QObject *sender, std::string const& collection) : Event(sender), collection(collection) {} CreateCollectionResponse(QObject *sender, std::string const& collection, const EventError &error) : Event(sender, error), collection(collection) {} std::string const collection; }; /** * @brief Drop Collection */ class DropCollectionRequest : public Event { R_EVENT public: DropCollectionRequest(QObject *sender, const MongoNamespace &ns) : Event(sender), _ns(ns) {} MongoNamespace ns() const { return _ns; } private: MongoNamespace _ns; }; struct DropCollectionResponse : public Event { R_EVENT public: DropCollectionResponse(QObject *sender, std::string const& collection) : Event(sender), collection(collection) {} DropCollectionResponse(QObject *sender, std::string const& collection, const EventError &error) : Event(sender, error), collection(collection) {} std::string const collection; }; /** * @brief Rename Collection */ class RenameCollectionRequest : public Event { R_EVENT public: RenameCollectionRequest(QObject *sender, const MongoNamespace &ns, const std::string &newCollection) : Event(sender), _ns(ns), _newCollection(newCollection) {} MongoNamespace ns() const { return _ns; } std::string newCollection() const { return _newCollection; } private: MongoNamespace const _ns; std::string const _newCollection; }; struct RenameCollectionResponse : public Event { R_EVENT RenameCollectionResponse(QObject *sender, std::string const& oldCollection, std::string const& newCollection) : Event(sender), oldCollection(oldCollection), newCollection(newCollection) {} RenameCollectionResponse(QObject *sender, const EventError &error) : Event(sender, error) {} std::string const oldCollection; std::string const newCollection; }; /** * @brief Clone Collection */ class DuplicateCollectionRequest : public Event { R_EVENT public: DuplicateCollectionRequest(QObject *sender, const MongoNamespace &ns, const std::string &newCollection) : Event(sender), _ns(ns), _newCollection(newCollection) {} MongoNamespace ns() const { return _ns; } std::string newCollection() const { return _newCollection; } private: MongoNamespace const _ns; std::string const _newCollection; }; struct DuplicateCollectionResponse : public Event { R_EVENT public: DuplicateCollectionResponse(QObject *sender, std::string const& sourceCollection, std::string const& duplicateCollection) : Event(sender), sourceCollection(sourceCollection), duplicateCollection(duplicateCollection) {} DuplicateCollectionResponse(QObject *sender, std::string const& sourceCollection, const EventError &error) : Event(sender, error), sourceCollection(sourceCollection) {} std::string const sourceCollection; std::string const duplicateCollection; }; /** * @brief Copy collection to diffrent server */ class CopyCollectionToDiffServerRequest : public Event { R_EVENT public: CopyCollectionToDiffServerRequest(QObject *sender, MongoWorker *worker, const std::string &databaseFrom, const std::string &collection, const std::string &databaseTo) : Event(sender), _worker(worker), _from(databaseFrom, collection), _to(databaseTo, collection) {} MongoWorker *worker() const { return _worker; } MongoNamespace from() const { return _from; } MongoNamespace to() const { return _to; } private: MongoWorker *_worker; const MongoNamespace _from; const MongoNamespace _to; }; class CopyCollectionToDiffServerResponse : public Event { R_EVENT public: CopyCollectionToDiffServerResponse(QObject *sender) : Event(sender) {} CopyCollectionToDiffServerResponse(QObject *sender, const EventError &error) : Event(sender, error) {} }; /** * @brief Create User */ class CreateUserRequest : public Event { R_EVENT public: CreateUserRequest(QObject *sender, const std::string &database, const MongoUser &user) : Event(sender), _database(database), _user(user) {} std::string database() const { return _database; } MongoUser user() const { return _user; } private: std::string _database; MongoUser _user; }; struct CreateUserResponse : public Event { R_EVENT CreateUserResponse(QObject *sender, std::string const& userName) : Event(sender), userName(userName) {} CreateUserResponse(QObject *sender, std::string const& userName, const EventError &error) : Event(sender, error), userName(userName) {} std::string const userName; }; /** * @brief Drop User */ class DropUserRequest : public Event { R_EVENT public: DropUserRequest(QObject *sender, const std::string &database, std::string const& username) : Event(sender), _database(database), _username(username) {} std::string database() const { return _database; } std::string username() const { return _username; } private: std::string _database; std::string const _username; }; struct DropUserResponse : public Event { R_EVENT DropUserResponse(QObject *sender, std::string const& username) : Event(sender), username(username) {} DropUserResponse(QObject *sender, std::string const& username, const EventError &error) : Event(sender, error), username(username) {} std::string const username; }; /** * @brief Create Function */ class CreateFunctionRequest : public Event { R_EVENT public: CreateFunctionRequest(QObject *sender, const std::string &database, float dbVersion, const MongoFunction &function, const std::string &existingFunctionName = std::string(), bool overwrite = false) : Event(sender), _database(database), _dbVersion(dbVersion), _existingFunctionName(existingFunctionName), _function(function), _overwrite(overwrite) {} std::string database() const { return _database; } float dbVersion() const { return _dbVersion; } std::string existingFunctionName() const { return _existingFunctionName; } MongoFunction function() const { return _function; } bool overwrite() const { return _overwrite; } private: std::string _database; float const _dbVersion = 0.0f; std::string _existingFunctionName; MongoFunction _function; bool _overwrite; }; struct CreateFunctionResponse : public Event { R_EVENT enum Operation { NewFunction, NameUpdate, CodeUpdate }; // todo CreateFunctionResponse(QObject *sender, std::string const& functionName) : Event(sender), functionName(functionName) {} CreateFunctionResponse(QObject *sender, std::string const& functionName, const EventError &error) : Event(sender, error), functionName(functionName) {} std::string const functionName; }; /** * @brief Drop Function */ class DropFunctionRequest : public Event { R_EVENT public: DropFunctionRequest(QObject *sender, const std::string &database, float dbVersion, const std::string &funcName) : Event(sender), _database(database), _dbVersion(dbVersion), _functionName(funcName) {} std::string database() const { return _database; } float dbVersion() const { return _dbVersion; } std::string functionName() const { return _functionName; } private: std::string _database; float const _dbVersion = 0.0f; std::string _functionName; }; struct DropFunctionResponse : public Event { R_EVENT DropFunctionResponse(QObject *sender, const std::string &functionName) : Event(sender), functionName(functionName) {} DropFunctionResponse(QObject *sender, const std::string &functionName, const EventError &error) : Event(sender, error), functionName(functionName) {} std::string const functionName; }; /** * @brief Query Mongodb */ class ExecuteQueryRequest : public Event { R_EVENT public: ExecuteQueryRequest(QObject *sender, int resultIndex, const MongoQueryInfo &queryInfo) : Event(sender), _resultIndex(resultIndex), _queryInfo(queryInfo) {} int resultIndex() const { return _resultIndex; } MongoQueryInfo queryInfo() const { return _queryInfo; } private: int _resultIndex; //external user data; MongoQueryInfo _queryInfo; }; class ExecuteQueryResponse : public Event { R_EVENT ExecuteQueryResponse(QObject *sender, int resultIndex, const MongoQueryInfo &queryInfo, const std::vector &documents) : Event(sender), resultIndex(resultIndex), queryInfo(queryInfo), documents(documents) { } ExecuteQueryResponse(QObject *sender, const EventError &error) : Event(sender, error) {} int resultIndex; MongoQueryInfo queryInfo; std::vector documents; }; class AutocompleteRequest : public Event { R_EVENT AutocompleteRequest(QObject *sender, const std::string &prefix, const AutocompletionMode mode) : Event(sender), prefix(prefix), mode(mode) {} std::string prefix; AutocompletionMode mode; }; class AutocompleteResponse : public Event { R_EVENT AutocompleteResponse(QObject *sender, const QStringList &list, const std::string &prefix) : Event(sender), list(list), prefix(prefix) {} AutocompleteResponse(QObject *sender, const EventError &error) : Event(sender, error) {} QStringList list; std::string prefix; }; /** * @brief ExecuteScript */ class ExecuteScriptRequest : public Event { R_EVENT ExecuteScriptRequest(QObject *sender, const std::string &script, const std::string &dbName, AggrInfo aggrInfo = AggrInfo(), int take = 0, int skip = 0) : Event(sender), script(script), databaseName(dbName), aggrInfo(aggrInfo), take(take), skip(skip) {} std::string script; std::string databaseName; int take; // int skip; AggrInfo const aggrInfo; }; class ExecuteScriptResponse : public Event { R_EVENT ExecuteScriptResponse(QObject *sender, const MongoShellExecResult &result, bool empty, bool timeoutReached = false) : Event(sender), result(result), empty(empty), _timeoutReached(timeoutReached) {} ExecuteScriptResponse(QObject *sender, const EventError &error, bool timeoutReached = false) : Event(sender, error), _timeoutReached(timeoutReached) {} bool timeoutReached() const { return _timeoutReached; } MongoShellExecResult result; bool empty; bool const _timeoutReached = false; }; class ConnectingEvent : public Event { R_EVENT ConnectingEvent(QObject *sender) : Event(sender) { } }; class OpeningShellEvent : public Event { R_EVENT OpeningShellEvent(QObject *sender, MongoShell *shell) : Event(sender), shell(shell) {} MongoShell *shell; }; class ConnectionFailedEvent : public Event { R_EVENT enum Reason { MongoConnection = 1, MongoAuth = 2, SshConnection = 3, SshChannel = 4, SslConnection = 5 }; ConnectionFailedEvent(QObject *sender, int serverHandle, ConnectionType connectionType, const std::string& message, Reason reason) : Event(sender), message(message), reason(reason), serverHandle(serverHandle), connectionType(connectionType) {} std::string message; Reason reason; ConnectionType connectionType; int serverHandle; }; // class ScriptExecute struct ConnectionEstablishedEvent : public Event { R_EVENT ConnectionEstablishedEvent(MongoServer *server, ConnectionType type, ConnectionInfo connInfo) : Event((QObject *)server), server(server), connectionType(type), connInfo(connInfo) { } MongoServer *server; ConnectionType const connectionType; ConnectionInfo const connInfo; }; class DatabaseListLoadedEvent : public Event { R_EVENT DatabaseListLoadedEvent(QObject *sender, const QList &list) : Event(sender), list(list) { } DatabaseListLoadedEvent(QObject *sender, const EventError &error) : Event(sender, error) {} QList list; }; class DocumentListLoadedEvent : public Event { R_EVENT public: DocumentListLoadedEvent(QObject *sender, int resultIndex, const MongoQueryInfo &queryInfo, const std::string &query, const std::vector &docs) : Event(sender), _resultIndex(resultIndex), _queryInfo(queryInfo), _query(query), _documents(docs) { } DocumentListLoadedEvent(QObject *sender, const EventError &error) : Event(sender, error) {} int resultIndex() const { return _resultIndex; } MongoQueryInfo queryInfo() const { return _queryInfo; } std::vector documents() const { return _documents; } std::string query() const { return _query; } private: int _resultIndex; MongoQueryInfo _queryInfo; std::vector _documents; std::string _query; }; class ScriptExecutedEvent : public Event { R_EVENT public: ScriptExecutedEvent(QObject *sender, const MongoShellExecResult &result, bool empty, bool timeoutReached = false) : Event(sender), _result(result), _empty(empty), _timeoutReached(timeoutReached) {} ScriptExecutedEvent(QObject *sender, const EventError &error, bool timeoutReached = false) : Event(sender, error), _timeoutReached(timeoutReached) {} MongoShellExecResult result() const { return _result; } bool empty() const { return _empty; } bool timeoutReached() const { return _timeoutReached; } private: MongoShellExecResult _result; bool _empty; bool const _timeoutReached = false; }; class ScriptExecutingEvent : public Event { R_EVENT public: ScriptExecutingEvent(QObject *sender) : Event(sender) { } }; class OperationFailedEvent : public Event { R_EVENT public: OperationFailedEvent(QObject *sender, const std::string &technicalErrorMessage, const std::string &userFriendlyErrorMessage) : technicalErrorMessage(technicalErrorMessage), userFriendlyErrorMessage(userFriendlyErrorMessage), Event(sender) { } std::string technicalErrorMessage; std::string userFriendlyErrorMessage; }; class QueryWidgetUpdatedEvent : public Event { R_EVENT public: QueryWidgetUpdatedEvent(QObject *sender, int numOfResults) : Event(sender), _numOfResults(numOfResults) { } int numOfResults() const { return _numOfResults; } private: int _numOfResults; }; /** * @brief Establish SSH Connection */ class EstablishSshConnectionRequest : public Event { R_EVENT EstablishSshConnectionRequest(QObject *sender, int serverHandle, SshTunnelWorker* worker, ConnectionSettings* settings, ConnectionType connectionType) : Event(sender), worker(worker), settings(settings), serverHandle(serverHandle), connectionType(connectionType) {} ConnectionSettings* settings; ConnectionType connectionType; SshTunnelWorker* worker; int serverHandle; }; class EstablishSshConnectionResponse : public Event { R_EVENT EstablishSshConnectionResponse(QObject *sender, int serverHandle, SshTunnelWorker* worker, ConnectionSettings* settings, ConnectionType connectionType, int localport) : Event(sender), worker(worker), settings(settings), connectionType(connectionType), serverHandle(serverHandle), localport(localport) {} EstablishSshConnectionResponse(QObject *sender, int serverHandle, const EventError &error, SshTunnelWorker* worker, ConnectionSettings* settings, ConnectionType connectionType) : Event(sender, error), worker(worker), settings(settings), serverHandle(serverHandle), connectionType(connectionType) {} ConnectionSettings* settings; ConnectionType connectionType; SshTunnelWorker* worker; int localport; int serverHandle; }; /** * @brief Start listening on all opened sockets (SSH tunnel) */ class ListenSshConnectionRequest : public Event { R_EVENT ListenSshConnectionRequest(QObject *sender, int serverHandle, ConnectionType connectionType) : Event(sender), serverHandle(serverHandle), connectionType(connectionType) {} ConnectionType connectionType; int serverHandle; }; class ListenSshConnectionResponse : public Event { R_EVENT ListenSshConnectionResponse(QObject *sender, int serverHandle, ConnectionSettings* settings, ConnectionType connectionType) : Event(sender), settings(settings), serverHandle(serverHandle), connectionType(connectionType) {} ListenSshConnectionResponse(QObject *sender, const EventError &error, int serverHandle, ConnectionSettings* settings, ConnectionType connectionType) : Event(sender, error), settings(settings), serverHandle(serverHandle), connectionType(connectionType) {} ConnectionSettings* settings; ConnectionType connectionType; int serverHandle; }; class LogEvent : public Event { R_EVENT enum LogLevel { RBM_ERROR = 1, RBM_WARN = 2, RBM_INFO = 3, RBM_DEBUG = 100 // log as much as possible }; LogEvent(QObject *sender, const std::string& message, LogLevel level, bool const informUser = false) : Event(sender), message(message), level(level), informUser(informUser) {} std::string severity() const { switch (level) { case RBM_ERROR : return "Error"; case RBM_WARN : return "Warning"; case RBM_INFO : return "Info"; case RBM_DEBUG : return "Debug"; default: return "Undefined"; } } QMessageBox::Icon qMessageBoxIcon() const { switch (level) { case RBM_ERROR: return QMessageBox::Icon::Critical; case RBM_WARN: return QMessageBox::Icon::Warning; case RBM_INFO: return QMessageBox::Icon::Information; case RBM_DEBUG: return QMessageBox::Icon::Information; default: return QMessageBox::Icon::Information; } } mongo::logger::LogSeverity mongoLogSeverity() const { switch (level) { case RBM_ERROR: return mongo::logger::LogSeverity::Error(); case RBM_WARN: return mongo::logger::LogSeverity::Warning(); case RBM_INFO: return mongo::logger::LogSeverity::Info(); case RBM_DEBUG: return mongo::logger::LogSeverity::Log(); default: return mongo::logger::LogSeverity::Info(); } } std::string message; LogLevel level; bool const informUser = false; }; class StopScriptRequest : public Event { R_EVENT StopScriptRequest(QObject *sender) : Event(sender) {} }; } ================================================ FILE: src/robomongo/core/events/MongoEventsInfo.cpp ================================================ #include "robomongo/core/events/MongoEventsInfo.h" namespace Robomongo { IndexInfo::IndexInfo( const MongoCollectionInfo &collection, const std::string &name, const std::string &keys, bool isUnique, bool isBackGround, bool isSparce, int expireAfter, const std::string &defaultLanguage, const std::string &languageOverride, const std::string &textWeights) : _name(name), _collection(collection), _keys(keys), _unique(isUnique), _backGround(isBackGround), _sparse(isSparce), _ttl(expireAfter), _defaultLanguage(defaultLanguage), _languageOverride(languageOverride), _textWeights(textWeights) {} ConnectionInfo::ConnectionInfo(std::string const& uuid) : _address(), _databases(), _version(0.0f), _dbVersionStr(), _uuid(uuid) {} ConnectionInfo::ConnectionInfo(const std::string &address, const std::vector &databases, float version, const std::string& dbVersionStr, const std::string& storageEngine, std::string const& uuid) : _address(address), _databases(databases), _version(version), _dbVersionStr(dbVersionStr), _storageEngineType(storageEngine), _uuid(uuid) {} } ================================================ FILE: src/robomongo/core/events/MongoEventsInfo.h ================================================ #pragma once #include #include "robomongo/core/domain/MongoCollectionInfo.h" namespace Robomongo { struct IndexInfo { IndexInfo( const MongoCollectionInfo &collection, const std::string &name = std::string(), const std::string &request = std::string(), bool isUnique = false, bool isBackGround = false, bool isSparce = false, int expireAfter = -1, const std::string &defaultLanguage = std::string(), const std::string &languageOverride = std::string(), const std::string &textWeights = std::string()); MongoCollectionInfo _collection; std::string _name; std::string _keys; bool _unique; bool _backGround; bool _sparse; int _ttl; std::string _defaultLanguage; std::string _languageOverride; std::string _textWeights; }; struct ConnectionInfo { ConnectionInfo(std::string const& uuid); ConnectionInfo(const std::string &address, const std::vector &databases, float version, const std::string& dbVersionStr, const std::string& storageEngine, std::string const& uuid); const std::string _address; const std::vector _databases; const float _version; const std::string _dbVersionStr; const std::string _storageEngineType; std::string const _uuid; }; } ================================================ FILE: src/robomongo/core/mongodb/MongoClient.cpp ================================================ #include "robomongo/core/mongodb/MongoClient.h" #include "mongo/db/namespace_string.h" #include "robomongo/core/domain/MongoDocument.h" #include "robomongo/core/utils/BsonUtils.h" #include "robomongo/shell/bson/json.h" namespace { Robomongo::IndexInfo makeIndexInfoFromBsonObj( const Robomongo::MongoCollectionInfo &collection, const mongo::BSONObj &obj) { using namespace Robomongo::BsonUtils; Robomongo::IndexInfo info(collection); info._name = obj.getStringField("name"); mongo::BSONObj keyObj = obj.getObjectField("key"); if (keyObj.isValid()) info._keys = jsonString(keyObj, mongo::TenGen, 1, Robomongo::DefaultEncoding, Robomongo::Utc); info._unique = obj.getBoolField("unique"); info._backGround = obj.getBoolField("background"); info._sparse = obj.getBoolField("sparse"); info._ttl = obj.getIntField("expireAfterSeconds"); info._defaultLanguage = obj.getStringField("default_language"); info._languageOverride = obj.getStringField("language_override"); mongo::BSONObj weightsObj = obj.getObjectField("weights"); if (weightsObj.isValid()) info._textWeights = jsonString(weightsObj, mongo::TenGen, 1, Robomongo::DefaultEncoding, Robomongo::Utc); return info; } } namespace Robomongo { MongoClient::MongoClient(mongo::DBClientBase *const dbclient) : _dbclient(dbclient) { } std::vector MongoClient::getCollectionNamesWithDbname(const std::string &dbname) const { std::list collList = _dbclient->getCollectionInfos(dbname); std::vector collNames; for (auto const& coll : collList) collNames.push_back(dbname + '.' + coll.getStringField("name")); // todo: verify std::sort(collNames.begin(), collNames.end()); return collNames; } // Warning: // Use string version dbVersionStr(), version number is corrupted after conversion to float // Todo: Remove this function float MongoClient::getVersion() const { float result = 0.0f; mongo::BSONObj resultObj; _dbclient->runCommand("db", BSON("buildInfo" << "1"), resultObj); std::string resultStr = BsonUtils::getField(resultObj, "version"); result = atof(resultStr.c_str()); return result; } std::string MongoClient::dbVersionStr() const { mongo::BSONObj resultObj; _dbclient->runCommand("db", BSON("buildInfo" << "1"), resultObj); std::string const resultStr = BsonUtils::getField(resultObj, "version"); return resultStr; } std::string MongoClient::getStorageEngineType() const { mongo::BSONObj resultObj; _dbclient->runCommand("db", BSON("serverStatus" << "1"), resultObj); return resultObj.getObjectField("storageEngine").getStringField("name"); } std::vector MongoClient::getDatabaseNames() const { std::list const& dbs = _dbclient->getDatabaseNames(); std::vector dbNames = {dbs.begin(), dbs.end()}; std::sort(dbNames.begin(), dbNames.end()); return dbNames; } std::vector MongoClient::getUsers(const std::string &dbName) { mongo::BSONObjBuilder cmd; cmd.append("usersInfo", 1); mongo::BSONObj result; if (!_dbclient->runCommand(dbName, cmd.done(), result)) { std::string errStr = result.getStringField("errmsg"); if (errStr.empty()) errStr = "Failed to get error message."; throw std::runtime_error(errStr); } std::vector users; for (auto const& usr : result.getField("users").Array()) users.push_back(MongoUser(getVersion(), usr.embeddedObject())); return users; } void MongoClient::createUser(const std::string &dbName, const MongoUser &user) { mongo::BSONObjBuilder cmd; cmd.append("createUser", user.name()); cmd.append("pwd", user.password()); mongo::BSONArrayBuilder roles; auto const& rolesStrs = user.roles(); for (auto const& roleStr : rolesStrs) { mongo::BSONObjBuilder role; role.append("role", roleStr).append("db", user.userSource()); roles.append(role.done()); } cmd.appendArray("roles", roles.done()); mongo::BSONObj result; if (!_dbclient->runCommand(dbName, cmd.done(), result)) { std::string errStr = result.getStringField("errmsg"); if (errStr.empty()) errStr = "Failed to get error message."; throw std::runtime_error(errStr); } } void MongoClient::dropUser(const std::string &dbName, const std::string &user) { mongo::BSONObjBuilder cmd; cmd.append("dropUser", user); mongo::BSONObj result; if (!_dbclient->runCommand(dbName, cmd.done(), result)) { std::string errStr = result.getStringField("errmsg"); if (errStr.empty()) errStr = "Failed to get error message."; throw std::runtime_error(errStr); } } std::vector MongoClient::getFunctions(const std::string &dbName) const { std::vector functions; std::unique_ptr cursor( _dbclient->query(mongo::NamespaceString(dbName, "system.js"), mongo::Query().sort("_id"))); // Cursor may be NULL, it means we have connectivity problem if (!cursor) throw std::runtime_error("Network error while attempting to load list of functions."); while (cursor->more()) { mongo::BSONObj bsonObj = cursor->next(); try { MongoFunction func(bsonObj); functions.push_back(func); } catch (const std::exception &) { // skip invalid docs } } return functions; } std::vector MongoClient::getIndexes(const MongoCollectionInfo &collection) const { std::vector result; std::list indexes = _dbclient->getIndexSpecs(collection.ns().toString()); for (std::list::iterator it = indexes.begin(); it != indexes.end(); ++it) { mongo::BSONObj bsonObj = *it; result.push_back(makeIndexInfoFromBsonObj(collection, bsonObj)); } return result; } void MongoClient::addEditIndex(const IndexInfo &oldInfo, const IndexInfo &newInfo) const { bool const editIndex = !oldInfo._name.empty(); // 1.Step: Drop Index (if this is an Edit Index action). // MongoDB docs: To modify an existing index, you need to drop and recreate the index. std::string const ns = newInfo._collection.ns().toString(); if (editIndex) _dbclient->dropIndex(ns, oldInfo._name); // 2.Step: Add/Edit Index auto const createIndexSpec = [](IndexInfo const& indexInfo) { mongo::IndexSpec indexSpec; indexSpec.name(indexInfo._name); indexSpec.addKeys(mongo::Robomongo::fromjson(indexInfo._keys)); mongo::BSONObjBuilder optionsBuilder; auto const addIfTrue = [&](auto const& keyValuePair) { if (keyValuePair.second) optionsBuilder.appendBool(keyValuePair.first, true); }; addIfTrue(std::pair{ "unique", indexInfo._unique }); addIfTrue(std::pair{ "background", indexInfo._backGround }); addIfTrue(std::pair{ "sparse", indexInfo._sparse }); if (!indexInfo._defaultLanguage.empty()) optionsBuilder.append("default_language", indexInfo._defaultLanguage); if (!indexInfo._languageOverride.empty()) optionsBuilder.append("language_override", indexInfo._languageOverride); if (!mongo::Robomongo::fromjson(indexInfo._textWeights).isEmpty()) optionsBuilder.append("weights", mongo::Robomongo::fromjson(indexInfo._textWeights)); if (indexInfo._ttl > 0) optionsBuilder.append("expireAfterSeconds", indexInfo._ttl); indexSpec.addOptions(optionsBuilder.obj()); return indexSpec; }; try { _dbclient->createIndex(ns, createIndexSpec(newInfo)); } catch (std::exception const& /*ex*/) { // Logging of "ex" is done in upper scope if (editIndex) { // If we are here, index that is being edited, must have already been dropped and // creation of new index failed. So, we try to at least recover the dropped (old) index _dbclient->createIndex(ns, createIndexSpec(oldInfo)); } throw; } std::string const errorStr = _dbclient->getLastError(); if (!errorStr.empty()) throw std::runtime_error(errorStr); } void MongoClient::renameIndexFromCollection(const MongoCollectionInfo &collection, const std::string &oldIndexName, const std::string &newIndexName) const { // This is simply an example of how to perform modifications of // BSON objects. Because BSONObj is immutable, you need to create // copy of this object, using BSONObjBuilder and BSONObjIterator. // // But we need to do not just simple renaming of Index name, we // also should allow our users to fully modify Index // (i.e. change name, keys, unique flag, sparse flag etc.) // // This should be done using the same dialog as for "Add Index". MongoNamespace ns(collection.ns().databaseName(), "system.indexes"); std::string systemIndexesNs = ns.toString(); // Building this JSON: { "name" : "oldIndexName" } mongo::BSONObj query(mongo::BSONObjBuilder() .append("name", oldIndexName) .obj()); // Searching for index with "oldIndexName" // with this query: db.system.indexes.find({ name : "oldIndexName"} mongo::BSONObj indexBson = _dbclient->findOne(systemIndexesNs, mongo::Query(query)); if (indexBson.isEmpty()) return; // Here we are building copy of "indexBson" object and // changing "name" field's value from "oldIndexText" to "newIndexText": mongo::BSONObjBuilder builder; mongo::BSONObjIterator i(indexBson); while (i.more()) { mongo::BSONElement element = i.next(); if (mongo::StringData(element.fieldName()).compare("name") == 0) { builder.append("name", newIndexName); continue; } builder.append(element); } std::string collectionNs = collection.ns().toString(); _dbclient->dropIndex(collectionNs, oldIndexName); _dbclient->insert(systemIndexesNs, builder.obj()); } void MongoClient::dropIndexFromCollection(const MongoCollectionInfo &collection, const std::string &indexName) const { _dbclient->dropIndex(collection.ns().toString(), indexName); } void MongoClient::createFunction(const std::string &dbName, const MongoFunction &fun, const std::string &existingFunctionName /* = QString() */) { MongoNamespace ns(dbName, "system.js"); mongo::BSONObj obj = fun.toBson(); if (existingFunctionName.empty()) { // create new function _dbclient->insert(ns.toString(), obj); std::string errorStr = _dbclient->getLastError(); if (!errorStr.empty()) throw std::runtime_error(errorStr/* , 0 */); } else { // this is update std::string name = fun.name(); if (existingFunctionName == name) { // update existing function code mongo::BSONObjBuilder builder; builder.append("_id", name); mongo::BSONObj bsonQuery = builder.obj(); mongo::Query query(bsonQuery); _dbclient->update(ns.toString(), query, obj, true, false); std::string errorStr = _dbclient->getLastError(); if (!errorStr.empty()) throw std::runtime_error(errorStr); } else { // update function name (remove & insert) _dbclient->insert(ns.toString(), obj); std::string errorStr = _dbclient->getLastError(); // if no errors if (errorStr.empty()) { mongo::BSONObjBuilder builder; builder.append("_id", existingFunctionName); mongo::BSONObj bsonQuery = builder.obj(); mongo::Query query(bsonQuery); _dbclient->remove(ns.toString(), query, true); } else { throw std::runtime_error(errorStr); } } } } void MongoClient::dropFunction(const std::string &dbName, const std::string &name) { MongoNamespace ns(dbName, "system.js"); mongo::BSONObjBuilder builder; builder.append("_id", name); mongo::BSONObj bsonQuery = builder.obj(); mongo::Query query(bsonQuery); _dbclient->remove(ns.toString(), query, true); std::string errorStr = _dbclient->getLastError(); if (!errorStr.empty()) throw std::runtime_error(errorStr); } void MongoClient::createDatabase(const std::string &dbName) { /* * Here we are going to insert temp document to ".temp" collection. * This will create database for us. * Finally we are dropping just created temporary collection. */ MongoNamespace ns(dbName, "temp"); // If .temp already exists, stop. if (_dbclient->exists(ns.toString())) throw std::runtime_error(dbName + ".temp already exists."); // Building { _id : "temp" } document mongo::BSONObjBuilder builder; builder.append("_id", "temp"); mongo::BSONObj obj = builder.obj(); // Insert this document _dbclient->insert(ns.toString(), obj); std::string errorStr = _dbclient->getLastError(); if (!errorStr.empty()) throw std::runtime_error(errorStr); // Drop temp collection _dbclient->dropCollection(ns.toString()); } void MongoClient::dropDatabase(const std::string &dbName) { mongo::BSONObj info; if (!_dbclient->dropDatabase(dbName, mongo::WriteConcernOptions(), &info)) { // todo: do we catch errorStr via info - test it?? std::string errStr = info.toString(); if (errStr.empty()) errStr = "Failed to get error message."; throw std::runtime_error(errStr); } } void MongoClient::createCollection(const std::string& ns, long long size, bool capped, int max, const mongo::BSONObj& extraOptions, mongo::BSONObj* info) { verify(!capped || size); mongo::BSONObj o; if (info == 0) info = &o; mongo::BSONObjBuilder b; std::string db = mongo::nsToDatabase(ns); b.append("create", ns.c_str() + db.length() + 1); if (size) { b.append("size", size); } if (capped) { b.append("capped", true); } if (max) { b.append("max", max); } b.appendElements(extraOptions); if (!_dbclient->exists(ns)) { mongo::BSONObj result; if (!_dbclient->runCommand(db.c_str(), b.done(), result)) { std::string errStr = result.getStringField("errmsg"); if (errStr.empty()) errStr = "Failed to get error message."; throw std::runtime_error(errStr); } } else { throw std::runtime_error("Collection with same name already exists."); } } void MongoClient::renameCollection(const MongoNamespace &ns, const std::string &newCollectionName) { MongoNamespace from(ns); MongoNamespace to(ns.databaseName(), newCollectionName); // Building { renameCollection: , to: } mongo::BSONObjBuilder command; // { collStats: "db.collection", scale : 1 } command.append("renameCollection", from.toString()); command.append("to", to.toString()); mongo::BSONObj result; if (!_dbclient->runCommand("admin", command.obj(), result)) { // this command should be run against "admin" db std::string errStr = result.getStringField("errmsg"); if (errStr.empty()) errStr = "Failed to get error message."; throw std::runtime_error(errStr); } } void MongoClient::duplicateCollection(const MongoNamespace &ns, const std::string &newCollectionName) { MongoNamespace const newCollection(ns.databaseName(), newCollectionName); if (!_dbclient->exists(newCollection.toString())) { mongo::BSONObj result; // todo: Issue #1258 : Duplicate Collection should support advanced collection options. // _dbclient->createCollection() should be called with properties of source collection // not with default parameters as below. if (!_dbclient->createCollection(newCollection.toString(), 0, false, 0, &result)) { std::string errStr = result.getStringField("errmsg"); if (errStr.empty()) errStr = "Failed to get error message."; throw std::runtime_error(errStr); } } else { throw std::runtime_error("Collection with same name already exists."); } std::unique_ptr cursor { _dbclient->query(mongo::NamespaceString(ns.databaseName(), ns.collectionName()), mongo::Query()) }; // Cursor may be NULL, it means we have connectivity problem if (!cursor) throw std::runtime_error("Network error while attempting to run query"); while (cursor->more()) { mongo::BSONObj bsonObj = cursor->next(); _dbclient->insert(newCollection.toString(), bsonObj); } } void MongoClient::copyCollectionToDiffServer(mongo::DBClientBase *const fromServ, const MongoNamespace &from, const MongoNamespace &to) { if (!_dbclient->exists(to.toString())) _dbclient->createCollection(to.toString()); std::unique_ptr cursor{fromServ->query( mongo::NamespaceString(from.databaseName(), from.collectionName()), mongo::Query()) }; // Cursor may be NULL, it means we have connectivity problem if (!cursor) throw std::runtime_error("Network error while attempting to run query"); while (cursor->more()) { mongo::BSONObj bsonObj = cursor->next(); _dbclient->insert(to.toString(), bsonObj); } } void MongoClient::dropCollection(const MongoNamespace &ns) { if (_dbclient->exists(ns.toString())) { mongo::BSONObj info; if (!_dbclient->dropCollection(ns.toString(), mongo::WriteConcernOptions(), &info)) { std::string errStr = info.toString(); if (errStr.empty()) errStr = "Failed to get error message."; throw std::runtime_error(errStr); } } else { throw std::runtime_error("Collection does not exist."); } } void MongoClient::insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) { _dbclient->insert(ns.toString(), obj); checkLastErrorAndThrow(ns.databaseName()); } void MongoClient::saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) { mongo::BSONElement id = obj.getField("_id"); mongo::BSONObjBuilder builder; builder.append(id); mongo::BSONObj bsonQuery = builder.obj(); mongo::Query query(bsonQuery); _dbclient->update(ns.toString(), query, obj, true, false); checkLastErrorAndThrow(ns.databaseName()); } void MongoClient::removeDocuments(const MongoNamespace &ns, mongo::Query query, bool justOne /*= true*/) { _dbclient->remove(ns.toString(), query, justOne); checkLastErrorAndThrow(ns.databaseName()); } std::vector MongoClient::query(const MongoQueryInfo &info) { MongoNamespace ns(info._info._ns); //int limit = (info.limit <= 0) ? 50 : info.limit; std::vector docs; if (info._limit == -1) // it means that we do not need to load any documents return docs; std::unique_ptr cursor = _dbclient->query( mongo::NamespaceString(ns.databaseName(), ns.collectionName()), info._query, info._limit, info._skip, info._fields.nFields() ? &info._fields : 0, info._options, info._batchSize ); // DBClientBase::query may return nullptr if (!cursor) throw std::runtime_error("Network error while attempting to run query"); while (cursor->more()) { mongo::BSONObj bsonObj = cursor->next(); MongoDocumentPtr doc(new MongoDocument(bsonObj.getOwned())); docs.push_back(doc); } return docs; } MongoCollectionInfo MongoClient::runCollStatsCommand(const std::string &ns) { MongoCollectionInfo info(ns); return info; /* // Commented for now, to speedup load of collection names MongoNamespace mongons(ns); mongo::BSONObjBuilder command; // { collStats: "db.collection", scale : 1 } command.append("collStats", mongons.collectionName()); command.append("scale", 1); mongo::BSONObj result; _dbclient->runCommand(mongons.databaseName(), command.obj(), result); std::string isCV = result.toString(); MongoCollectionInfo newInfo(result); return newInfo; */ } std::vector MongoClient::runCollStatsCommand(const std::vector &namespaces) { std::vector infos; for (auto const& ns : namespaces) { MongoCollectionInfo info = runCollStatsCommand(ns); if (info.ns().isValid()) infos.push_back(info); } return infos; } void MongoClient::done() { // do nothing here, because we are not using ScopedDbConnection now //_scopedConnection->done(); } void MongoClient::checkLastErrorAndThrow(const std::string &db) { std::string const lastError = _dbclient->getLastError(db); if (lastError.empty()) return; throw std::runtime_error(lastError/*, mongo::ErrorCodes::InternalError*/); } } ================================================ FILE: src/robomongo/core/mongodb/MongoClient.h ================================================ #pragma once #include #include #include "robomongo/core/Core.h" #include "robomongo/core/domain/MongoQueryInfo.h" #include "robomongo/core/domain/MongoUser.h" #include "robomongo/core/domain/MongoFunction.h" #include "robomongo/core/events/MongoEventsInfo.h" namespace Robomongo { class MongoClient { public: MongoClient(mongo::DBClientBase *const scopedConnection); std::vector getCollectionNamesWithDbname(const std::string &dbname) const; std::vector getDatabaseNames() const; float getVersion() const; std::string dbVersionStr() const; std::string getStorageEngineType() const; std::vector getUsers(const std::string &dbName); void createUser(const std::string &dbName, const MongoUser &user); void dropUser(const std::string &dbName, const std::string &user); std::vector getFunctions(const std::string &dbName) const; std::vector getIndexes(const MongoCollectionInfo &collection) const; void dropIndexFromCollection(const MongoCollectionInfo &collection, const std::string &indexName) const; void addEditIndex(const IndexInfo &oldInfo, const IndexInfo &newInfo) const; void renameIndexFromCollection(const MongoCollectionInfo &collection, const std::string &oldIndexName, const std::string &newIndexName) const; void createFunction(const std::string &dbName, const MongoFunction &fun, const std::string &existingFunctionName = std::string()); void dropFunction(const std::string &dbName, const std::string &name); void createDatabase(const std::string &dbName); void dropDatabase(const std::string &dbName); void createCollection(const std::string &ns, long long size, bool capped, int max, const mongo::BSONObj& extraOptions, mongo::BSONObj* info = nullptr); void renameCollection(const MongoNamespace &ns, const std::string &newCollectionName); void duplicateCollection(const MongoNamespace &ns, const std::string &newCollectionName); void dropCollection(const MongoNamespace &ns); void copyCollectionToDiffServer(mongo::DBClientBase *const, const MongoNamespace &from, const MongoNamespace &to); void insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns); void saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns); void removeDocuments(const MongoNamespace &ns, mongo::Query query, bool justOne = true); std::vector query(const MongoQueryInfo &info); MongoCollectionInfo runCollStatsCommand(const std::string &ns); std::vector runCollStatsCommand(const std::vector &namespaces); void done(); private: mongo::DBClientBase *const _dbclient; void checkLastErrorAndThrow(const std::string &db); }; } ================================================ FILE: src/robomongo/core/mongodb/MongoWorker.cpp ================================================ #include "robomongo/core/mongodb/MongoWorker.h" #include #include #include #include #include #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/domain/MongoShellResult.h" #include "robomongo/core/domain/MongoCollectionInfo.h" #include "robomongo/core/events/MongoEvents.h" #include "robomongo/core/engine/ScriptEngine.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/mongodb/MongoClient.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/core/settings/CredentialSettings.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/settings/SslSettings.h" #include "robomongo/core/utils/BsonUtils.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/utils/StringOperations.h" namespace Robomongo { std::string const APP_VERSION = PROJECT_VERSION; std::string const APP_NAME_VERSION { "robo3t-" + APP_VERSION }; MongoWorker::MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize, double mongoTimeoutSec, int shellTimeoutSec, QObject *parent) : QObject(parent), _scriptEngine(nullptr), _isLoadMongoRcJs(isLoadMongoRcJs), _batchSize(batchSize), _timerId(-1), _dbAutocompleteCacheTimerId(-1), _mongoTimeoutSec(mongoTimeoutSec), _shellTimeoutSec(shellTimeoutSec), _isQuiting(0), _dbclient(nullptr), _dbclientRepSet(nullptr), _connSettings(connection) { // Whitespace removed from the start and the end of host string _connSettings->setServerHost(QString::fromStdString(_connSettings->serverHost()).trimmed().toStdString()); _thread = new QThread(); moveToThread(_thread); VERIFY(connect( _thread, SIGNAL(finished()), _thread, SLOT(deleteLater()) )); VERIFY(connect( _thread, SIGNAL(finished()), this, SLOT(deleteLater()) )); _thread->start(); } void MongoWorker::timerEvent(QTimerEvent *event) { if (_timerId == event->timerId()) { keepAlive(); return; } if (_dbAutocompleteCacheTimerId == event->timerId() && !_scriptEngine) { _scriptEngine->invalidateDbCollectionsCache(); return; } } void MongoWorker::restartReplicaSetConnection() { if (!_connSettings->hasEnabledPrimaryCredential()) return; CredentialSettings *credentials = _connSettings->primaryCredential(); mongo::BSONObj authParams { mongo::BSONObjBuilder() .append("user", credentials->userName()) .append("db", credentials->databaseName()) .append("pwd", credentials->userPassword()) .append("mechanism", credentials->mechanism()) .obj() }; _dbclientRepSet.release(); if(mongo::DBClientBase *conn = getConnection(true).first) conn->auth(authParams); } void MongoWorker::keepAlive() { try { if (_dbclient) pingDatabase(_dbclient.get()); if (_dbclientRepSet) pingDatabase(_dbclientRepSet.get()); if (_scriptEngine) _scriptEngine->ping(); } catch(std::exception &ex) { sendLog(this, LogEvent::RBM_WARN, "Failed to ping the server. " + std::string(ex.what())); } } void MongoWorker::init() { try { _scriptEngine.reset(new ScriptEngine(_connSettings, _shellTimeoutSec)); _scriptEngine->init(_isLoadMongoRcJs); _scriptEngine->use(_connSettings->defaultDatabase()); _scriptEngine->setBatchSize(_batchSize); constexpr int PING_INTERVAL_MSEC { 60 * 1000 }; // 60 seconds _timerId = startTimer(PING_INTERVAL_MSEC); _dbAutocompleteCacheTimerId = startTimer(30000); } catch (const std::exception &ex) { auto const msg { "Failed to initialize MongoWorker. Reason: "}; sendLog(this, LogEvent::RBM_ERROR, msg + std::string(ex.what())); throw std::runtime_error(msg + std::string(ex.what())); } } void MongoWorker::interrupt() { try { if (_isQuiting || !_scriptEngine) return; _scriptEngine->interrupt(); } catch(const std::exception &ex) { sendLog(this, LogEvent::RBM_ERROR, std::string(ex.what())); } } MongoWorker::~MongoWorker() { if (_timerId != -1) killTimer(_timerId); if (_dbAutocompleteCacheTimerId != -1) killTimer(_dbAutocompleteCacheTimerId); delete _connSettings; // QThread "_thread" and MongoWorker itself will be deleted later // (see MongoWorker() constructor) } void MongoWorker::stopAndDelete() { _isQuiting = 1; _thread->quit(); } void MongoWorker::changeTimeout(int newTimeout) { _scriptEngine->changeTimeout(newTimeout); } /** * @brief Initiate connection to MongoDB */ bool MongoWorker::handle(EstablishConnectionRequest *event) { QMutexLocker lock(&_firstConnectionMutex); std::unique_ptr repSetInfo(new ReplicaSet); auto errorCode = EventError::ErrorCode::Unknown; try { auto const& connAndErrorStr = getConnection(true); mongo::DBClientBase *conn = connAndErrorStr.first; // --- Connection failed for single server & replica set (no member of the set is reachable) if (!conn) { auto errorReason = std::string("Connection failure: Unknown error."); auto const& connErrorStr = connAndErrorStr.second; if (_connSettings->sslSettings()->sslEnabled()) errorReason = "TLS tunnel failure: Network is unreachable or TLS connection rejected by server." + (connErrorStr.empty() ? "" : " Reason: " + connErrorStr); else { // Non-TLS connections if (_connSettings->isReplicaSet()) { errorReason = "No member of the set is reachable." + (connErrorStr.empty() ? "" : " Reason: " + connErrorStr); std::vector> membersAndHealths; for (auto const& member : _connSettings->replicaSetSettings()->members()) membersAndHealths.push_back({ member, false }); repSetInfo.reset(new ReplicaSet("", mongo::HostAndPort(), membersAndHealths, errorReason)); } else // single server errorReason = "Network is unreachable." + (connErrorStr.empty() ? "" : " Reason: " + connErrorStr); } resetGlobalSSLparams(); reply(event->sender(), new EstablishConnectionResponse(this, EventError(errorReason, errorCode), event->connectionType, event->uuid, *repSetInfo.release(), EstablishConnectionResponse::MongoConnection)); return false; } // --- Single server: Connection successful // --- Replica set: Connection successful (primary reachable) or // Connection failed (primary unreachable with at least one reachable member) if (_connSettings->isReplicaSet()) { ReplicaSet const& setInfo = getReplicaSetInfo(); // Check if same set name used with different members which is not supported auto const& members = _connSettings->replicaSetSettings()->members(); if (std::find(members.cbegin(), members.cend(), setInfo.primary.toString()) == members.cend()) { // primary not found between user entered members std::string const errorStr { "Different members found under same replica set name \"" + setInfo.setName + "\"." }; repSetInfo.reset(new ReplicaSet(setInfo)); errorCode = EventError::ErrorCode::ServerHasDifferentMembers; sendLog(this, LogEvent::RBM_ERROR, errorStr); throw std::runtime_error(errorStr); } if (setInfo.primary.empty()) { // No reachable primary // Pass possible reachable secondary(ies) info repSetInfo.reset(new ReplicaSet(setInfo)); sendLog(this, LogEvent::RBM_ERROR, setInfo.errorStr); throw std::runtime_error(setInfo.errorStr); } else { // Primary is reachable, save setInfo and continue repSetInfo.reset(new ReplicaSet(setInfo)); } } if (_connSettings->hasEnabledPrimaryCredential()) { CredentialSettings const * const credentials = _connSettings->primaryCredential(); // Building BSON object: mongo::BSONObj const authParams { mongo::BSONObjBuilder() .append("user", credentials->userName()) .append("db", credentials->databaseName()) .append("pwd", credentials->userPassword()) .append("mechanism", credentials->mechanism()) .obj() }; conn->auth(authParams); } boost::scoped_ptr client(getClient()); std::vector const dbNames = getDatabaseNamesSafe(event); // If we do not have databases, it means that we are unable to // execute "listdatabases" command and we have nothing to show. if (dbNames.size() == 0) throw std::runtime_error("Failed to execute \"listdatabases\" command."); if (!_connSettings->isReplicaSet()) init(); // Init MongoWorker for single server (for replica set connections early init is used) resetGlobalSSLparams(); auto connInfo = ConnectionInfo(_connSettings->getFullAddress(), dbNames, client->getVersion(), client->dbVersionStr(), client->getStorageEngineType(), event->uuid); // todo: two ctors for rep.set and single server. reply(event->sender(), new EstablishConnectionResponse(this, connInfo, event->connectionType, *repSetInfo.release())); return true; } catch(const std::exception &ex) { resetGlobalSSLparams(); auto errorReason = _connSettings->sslSettings()->sslEnabled() ? EstablishConnectionResponse::ErrorReason::MongoSslConnection : EstablishConnectionResponse::ErrorReason::MongoAuth; reply(event->sender(), new EstablishConnectionResponse(this, EventError(ex.what(), errorCode), event->connectionType, ConnectionInfo(event->uuid), *repSetInfo.release(), errorReason)); sendLog(this, LogEvent::RBM_ERROR, ex.what()); // todo: duplicate logging? } return false; } void MongoWorker::handle(RefreshReplicaSetFolderRequest *event) { configureSSL(); try { ReplicaSet const& replicaSetInfo = getReplicaSetInfo(); // Primary is unreachable, but there might be reachable secondary(ies) if (replicaSetInfo.primary.empty()) { reply( event->sender(), new RefreshReplicaSetFolderResponse( this, replicaSetInfo, event->expanded, EventError(replicaSetInfo.errorStr) ) ); sendLog(this, LogEvent::RBM_ERROR, replicaSetInfo.errorStr); return; } else { // Primary is reachable reply(event->sender(), new RefreshReplicaSetFolderResponse(this, replicaSetInfo, event->expanded)); } } catch (const std::exception &ex) { reply( event->sender(), new RefreshReplicaSetFolderResponse( this, ReplicaSet(), event->expanded, EventError(ex.what()) ) ); sendLog(this, LogEvent::RBM_ERROR, ex.what()); } } std::string MongoWorker::getAuthBase() const { if (_connSettings->hasEnabledPrimaryCredential()) return _connSettings->primaryCredential()->databaseName(); return std::string(); } std::vector MongoWorker::getDatabaseNamesSafe(EstablishConnectionRequest* event /*= nullptr*/) { std::set dbNames; auto const primaryCredential { _connSettings->primaryCredential() }; try { boost::scoped_ptr client(getClient()); std::vector dbNamesFetched { client->getDatabaseNames() }; dbNames = std::set { dbNamesFetched.cbegin(), dbNamesFetched.cend() }; } catch(const std::exception &ex) { #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wlogical-op-parentheses" #endif bool const informUser { event != nullptr && event->connectionType == ConnectionType::ConnectionPrimary && _connSettings->credentialCount() > 0 && !primaryCredential->useManuallyVisibleDbs() || primaryCredential->manuallyVisibleDbs().empty() }; #if defined(__clang__) #pragma clang diagnostic pop #endif std::string const hint { "\n\nHint: If this user has access to a specific database, " "please use \"Manually specify visible databases\" option in " "Connection Settings window -> Authentication tab." }; sendLog(this, LogEvent::RBM_WARN, ex.what() + hint, informUser); } if (_connSettings->credentialCount() > 0 && primaryCredential->useManuallyVisibleDbs() && !primaryCredential->manuallyVisibleDbs().empty() ) { QString const manuallyVisibleDbs { QString::fromStdString(primaryCredential->manuallyVisibleDbs()) }; for (auto const& db : manuallyVisibleDbs.split(',').toStdList()) dbNames.insert(db.toStdString()); } std::string const authBase = getAuthBase(); if (!authBase.empty()) dbNames.insert(authBase); return std::vector { dbNames.cbegin(), dbNames.cend() }; } /** * @brief Load list of all database names */ void MongoWorker::handle(LoadDatabaseNamesRequest *event) { try { // If user not an admin - he doesn't have access to mongodb 'listDatabases' command // Non admin user has access only to the single database he specified while performing auth. std::vector dbNames = getDatabaseNamesSafe(); // Remove from list of created databases existing databases for (std::vector::iterator it = dbNames.begin(); it != dbNames.end(); ++it) { std::unordered_set::const_iterator exists = _createdDbs.find(*it); if (exists != _createdDbs.end()) { _createdDbs.erase(*it); } } // Merge with list of created databases for (std::unordered_set::iterator it = _createdDbs.begin(); it != _createdDbs.end(); ++it) { dbNames.push_back(*it); } if (dbNames.size()) { reply(event->sender(), new LoadDatabaseNamesResponse(this, dbNames)); } else { auto const errorStr{ "Failed to execute \"listdatabases\" command." }; reply(event->sender(), new LoadDatabaseNamesResponse(this, EventError(errorStr))); } } catch(const std::exception &ex) { reply(event->sender(), new LoadDatabaseNamesResponse(this, EventError(ex.what()))); sendLog(this, LogEvent::RBM_ERROR, ex.what()); } } /** * @brief Load list of all collection names */ void MongoWorker::handle(LoadCollectionNamesRequest *event) { try { boost::scoped_ptr client(getClient()); auto const& namespaces = client->getCollectionNamesWithDbname(event->databaseName()); std::vector const& collInfos = client->runCollStatsCommand(namespaces); client->done(); reply(event->sender(), new LoadCollectionNamesResponse(this, event->databaseName(), collInfos)); } catch(const std::exception &ex) { reply(event->sender(), new LoadCollectionNamesResponse(this, EventError(ex.what()))); // Logging handled in main thread } } void MongoWorker::handle(LoadUsersRequest *event) { try { boost::scoped_ptr client(getClient()); const std::vector &users = client->getUsers(event->databaseName()); client->done(); reply(event->sender(), new LoadUsersResponse(this, event->databaseName(), users)); } catch(const std::exception &ex) { reply(event->sender(), new LoadUsersResponse(this, EventError(ex.what()))); // Logging handled in main thread } } void MongoWorker::handle(LoadCollectionIndexesRequest *event) { try { boost::scoped_ptr client(getClient()); const std::vector &ind = client->getIndexes(event->collection()); client->done(); reply(event->sender(), new LoadCollectionIndexesResponse(this, ind)); } catch(const std::exception &ex) { reply(event->sender(), new LoadCollectionIndexesResponse(this, EventError(ex.what()))); sendLog(this, LogEvent::RBM_ERROR, ex.what()); } } void MongoWorker::handle(AddEditIndexRequest *event) { const IndexInfo &newIndex = event->newInfo(); const IndexInfo &oldIndex = event->oldInfo(); try { boost::scoped_ptr client(getClient()); client->addEditIndex(oldIndex, newIndex); client->done(); reply(event->sender(), new AddEditIndexResponse(this, oldIndex, newIndex)); std::vector const &indexes = client->getIndexes(newIndex._collection); reply(event->sender(), new LoadCollectionIndexesResponse(this, indexes)); } catch(const std::exception &ex) { reply(event->sender(), new AddEditIndexResponse(this, EventError(ex.what()), oldIndex, newIndex) ); // Logging handled in main thread } } void MongoWorker::handle(DropCollectionIndexRequest *event) { try { boost::scoped_ptr client(getClient()); client->dropIndexFromCollection(event->collection(), event->index()); client->done(); reply(event->sender(), new DropCollectionIndexResponse(this, event->collection(), event->index())); } catch(const std::exception &ex) { reply(event->sender(), new DropCollectionIndexResponse(this, EventError(ex.what()), event->index())); // Logging handled in main thread } } void MongoWorker::handle(LoadFunctionsRequest *event) { try { boost::scoped_ptr client(getClient()); const std::vector &funcs = client->getFunctions(event->databaseName()); client->done(); // If list of functions from client is empty, try getting it with script engine if (funcs.empty()) { MongoShellExecResult const& result = _scriptEngine->exec("db.system.js.find()", event->databaseName()); std::vector functions; if (!result.results().empty()) { auto const& resultDocs = result.results().front().documents(); for (auto const res : resultDocs) functions.push_back(MongoFunction(res->bsonObj())); } reply(event->sender(), new LoadFunctionsResponse(this, event->databaseName(), functions)); return; } reply(event->sender(), new LoadFunctionsResponse(this, event->databaseName(), funcs)); } catch(const std::exception &ex) { reply(event->sender(), new LoadFunctionsResponse(this, EventError(ex.what()))); // Logging handled in main thread } } void MongoWorker::handle(InsertDocumentRequest *event) { try { boost::scoped_ptr client(getClient()); if (event->overwrite()) client->saveDocument(event->obj(), event->ns()); else client->insertDocument(event->obj(), event->ns()); client->done(); reply(event->sender(), new InsertDocumentResponse(this)); } catch(const std::exception &ex) { reply(event->sender(), new InsertDocumentResponse(this, EventError(ex.what()))); sendLog(this, LogEvent::RBM_ERROR, ex.what()); } } void MongoWorker::handle(RemoveDocumentRequest *event) { try { boost::scoped_ptr client(getClient()); client->removeDocuments(event->ns(), event->query(), event->removeCount() == RemoveDocumentCount::ONE); client->done(); reply(event->sender(), new RemoveDocumentResponse(this, event->removeCount(), event->index())); } catch(const std::exception &ex) { reply(event->sender(), new RemoveDocumentResponse(this, EventError(ex.what()), event->removeCount(), event->index())); // Logging handled in main thread } } void MongoWorker::handle(ExecuteQueryRequest *event) { auto const executeQuery = [&]() { boost::scoped_ptr client { getClient() }; std::vector docs = client->query(event->queryInfo()); client->done(); reply(event->sender(), new ExecuteQueryResponse(this, event->resultIndex(), event->queryInfo(), docs) ); }; try { executeQuery(); } catch(const std::exception &ex) { QString const NTORETURN_ERROR { "unrecognized field: 'ntoreturn'" }; bool const ntoreturnError { QString(ex.what()).compare(NTORETURN_ERROR, Qt::CaseInsensitive) == 0 }; // If we have this DocumentDB specific error, try again if (ntoreturnError && _dbclient) { sendLog(this, LogEvent::RBM_ERROR, std::string(ex.what())); try { _dbclient->tagAsDocDb(true); executeQuery(); return; } catch (const std::exception &ex) { sendLog(this, LogEvent::RBM_ERROR, "Re-try: " + std::string(ex.what())); } } reply(event->sender(), new ExecuteQueryResponse(this, EventError(ex.what()))); sendLog(this, LogEvent::RBM_ERROR, std::string(ex.what())); } } /** * @brief Execute javascript */ void MongoWorker::handle(ExecuteScriptRequest *event) { try { if(!_scriptEngine || (_connSettings->isReplicaSet() && !_dbclientRepSet)) { auto const error{ EventError("MongoDB Shell was not initialized or connection failure") }; reply(event->sender(), new ExecuteScriptResponse(this, error)); return; } // Try to handle case where new shell (which was opened when server unreachable) // was re-executed if (_scriptEngine->failedScope()) { try { _scriptEngine->init(_isLoadMongoRcJs); } catch (std::exception const& ex) { sendLog(this, LogEvent::RBM_ERROR, captilizeFirstChar(ex.what()) + ", cannot init mongo scope"); } } // todo: should we use dbName from event or _connSettings? MongoShellExecResult result { _scriptEngine->exec( event->script, _connSettings->defaultDatabase(), event->aggrInfo ) }; // To fix the problem where 'result' comes with old primary address. if (_connSettings->isReplicaSet()) result.setCurrentServer( _dbclientRepSet->getSuspectedPrimaryHostAndPort().toString() ); if (!result.error()) { reply( event->sender(), new ExecuteScriptResponse(this, result, event->script.empty(), result.timeoutReached()) // todo: rename to shellTimeout... ); return; } retry(event); } catch(const std::exception &ex) { auto const error { EventError(ex.what(), EventError::Unknown) }; reply(event->sender(), new ExecuteScriptResponse(this, error)); sendLog(this, LogEvent::RBM_ERROR, ex.what()); } } void MongoWorker::retry(ExecuteScriptRequest * event) { mongo::DBClientBase* mongodbClient { _dbclient ? _dbclient.get() : dynamic_cast(_dbclientRepSet.get()) }; if (!mongodbClient->isStillConnected()) mongodbClient->checkConnection(); MongoShellExecResult const result { _scriptEngine->exec(event->script, _connSettings->defaultDatabase()) }; if (result.error()) { auto const error { EventError(result.errorMessage()) }; reply(event->sender(), new ExecuteScriptResponse(this, error)); } else { reply( event->sender(), new ExecuteScriptResponse(this, result, event->script.empty(), result.timeoutReached()) ); } } /** * @brief Interrupt javascript execution */ void MongoWorker::handle(StopScriptRequest *) { try { if (!_scriptEngine) { return; } _scriptEngine->interrupt(); } catch(const std::exception &ex) { sendLog(this, LogEvent::RBM_ERROR, std::string(ex.what())); } } void MongoWorker::handle(AutocompleteRequest *event) { try { if (!_scriptEngine) { reply(event->sender(), new AutocompleteResponse(this, EventError("MongoDB Shell was not initialized"))); return; } QStringList list = _scriptEngine->complete(event->prefix, event->mode); reply(event->sender(), new AutocompleteResponse(this, list, event->prefix)); } catch(const std::exception &ex) { reply(event->sender(), new AutocompleteResponse(this, EventError(ex.what()))); sendLog(this, LogEvent::RBM_ERROR, std::string(ex.what())); } } void MongoWorker::handle(CreateDatabaseRequest *event) { std::string dbname = event->database(); try { boost::scoped_ptr client(getClient()); client->createDatabase(dbname); // Insert to list of created database. Read docs for this hashset in the header _createdDbs.insert(dbname); reply(event->sender(), new CreateDatabaseResponse(this, dbname)); } catch(const std::exception &ex) { reply(event->sender(), new CreateDatabaseResponse(this, dbname, EventError(ex.what())) ); // Logging handled in main thread } } void MongoWorker::handle(DropDatabaseRequest *event) { try { boost::scoped_ptr client(getClient()); client->dropDatabase(event->database); // Remove from the list of created database, Read docs for this hashset in the header _createdDbs.erase(event->database); reply(event->sender(), new DropDatabaseResponse(this, event->database)); } catch(const std::exception &ex) { reply(event->sender(), new DropDatabaseResponse(this, event->database, EventError(ex.what())) ); // Logging handled in main thread } } void MongoWorker::handle(CreateCollectionRequest *event) { std::string const& collection = event->ns().collectionName(); try { boost::scoped_ptr client(getClient()); client->createCollection(event->ns().toString(), event->getSize(), event->getCapped(), event->getMaxDocNum(), event->getExtraOptions()); client->done(); reply(event->sender(), new CreateCollectionResponse(this, collection)); } catch(const std::exception &ex) { reply(event->sender(), new CreateCollectionResponse(this, collection, EventError(ex.what())) ); // Logging handled in main thread } } void MongoWorker::handle(DropCollectionRequest *event) { std::string const& collection = event->ns().collectionName(); try { boost::scoped_ptr client(getClient()); client->dropCollection(event->ns()); client->done(); reply(event->sender(), new DropCollectionResponse(this, collection)); } catch(const std::exception &ex) { reply(event->sender(), new DropCollectionResponse(this, collection, EventError(ex.what())) ); // Logging handled in main thread } } void MongoWorker::handle(RenameCollectionRequest *event) { try { boost::scoped_ptr client(getClient()); client->renameCollection(event->ns(), event->newCollection()); client->done(); reply(event->sender(), new RenameCollectionResponse(this, event->ns().collectionName(), event->newCollection())); } catch(const std::exception &ex) { reply(event->sender(), new RenameCollectionResponse(this, EventError(ex.what()))); // Logging handled in main thread } } void MongoWorker::handle(DuplicateCollectionRequest *event) { std::string const& sourceCollection = event->ns().collectionName(); try { boost::scoped_ptr client(getClient()); client->duplicateCollection(event->ns(), event->newCollection()); client->done(); reply(event->sender(), new DuplicateCollectionResponse(this, sourceCollection, event->newCollection()) ); } catch (const std::exception &ex) { reply(event->sender(), new DuplicateCollectionResponse(this, sourceCollection, EventError(ex.what())) ); // Logging handled in main thread } } void MongoWorker::handle(CopyCollectionToDiffServerRequest *event) { try { boost::scoped_ptr client(getClient()); MongoWorker *cl = event->worker(); client->copyCollectionToDiffServer( cl->_dbclient.get(), event->from(), event->to() ); client->done(); reply(event->sender(), new CopyCollectionToDiffServerResponse(this)); } catch(const std::exception &ex) { reply(event->sender(), new CopyCollectionToDiffServerResponse(this, EventError(ex.what())) ); sendLog(this, LogEvent::RBM_ERROR, std::string(ex.what())); } } void MongoWorker::handle(CreateUserRequest *event) { try { boost::scoped_ptr client(getClient()); client->createUser(event->database(), event->user()); client->done(); reply(event->sender(), new CreateUserResponse(this, event->user().name())); } catch(const std::exception &ex) { reply(event->sender(), new CreateUserResponse(this, event->user().name(), EventError(ex.what())) ); // Logging handled in main thread } } void MongoWorker::handle(DropUserRequest *event) { try { boost::scoped_ptr client(getClient()); client->dropUser(event->database(), event->username()); client->done(); reply(event->sender(), new DropUserResponse(this, event->username())); } catch(const std::exception &ex) { reply(event->sender(), new DropUserResponse(this, event->username(), EventError(ex.what())) ); // Logging handled in main thread } } void MongoWorker::handle(CreateFunctionRequest *event) { std::string const& functionName = event->function().name(); try { if (event->dbVersion() >= 3.4) { auto const cmd = "db.system.js.save(" + event->function().toBson().toString() + ')'; MongoShellExecResult const& result = _scriptEngine->exec(cmd, event->database()); if (result.error()) throw std::runtime_error(result.errorMessage()); } else { boost::scoped_ptr client(getClient()); client->createFunction(event->database(), event->function(), event->existingFunctionName()); client->done(); } reply(event->sender(), new CreateFunctionResponse(this, functionName)); } catch(const std::exception &ex) { reply(event->sender(), new CreateFunctionResponse(this, functionName, EventError(ex.what())) ); // Logging handled in main thread } } void MongoWorker::handle(DropFunctionRequest *event) { try { if (event->dbVersion() >= 3.4) { auto const cmd = "db.system.js.remove( { _id : \"" + event->functionName() + "\" } )"; MongoShellExecResult const& result = _scriptEngine->exec(cmd, event->database()); if (result.error()) throw std::runtime_error(result.errorMessage()); } else { boost::scoped_ptr client(getClient()); client->dropFunction(event->database(), event->functionName()); client->done(); } reply(event->sender(), new DropFunctionResponse(this, event->functionName())); } catch (const std::exception &ex) { reply(event->sender(), new DropFunctionResponse(this, event->functionName(), EventError(ex.what())) ); // Logging handled in main thread } } std::pair MongoWorker::getConnection(bool mayReturnNull /* = false */) { configureSSL(); // --- Perform connection --- if (_connSettings->isReplicaSet()) { // connection to replica set if(_dbclientRepSet) return { _dbclientRepSet.get(), "" }; init(); // Init mongoworker for early-use of _scriptEngine // Step-1: Use user entered set name or retrieve set name from cache or from a reachable member std::string setName = _connSettings->replicaSetSettings()->setNameUserEntered(); if (setName.empty()) { setName = _connSettings->replicaSetSettings()->cachedSetName(); if (setName.empty()) // If there is no cached set name, get it from an on-line replica node setName = connectAndGetReplicaSetName(); if (setName.empty()) // It is not possible to continue with empty set name return { nullptr, "It is not possible to continue with empty set name" }; } // Step-2: Try connect to replica set with set name auto const& membersHostsAndPorts = _connSettings->replicaSetSettings()->membersToHostAndPort(); _dbclientRepSet.reset(new mongo::DBClientReplicaSet { setName, membersHostsAndPorts, APP_NAME_VERSION, _mongoTimeoutSec }); if (!_dbclientRepSet->connect()) return { nullptr, "Connect failed" }; else return { _dbclientRepSet.get(), "" }; } else { // connection to single server if(_dbclient) return { _dbclient.get(), "" }; // Timeout for operations // Connect timeout is fixed, but short, at 5 seconds (see headers for DBClientConnection) _dbclient.reset(new mongo::DBClientConnection { true, _mongoTimeoutSec }); mongo::Status const& status = _dbclient->connect(_connSettings->hostAndPort(), APP_NAME_VERSION); if (!status.isOK() && mayReturnNull) return { nullptr, status.reason() }; else return { _dbclient.get(), "" }; } } MongoClient *MongoWorker::getClient() { return new MongoClient(getConnection().first); } void MongoWorker::configureSSL() { // As a precaution reset SSL global params for any kind of connection request (SSL or non-SSL) resetGlobalSSLparams(); // Update global SSL mode and global mongo SSL settings if (_connSettings->sslSettings()->sslEnabled()) { // Force SSL mode for outgoing connections mongo::sslGlobalParams.sslMode.store(mongo::SSLParams::SSLMode_requireSSL); updateGlobalSSLparams(); } else { // Disable forced SSL mode for outgoing connections mongo::sslGlobalParams.sslMode.store(mongo::SSLParams::SSLMode_allowSSL); } } void MongoWorker::updateGlobalSSLparams() const { resetGlobalSSLparams(); const SslSettings * const sslSettings = _connSettings->sslSettings(); mongo::sslGlobalParams.sslAllowInvalidCertificates = sslSettings->allowInvalidCertificates(); if (!mongo::sslGlobalParams.sslAllowInvalidCertificates) { mongo::sslGlobalParams.sslCAFile = sslSettings->caFile(); } if (sslSettings->usePemFile()) { mongo::sslGlobalParams.sslPEMKeyFile = sslSettings->pemKeyFile(); mongo::sslGlobalParams.sslPEMKeyPassword = sslSettings->pemPassPhrase(); } if (sslSettings->useAdvancedOptions()) { mongo::sslGlobalParams.sslCRLFile = sslSettings->crlFile(); mongo::sslGlobalParams.sslAllowInvalidHostnames = sslSettings->allowInvalidHostnames(); } } void MongoWorker::resetGlobalSSLparams() const { mongo::sslGlobalParams.sslAllowInvalidCertificates = false; mongo::sslGlobalParams.sslCAFile = ""; mongo::sslGlobalParams.sslPEMKeyFile = ""; mongo::sslGlobalParams.sslPEMKeyPassword = ""; mongo::sslGlobalParams.sslCRLFile = ""; mongo::sslGlobalParams.sslAllowInvalidHostnames = false; } // todo: From 1.4, this function started to return incorrect member healths when set is unreachable. // Needs more investigation. ReplicaSet MongoWorker::getReplicaSetInfo() const { std::string setName; mongo::HostAndPort primary; std::vector> membersAndHealths; // Refresh view of Replica Set Monitor to get live data auto repSetMonitor = mongo::globalRSMonitorManager.getMonitor(_dbclientRepSet->getSetName()); // Handle long lasting (e.g. overnight) connections which causes replica set monitor to be null if (!repSetMonitor) { mongo::ReplicaSetMonitor::createIfNeeded(_dbclientRepSet->getSetName(), _connSettings->replicaSetSettings()->membersToHostAndPortAsSet()); repSetMonitor = mongo::globalRSMonitorManager.getMonitor(_dbclientRepSet->getSetName()); if (!repSetMonitor) // if nullptr again even if the set is reachable, something went really wrong. return ReplicaSet(setName, primary, membersAndHealths, "Unexpected error. Unable to get replica set monitor for set name: " + _dbclientRepSet->getSetName() + "\nPlease open a new connection."); } setName = repSetMonitor->getName(); auto const readPrimaryOnly = mongo::ReadPreferenceSetting(mongo::ReadPreference::PrimaryOnly, mongo::TagSet()); auto const primaryFuture = repSetMonitor->getHostOrRefresh(readPrimaryOnly, mongo::Milliseconds(2000)); auto const& primaryStatus{ primaryFuture.waitNoThrow() }; if (primaryStatus.isOK()) primary = primaryFuture.get(); QStringList servers; // i.e. setNameAndMembers: "repset/localhost:27017,localhost:27018,localhost:27019" QString const setNameAndMembers = QString::fromStdString(repSetMonitor->getServerAddress()); QStringList const result = setNameAndMembers.split("/"); if (result.size() > 1) servers = result[1].split(","); // Save address and health of replica members for (QString const& server : servers) { auto const hostAndPort = mongo::HostAndPort(mongo::StringData(server.toStdString())); membersAndHealths.push_back({ server.toStdString(), repSetMonitor->isHostUp(hostAndPort) }); } return ReplicaSet(setName, primary, membersAndHealths, primaryStatus.reason()); } std::string MongoWorker::connectAndGetReplicaSetName() const { auto const dbclientTemp { std::make_unique(true, 10) }; std::string setName = ""; // Try connecting to the nodes one by one until getting replica set name. for (auto const& node : _connSettings->replicaSetSettings()->membersToHostAndPort()) { if (!dbclientTemp->connect(node, APP_NAME_VERSION).isOK()) return ""; _scriptEngine->init(_isLoadMongoRcJs, node.toString()); MongoShellExecResult const& result = _scriptEngine->exec("rs.status()", ""); if (!result.results().empty()) { auto const& resultDocs = result.results().front().documents(); if (!resultDocs.empty()) { setName = resultDocs.front()->bsonObj().getStringField("set"); if (!setName.empty()) // We get the information, finish the loop break; } } } return setName; } /** * @brief Send event to this MongoWorker */ void MongoWorker::send(Event *event) { if (_isQuiting) return; AppRegistry::instance().bus()->send(this, event); } /** * @brief Send reply event to object 'receiver' */ void MongoWorker::reply(QObject *receiver, Event *event) { if (_isQuiting) return; AppRegistry::instance().bus()->send(receiver, event); } void MongoWorker::pingDatabase(mongo::DBClientBase *dbclient) const { // Building { ping: 1 } mongo::BSONObjBuilder command; command.append("ping", 1); mongo::BSONObj result; std::string const authBase = getAuthBase(); dbclient->runCommand(authBase.empty() ? "admin" : authBase, command.obj(), result); } } ================================================ FILE: src/robomongo/core/mongodb/MongoWorker.h ================================================ #pragma once #include #include #include #include #include "robomongo/core/events/MongoEvents.h" QT_BEGIN_NAMESPACE class QThread; class QTimer; QT_END_NAMESPACE namespace Robomongo { class MongoClient; class ScriptEngine; class ConnectionSettings; class MongoWorker : public QObject { Q_OBJECT public: explicit MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize, double mongoTimeoutSec, int shellTimeoutSec, QObject *parent = nullptr); ~MongoWorker(); void interrupt(); void stopAndDelete(); void changeTimeout(int newTimeout); protected Q_SLOTS: void init(); /** * @brief Every minute we are issuing { ping : 1 } command to every used connection * in order to avoid dropped connections. */ void keepAlive(); /** * @brief Initiate connection to MongoDB */ bool handle(EstablishConnectionRequest *event); /** * @brief Try to re-connect to MongoDB replica set in order to refresh connection view. * For more details, see MongoServer::tryRefreshReplicaSetFolder() */ void handle(RefreshReplicaSetFolderRequest *event); /** * @brief Load list of all database names */ void handle(LoadDatabaseNamesRequest *event); /** * @brief Load list of all collection names */ void handle(LoadCollectionNamesRequest *event); /** * @brief Load list of all users */ void handle(LoadUsersRequest *event); /** * @brief Load indexes in collection */ void handle(LoadCollectionIndexesRequest *event); /** * @brief Add/edit indexes in collection */ void handle(AddEditIndexRequest *event); /** * @brief delete index from collection */ void handle(DropCollectionIndexRequest *event); /** * @brief Load list of all JS functions */ void handle(LoadFunctionsRequest *event); /** * @brief Inserts document */ void handle(InsertDocumentRequest *event); /** * @brief Remove documents */ void handle(RemoveDocumentRequest *event); /** * @brief Load list of all collection names */ void handle(ExecuteQueryRequest *event); /** * @brief Execute javascript */ void handle(ExecuteScriptRequest *event); void retry(ExecuteScriptRequest *event); void handle(StopScriptRequest *event); void handle(AutocompleteRequest *event); void handle(CreateDatabaseRequest *event); void handle(DropDatabaseRequest *event); void handle(CreateCollectionRequest *event); void handle(DropCollectionRequest *event); void handle(RenameCollectionRequest *event); void handle(DuplicateCollectionRequest *event); void handle(CopyCollectionToDiffServerRequest *event); // todo: unused? remove void handle(CreateUserRequest *event); void handle(DropUserRequest *event); void handle(CreateFunctionRequest *event); void handle(DropFunctionRequest *event); protected: virtual void timerEvent(QTimerEvent *); private: // Added after Mongo 4.0 to fix connection failures seen after a first edit/add/remove doc. operation void restartReplicaSetConnection(); /** * @brief Send event to this MongoWorker */ void send(Event *event); std::vector getDatabaseNamesSafe(EstablishConnectionRequest* event = nullptr); std::string getAuthBase() const; // Returns a pair of DBClientBase* connection and error string std::pair getConnection(bool mayReturnNull = false); MongoClient *getClient(); /** *@brief Reset and update global mongo SSL settings (mongo::sslGlobalParams) */ void configureSSL(); /** *@brief Update global mongo SSL settings (mongo::sslGlobalParams) according to active connection * request's SSL settings. */ void updateGlobalSSLparams() const; /** *@brief Reset global mongo SSL settings (mongo::sslGlobalParams) into default zero state */ void resetGlobalSSLparams() const; /** *@brief Update Replica Set related parameters/settings. */ ReplicaSet getReplicaSetInfo() const; std::string connectAndGetReplicaSetName() const; /** * @brief Send reply event to object 'obj' */ void reply(QObject *receiver, Event *event); /** * @brief Send hear beat messages to database in order to keep connection alive */ void pingDatabase(mongo::DBClientBase *dbclient) const; QThread *_thread; QMutex _firstConnectionMutex; std::unique_ptr _scriptEngine; const bool _isLoadMongoRcJs; const int _batchSize; int _timerId; int _dbAutocompleteCacheTimerId; double _mongoTimeoutSec; int _shellTimeoutSec; QAtomicInteger _isQuiting; std::unique_ptr _dbclient; std::unique_ptr _dbclientRepSet; ConnectionSettings *_connSettings; // Collection of created databases. // Starting from 3.0, MongoDB drops empty databases. // It means, we did not find a way to create "empty" database. // We save all created databases in this collection and merge with // list of real databases returned from MongoDB server. std::unordered_set _createdDbs; }; } ================================================ FILE: src/robomongo/core/mongodb/ReplicaSet.cpp ================================================ #include "robomongo/core/mongodb/ReplicaSet.h" namespace Robomongo { ReplicaSet::ReplicaSet(std::string const& setName, const mongo::HostAndPort primary, std::vector> const& membersAndHealths, std::string const& errorStr) : setName(setName), primary(primary), membersAndHealths(membersAndHealths), errorStr(errorStr) {} } ================================================ FILE: src/robomongo/core/mongodb/ReplicaSet.h ================================================ #pragma once #include #include namespace Robomongo { /** * @brief Struct to represent live information of a replica set. */ struct ReplicaSet { ReplicaSet(std::string const& setName, const mongo::HostAndPort primary, std::vector> const& membersAndHealths, std::string const& errorStr = ""); ReplicaSet() {}; std::string const setName; mongo::HostAndPort const primary; std::string const errorStr; // pair: {HostNameAndPort, Health} std::vector> const membersAndHealths; // todo: make first element of pair type HostnameAndPort }; } ================================================ FILE: src/robomongo/core/mongodb/SshTunnelWorker.cpp ================================================ #include "robomongo/core/mongodb/SshTunnelWorker.h" #include #include #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/settings/SshSettings.h" #include "robomongo/core/events/MongoEvents.h" #include "robomongo/ssh/ssh.h" #include "robomongo/core/domain/App.h" namespace Robomongo { SshTunnelWorker::SshTunnelWorker(ConnectionSettings *settings) : QObject(), _settings(settings), _sshSession(NULL), _configCreator(settings) { _thread = new QThread(); moveToThread(_thread); VERIFY(connect(_thread, SIGNAL(finished()), _thread, SLOT(deleteLater()))); VERIFY(connect(_thread, SIGNAL(finished()), this, SLOT(deleteLater()))); _thread->start(); } SshTunnelWorker::~SshTunnelWorker() { // QThread "_thread" and MongoWorker itself will be deleted later // (see MongoWorker() constructor) delete _settings; printf("SSH tunnel closed.\n"); } void SshTunnelWorker::stopAndDelete() { _isQuiting = 1; _thread->quit(); } void SshTunnelWorker::handle(EstablishSshConnectionRequest *event) { try { if (_isQuiting) return; // Additionally configure "rbm_ssh_tunnel_config" _configCreator.config()->logcontext = this; _configCreator.config()->logcallback = &SshTunnelWorker::logCallbackHandler; _configCreator.config()->loglevel = (rbm_ssh_log_type) _settings->sshSettings()->logLevel(); // RBM_SSH_LOG_TYPE_DEBUG; if ((_sshSession = rbm_ssh_session_create(_configCreator.config())) == 0) { // Not much we can say about this error throw std::runtime_error("Failed to create SSH session"); } if (rbm_ssh_session_setup(_sshSession) == -1) { // Prepare copy of error message (if any) std::string error(_sshSession->lasterror); std::stringstream ss; ss << "Failed to create SSH tunnel to " << _settings->sshSettings()->host() << ":" << _settings->sshSettings()->port() << ".\n\nError:\n" << error; // Cleanup session rbm_ssh_session_close(_sshSession); _sshSession = NULL; throw std::runtime_error(ss.str()); } reply(event->sender(), new EstablishSshConnectionResponse( this, event->serverHandle, event->worker, event->settings, event->connectionType, _configCreator.config()->localport)); } catch (const std::exception& ex) { reply(event->sender(), new EstablishSshConnectionResponse(this, event->serverHandle, EventError(ex.what()), event->worker, event->settings, event->connectionType)); // In case of error in connection, we should cleanup SshTunnelWorker stopAndDelete(); } } void SshTunnelWorker::handle(ListenSshConnectionRequest *event) { try { if (_isQuiting) return; if (_sshSession == NULL) return; // We are running this timer in order to distinguish between two // types of errors: // 1) SSH tunnel wasn't successfully created // 2) SSH tunnel was disconnected // This is used now only for UI error message, that we show to user. QElapsedTimer timer; timer.start(); // This function will block until all TCP connections disconnects. // Initially, it will wait for at least one such connection. if (rbm_ssh_open_tunnel(_sshSession) != 0) { qint64 elapsed = timer.elapsed(); bool wasDisconnected = elapsed > 20000; // More than 20 seconds passed // Prepare copy of error message (if any) std::string error(_sshSession->lasterror); std::stringstream ss; if (wasDisconnected) { ss << "You are disconnected from SSH tunnel (" << _settings->sshSettings()->host() << ":" << _settings->sshSettings()->port() << "). " << "Please initiate a new connection and reopen all tabs.\n\nError:\n" << error; } else { ss << "Cannot establish SSH tunnel (" << _settings->sshSettings()->host() << ":" << _settings->sshSettings()->port() << "). " << "\n\nError:\n" << error; } // Cleanup session rbm_ssh_session_close(_sshSession); _sshSession = NULL; throw std::runtime_error(ss.str()); } log("SSH tunnel stopped normally.", false); } catch (const std::exception& ex) { reply(event->sender(), new ListenSshConnectionResponse(this, EventError(ex.what()), event->serverHandle, _settings, event->connectionType)); } // When we done with SSH (i.e. rbm_ssh_open_tunnel exits) regardless // of the error or success state, we should cleanup SshTunnelWorker. stopAndDelete(); } /** * @brief Send reply event to object 'receiver' */ void SshTunnelWorker::reply(QObject *receiver, Event *event) { if (_isQuiting) return; AppRegistry::instance().bus()->send(receiver, event); } void SshTunnelWorker::log(const std::string &message, int level) { if (_isQuiting) return; AppRegistry::instance().bus()->send( AppRegistry::instance().app(), new LogEvent(this, message, (LogEvent::LogLevel)level)); } void SshTunnelWorker::logCallbackHandler(void *context, char *message, int level) { static_cast(context)->log(message, level); } /* * SshTunnelConfigCreator */ SshTunnelConfigCreator::SshTunnelConfigCreator(ConnectionSettings *settings) { SshSettings *ssh = settings->sshSettings(); // We are going to access raw char* buffers, so prepare copies // of strings (and rest of the things for symmetry) _sshhost = ssh->host(); _sshport = ssh->port(); _remotehost = settings->serverHost(); _remoteport = settings->serverPort(); _localip = "127.0.0.1"; _localport = 27040; _userName = ssh->userName(); _userPassword = ssh->userPassword(); _privateKeyFile = ssh->privateKeyFile(); _publicKeyFile = ssh->publicKeyFile(); _passphrase = ssh->passphrase(); _authMethod = ssh->authMethod(); // "password" or "publickey" // Use "askedPassword" for both passphrase and password if required if (ssh->askPassword()) { _passphrase = ssh->askedPassword(); _userPassword = ssh->askedPassword(); } _sshConfig = new rbm_ssh_tunnel_config; _sshConfig->sshserverhost = const_cast(_sshhost.c_str()); _sshConfig->sshserverport = static_cast(_sshport); _sshConfig->remotehost = const_cast(_remotehost.c_str()); _sshConfig->remoteport = static_cast(_remoteport); _sshConfig->localip = const_cast(_localip.c_str()); _sshConfig->localport = static_cast(_localport); _sshConfig->username = const_cast(_userName.c_str()); _sshConfig->password = const_cast(_userPassword.c_str()); _sshConfig->privatekeyfile = const_cast(_privateKeyFile.c_str()); _sshConfig->publickeyfile = _publicKeyFile.empty() ? NULL : const_cast(_publicKeyFile.c_str()); _sshConfig->passphrase = const_cast(_passphrase.c_str()); _sshConfig->authtype = (_authMethod == "publickey") ? RBM_SSH_AUTH_TYPE_PUBLICKEY : RBM_SSH_AUTH_TYPE_PASSWORD; // Default settings _sshConfig->logcontext = NULL; _sshConfig->logcallback = NULL; _sshConfig->loglevel = RBM_SSH_LOG_TYPE_ERROR; } SshTunnelConfigCreator::~SshTunnelConfigCreator() { delete _sshConfig; } } ================================================ FILE: src/robomongo/core/mongodb/SshTunnelWorker.h ================================================ #pragma once #include #include #include "robomongo/core/events/MongoEvents.h" QT_BEGIN_NAMESPACE class QThread; QT_END_NAMESPACE struct rbm_ssh_tunnel_config; struct rbm_ssh_session; namespace Robomongo { class ConnectionSettings; class EstablishSshConnectionRequest; class ListenSshConnectionRequest; /* * Helper class for creating rbm_ssh_tunnel_config from ConnectionSettings * Automatically deletes allocated structure */ class SshTunnelConfigCreator { public: explicit SshTunnelConfigCreator(ConnectionSettings *settings); ~SshTunnelConfigCreator(); rbm_ssh_tunnel_config *config() { return _sshConfig; } private: std::string _sshhost; int _sshport; std::string _remotehost; int _remoteport; std::string _localip; int _localport; std::string _userName; std::string _userPassword; std::string _privateKeyFile; std::string _publicKeyFile; std::string _passphrase; std::string _authMethod; // "password" or "publickey" rbm_ssh_tunnel_config* _sshConfig; }; class SshTunnelWorker : public QObject { Q_OBJECT public: explicit SshTunnelWorker(ConnectionSettings *settings); ~SshTunnelWorker(); static void logCallbackHandler(void* context, char *message, int level); protected: void stopAndDelete(); protected Q_SLOTS: // handlers: void handle(EstablishSshConnectionRequest *event); void handle(ListenSshConnectionRequest *event); private: void reply(QObject *receiver, Event *event); void log(const std::string& message, int level = 3); QThread *_thread; QAtomicInteger _isQuiting; ConnectionSettings* _settings; rbm_ssh_session* _sshSession; SshTunnelConfigCreator _configCreator; }; } ================================================ FILE: src/robomongo/core/settings/ConnectionSettings.cpp ================================================ #include "robomongo/core/settings/ConnectionSettings.h" #include #include #include "robomongo/core/settings/CredentialSettings.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/core/settings/SshSettings.h" #include "robomongo/core/settings/SslSettings.h" #include "robomongo/core/utils/QtUtils.h" #include namespace { const unsigned port = 27017; const char *defaultServerHost = "localhost"; const char *defaultNameConnection = "New Connection"; const int maxLength = 300; } namespace Robomongo { /** * Creates ConnectionSettings with default values */ ConnectionSettings::ConnectionSettings(bool isClone) : QObject(), _connectionName(defaultNameConnection), _host(defaultServerHost), _port(port), _imported(false), _sshSettings(new SshSettings()), _sslSettings(new SslSettings()), _isReplicaSet(false), _replicaSetSettings(new ReplicaSetSettings()), _clone(isClone) { } ConnectionSettings::ConnectionSettings(const mongo::MongoURI& uri, bool isClone) : _connectionName(defaultNameConnection), _host(defaultServerHost), _port(port), _imported(false), _sshSettings(new SshSettings()), _sslSettings(new SslSettings()), _isReplicaSet((uri.type() == mongo::ConnectionString::ConnectionType::SET)), _replicaSetSettings(new ReplicaSetSettings(uri)), _clone(isClone) { // Disabling unfinished feature : Export / Import to / from MongoURI /* if (!uri.getServers().empty()) { _host = uri.getServers().front().host(); _port = uri.getServers().front().port(); } auto str = std::string(uri.getOptions().getStringField("ssl")); auto sslEnabled = ("true" == str); if (sslEnabled) { _sslSettings->enableSSL(true); _sslSettings->setAllowInvalidCertificates(true); } auto credential = new CredentialSettings(); credential->setUserName(uri.getUser()); credential->setUserPassword(uri.getPassword()); credential->setDatabaseName(uri.getDatabase()); if (!credential->userName().empty() && !credential->userPassword().empty()) { // todo: credential->setEnabled(true); } addCredential(credential); */ } void ConnectionSettings::fromVariant(const QVariantMap &map) { setConnectionName(QtUtils::toStdString(map.value("connectionName").toString())); setServerHost(QtUtils::toStdString(map.value("serverHost").toString().left(maxLength))); setServerPort(map.value("serverPort").toInt()); setDefaultDatabase(QtUtils::toStdString(map.value("defaultDatabase").toString())); setReplicaSet(map.value("isReplicaSet").toBool()); QVariantList list = map.value("credentials").toList(); for (QVariantList::const_iterator it = list.begin(); it != list.end(); ++it) { QVariant var = *it; CredentialSettings *credential = new CredentialSettings(var.toMap()); addCredential(credential); } if (map.contains("ssh")) { _sshSettings->fromVariant(map.value("ssh").toMap()); } if (map.contains("ssl")) { _sslSettings->fromVariant(map.value("ssl").toMap()); } if (isReplicaSet()) { _replicaSetSettings->fromVariant(map.value("replicaSet").toMap()); } // If UUID has never been created or is empty, create a new one. Otherwise load the existing. if (!map.contains("uuid") || map.value("uuid").toString().isEmpty()) _uuid = QUuid::createUuid().toString(); else _uuid = map.value("uuid").toString(); //#ifdef MONGO_SSL // ,SSLInfo(map.value("sslEnabled").toBool(),QtUtils::toStdString(map.value("sslPemKeyFile").toString())) //#endif } /** * Cleanup used resources */ ConnectionSettings::~ConnectionSettings() { clearCredentials(); } /** * Creates completely new ConnectionSettings by cloning this record. */ ConnectionSettings *ConnectionSettings::clone() const { auto settings = new ConnectionSettings(true); settings->setUuid(_uuid); settings->apply(this); return settings; } /** * Discards current state and applies state from 'source' ConnectionSettings. */ void ConnectionSettings::apply(const ConnectionSettings *source) { setConnectionName(source->connectionName()); setServerHost(source->serverHost()); setServerPort(source->serverPort()); setDefaultDatabase(source->defaultDatabase()); setImported(source->imported()); setReplicaSet(source->isReplicaSet()); clearCredentials(); QList cred = source->credentials(); for (QList::iterator it = cred.begin(); it != cred.end(); ++it) { addCredential((*it)->clone()); } _sshSettings.reset(source->sshSettings()->clone()); _sslSettings.reset(source->sslSettings()->clone()); _replicaSetSettings.reset(source->_replicaSetSettings->clone()); //#ifdef MONGO_SSL // setSslInfo(source->sslInfo()); //#endif } /** * @brief Converts to QVariantMap */ QVariant ConnectionSettings::toVariant() const { QVariantMap map; map.insert("connectionName", QtUtils::toQString(connectionName())); map.insert("serverHost", QtUtils::toQString(serverHost())); map.insert("serverPort", serverPort()); map.insert("defaultDatabase", QtUtils::toQString(defaultDatabase())); map.insert("isReplicaSet", isReplicaSet()); if (isReplicaSet()) map.insert("replicaSet", _replicaSetSettings->toVariant()); #ifdef MONGO_SSL SSLInfo infl = _info.sslInfo(); map.insert("sslEnabled", infl._sslSupport); map.insert("sslPemKeyFile", QtUtils::toQString(infl._sslPEMKeyFile)); #endif QVariantList list; for (auto const& cred : _credentials) list.append(cred->toVariant()); map.insert("credentials", list); map.insert("ssh", _sshSettings->toVariant()); map.insert("ssl", _sslSettings->toVariant()); map.insert("uuid", _uuid); return map; } CredentialSettings *ConnectionSettings::findCredential(const std::string &databaseName) const { CredentialSettings *result = nullptr; for (QList::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) { CredentialSettings *cred = *it; if (cred->databaseName() == databaseName) { result = cred; break; } } return result; } /** * @brief Adds credential to this connection */ void ConnectionSettings::addCredential(CredentialSettings *credential) { if (!findCredential(credential->databaseName())) _credentials.append(credential); } /** * @brief Checks whether this connection has primary credential * which is also enabled. */ bool ConnectionSettings::hasEnabledPrimaryCredential() const { if (_credentials.count() == 0) return false; return primaryCredential()->enabled(); } /** * @brief Returns primary credential */ CredentialSettings *ConnectionSettings::primaryCredential() const { if (_credentials.count() == 0) { _credentials.append(new CredentialSettings()); } return _credentials.at(0); } /** * @brief Clears and releases memory occupied by credentials */ void ConnectionSettings::clearCredentials() { qDeleteAll(_credentials); _credentials.clear(); } std::string ConnectionSettings::getFullAddress() const { return hostAndPort().toString(); } mongo::HostAndPort ConnectionSettings::hostAndPort() const { // If it doesn't look like IPv6 address, // treat it like IPv4 or literal hostname if (_host.find(':') == std::string::npos) { return mongo::HostAndPort(_host, _port); } // The following code assumes, that it is IPv6 address // If address contains square brackets ("["), remove them: std::string hostCopy = _host; if (_host.find('[') != std::string::npos) { boost::erase_all(hostCopy, "["); boost::erase_all(hostCopy, "]"); } return mongo::HostAndPort(hostCopy, _port); } } ================================================ FILE: src/robomongo/core/settings/ConnectionSettings.h ================================================ #pragma once #include #include #include #include #include #include namespace Robomongo { class CredentialSettings; class SshSettings; class SslSettings; class ReplicaSetSettings; /** * @brief Represents connection record */ class ConnectionSettings : public QObject { Q_OBJECT public: /** * @brief Creates ConnectionSettings with default values */ ConnectionSettings(bool isClone); /** * @brief Creates ConnectionSettings from mongo connection string URI */ ConnectionSettings(const mongo::MongoURI& uri, bool isClone); explicit ConnectionSettings(QVariantMap map, bool isClone); /** * @brief Cleanup used resources */ ~ConnectionSettings(); /** * @brief Creates completely new ConnectionSettings by cloning this record. */ ConnectionSettings *clone() const; /** * @brief Discards current state and applies state from 'source' ConnectionSettings. */ void apply(const ConnectionSettings *source); /** * @brief Converts to QVariantMap */ QVariant toVariant() const; void fromVariant(const QVariantMap &map); /** * @brief Name of connection */ std::string connectionName() const { return _connectionName; } void setConnectionName(const std::string &connectionName) { _connectionName = connectionName; } /** * @brief Server host */ std::string serverHost() const { return _host; } void setServerHost(const std::string &serverHost) { _host = serverHost; } /** * @brief Port of server */ int serverPort() const { return _port; } void setServerPort(const int port) { _port = port; } /** * @brief Default database */ std::string defaultDatabase() const { return _defaultDatabase; } void setDefaultDatabase(const std::string &defaultDatabase) { _defaultDatabase = defaultDatabase; } /** * Was this connection imported from somewhere? */ bool imported() const { return _imported; } void setImported(bool imported) { _imported = imported; } /** * @brief Adds credential to this connection */ void addCredential(CredentialSettings *credential); /** * @brief Clears and releases memory occupied by credentials */ void clearCredentials(); /** * @brief Checks whether this connection has primary credential * which is also enabled. */ bool hasEnabledPrimaryCredential() const; /** * @brief Returns primary credential */ CredentialSettings *primaryCredential() const; /** * @brief Returns number of credentials */ int credentialCount() const { return _credentials.size(); } /** * @brief Returns all credentials */ QList credentials() const { return _credentials; } /** * @brief Checks that auth required */ /*bool isAuthNeeded() const { bool userSpecified = !_userName.isEmpty(); bool passwordSpecified = !_userPassword.isEmpty(); return (userSpecified || passwordSpecified); }*/ /** * @brief Returns connection full address (i.e. locahost:8090) */ std::string getFullAddress() const; std::string getReadableName() const { if (_connectionName.empty()) return getFullAddress(); return _connectionName; } mongo::HostAndPort hostAndPort() const; SshSettings *sshSettings() const { return _sshSettings.get(); } SslSettings *sslSettings() const { return _sslSettings.get(); } ReplicaSetSettings *replicaSetSettings() const { return _replicaSetSettings.get(); } bool isReplicaSet() const { return _isReplicaSet; } void setReplicaSet(bool flag) { _isReplicaSet = flag; } QString uuid() const { return _uuid; } void setUuid(QString const& uuid) { _uuid = uuid; } private: CredentialSettings *findCredential(const std::string &databaseName) const; std::string _connectionName; std::string _host; int _port; std::string _defaultDatabase; mutable QList _credentials; std::unique_ptr _sshSettings; std::unique_ptr _sslSettings; bool _isReplicaSet; std::unique_ptr _replicaSetSettings; // Was this connection imported from somewhere? bool _imported; // Flag to check if this is a clone(copy) or original ConnectionSettings // Note: If this is not a clone connection settings, this object is original connection // ConnectionSettings object which is loaded/saved into Robomongo config. file. bool _clone = false; // If this is a clone connection settings, unique ID will be used to identify from which // original connection settings this object is cloned. // -1 for invalid(uninitialized) unique ID which should not be seen in theory int _uniqueId = -1; // UUID string taken from QUuid. // It is used to identify the unique ID of a connection settings object QString _uuid; }; } Q_DECLARE_METATYPE(Robomongo::ConnectionSettings *) ================================================ FILE: src/robomongo/core/settings/CredentialSettings.cpp ================================================ #include "robomongo/core/settings/CredentialSettings.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/utils/RoboCrypt.h" namespace Robomongo { CredentialSettings::CredentialSettings() : _userName(), _userPassword(), _databaseName(), _mechanism(), _enabled(false), _useManuallyVisibleDbs(false), _manuallyVisibleDbs() { } CredentialSettings::CredentialSettings(const QVariantMap &map) : _userName(QtUtils::toStdString(map.value("userName").toString())), _databaseName(QtUtils::toStdString(map.value("databaseName").toString())), _mechanism(QtUtils::toStdString(map.value("mechanism").toString())), _useManuallyVisibleDbs(map.value("useManuallyVisibleDbs").toBool()), _manuallyVisibleDbs(QtUtils::toStdString(map.value("manuallyVisibleDbs").toString())), _enabled(map.value("enabled").toBool()) { // From version Robo 1.3 "userPasswordEncrypted" is used instead of "userPassword" in config. file if(map.contains("userPassword")) // Robo 1.2 and below _userPassword = map.value("userPassword").toString().toStdString(); else if(map.contains("userPasswordEncrypted")) // From Robo 1.3 _userPassword = RoboCrypt::decrypt(map.value("userPasswordEncrypted").toString().toStdString()); } /** * @brief Clones credential settings. */ CredentialSettings *CredentialSettings::clone() const { CredentialSettings *cloned = new CredentialSettings(*this); return cloned; } QVariant CredentialSettings::toVariant() const { QVariantMap map; map.insert("userName", QtUtils::toQString(userName())); map.insert("userPasswordEncrypted", userPassword().empty() ? "" : QtUtils::toQString(RoboCrypt::encrypt(userPassword()))); map.insert("databaseName", QtUtils::toQString(databaseName())); map.insert("mechanism", QtUtils::toQString(mechanism())); map.insert("useManuallyVisibleDbs", _useManuallyVisibleDbs); map.insert("manuallyVisibleDbs", QtUtils::toQString(_manuallyVisibleDbs)); map.insert("enabled", enabled()); return map; } } ================================================ FILE: src/robomongo/core/settings/CredentialSettings.h ================================================ #pragma once #include #include #include namespace Robomongo { class CredentialSettings { public: CredentialSettings(); explicit CredentialSettings(const QVariantMap &map); /** * @brief Clones credential settings. */ CredentialSettings *clone() const; /** * @brief Converts to QVariantMap */ QVariant toVariant() const; /** * @brief User name */ std::string userName() const { return _userName; } void setUserName(const std::string &userName) { _userName = userName; } /** * @brief Password */ std::string userPassword() const { return _userPassword; } void setUserPassword(const std::string &userPassword) { _userPassword = userPassword; } /** * @brief Database name, on which authentication performed */ std::string databaseName() const { return _databaseName.empty() ? "admin" : _databaseName; } void setDatabaseName(const std::string &databaseName) { _databaseName = databaseName; } /** * @brief Authentication mechanism (SCRAM-SHA-1, SCRAM-SHA-256 or MONGODB-CR) */ std::string mechanism() const { return _mechanism.empty() ? "SCRAM-SHA-1" : _mechanism; } void setMechanism(const std::string &mechanism) { _mechanism = mechanism; } /** * @brief Flag, indecating whether we should use this * credentials to perform authentication, or not. */ bool enabled() const { return _enabled; } void setEnabled(bool enabled) { _enabled = enabled; } bool useManuallyVisibleDbs() const { return _useManuallyVisibleDbs; } void setUseManuallyVisibleDbs(bool enabled) { _useManuallyVisibleDbs = enabled; } std::string manuallyVisibleDbs() const { return _manuallyVisibleDbs; } void setManuallyVisibleDbs(std::string const& dbs) { _manuallyVisibleDbs = dbs; } private: std::string _userName; std::string _userPassword; // clear text password (decrypted) std::string _databaseName; std::string _mechanism; // authentication mechanism (SCRAM-SHA-1, SCRAM-SHA-256 or MONGODB-CR) bool _useManuallyVisibleDbs; std::string _manuallyVisibleDbs; /** * @brief Flag, indecating whether we should use this * credentials to perform authentication, or not. */ bool _enabled; }; } ================================================ FILE: src/robomongo/core/settings/ReplicaSetSettings.cpp ================================================ #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { ReplicaSetSettings::ReplicaSetSettings() : _cachedSetName(""), _members(), _readPreference(ReadPreference::PRIMARY) {} ReplicaSetSettings::ReplicaSetSettings(const mongo::MongoURI& uri) : ReplicaSetSettings() { _cachedSetName = uri.getSetName(); for (auto const& server : uri.getServers()) { _members.push_back(server.host() + ":" + std::to_string(server.port())); } } ReplicaSetSettings *ReplicaSetSettings::clone() const { ReplicaSetSettings *cloned = new ReplicaSetSettings(*this); return cloned; } QVariant ReplicaSetSettings::toVariant() const { QVariantMap map; map.insert("setNameUserEntered", QString::fromStdString(_setNameUserEntered)); map.insert("cachedSetName", QString::fromStdString(_cachedSetName)); int idx = 0; for (std::string const& str : _members) { map.insert(QString::number(idx), QtUtils::toQString(str)); ++idx; } map.insert("readPreference", static_cast(readPreference())); return map; } void ReplicaSetSettings::fromVariant(const QVariantMap &map) { setSetNameUserEntered(map.value("setNameUserEntered").toString().toStdString()); setCachedSetName(map.value("cachedSetName").toString().toStdString()); // Extract and set replica members std::vector vec; auto itr = map.begin(); int idx = 0; do { itr = map.find(QString::number(idx)); if (map.end() == itr) break; vec.push_back(itr->toString().toStdString()); ++idx; } while (map.end() != itr); setMembers(vec); // Extract and set read reference setReadPreference(static_cast(map.value("readPreference").toInt())); } void ReplicaSetSettings::setMembers(const std::vector& members) { _members.clear(); for (auto const& member : members) _members.push_back(member); } void ReplicaSetSettings::setMembers(const std::vector>& membersAndHealts) { _members.clear(); for (auto const& memberAndHealth : membersAndHealts) _members.push_back(memberAndHealth.first); } std::vector ReplicaSetSettings::membersToHostAndPort() const { std::vector membersHostAndPort; for (auto const& member : _members) membersHostAndPort.push_back(mongo::HostAndPort(member)); return membersHostAndPort; } } ================================================ FILE: src/robomongo/core/settings/ReplicaSetSettings.h ================================================ #pragma once #include #include #include #include #include namespace Robomongo { class ReplicaSetSettings { public: enum class ReadPreference { PRIMARY = 0, PRIMARY_PREFERRED = 1 // todo: Add all supported read preferences }; ReplicaSetSettings(); ReplicaSetSettings(const mongo::MongoURI& uri); /** * Clones Replica Set settings. */ ReplicaSetSettings* clone() const; /** * Converts to QVariantMap */ QVariant toVariant() const; void fromVariant(const QVariantMap &map); // Getters std::string const& setNameUserEntered() { return _setNameUserEntered; } std::string const& cachedSetName() { return _cachedSetName; } std::vector const& members() const { return _members; } std::vector membersToHostAndPort() const; std::set membersToHostAndPortAsSet() const { auto const& members = membersToHostAndPort(); return std::set(members.begin(), members.end()); } ReadPreference readPreference() const { return _readPreference; } // Setters void setSetNameUserEntered(const std::string& setName) { _setNameUserEntered = setName; } void setCachedSetName(const std::string& setName) { _cachedSetName = setName; } void setMembers(const std::vector& members); void setMembers(const std::vector>& membersAndHealts); void deleteAllMembers() { _members.clear(); } void setReadPreference(ReadPreference readPreference) { _readPreference = readPreference; } private: std::string _setNameUserEntered; std::string _cachedSetName; std::vector _members; ReadPreference _readPreference = ReadPreference::PRIMARY; }; } ================================================ FILE: src/robomongo/core/settings/SettingsManager.cpp ================================================ #include "robomongo/core/settings/SettingsManager.h" #include #include #include #include #include #include #include #include #include #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/CredentialSettings.h" #include "robomongo/core/settings/SshSettings.h" #include "robomongo/core/settings/SslSettings.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/utils/StdUtils.h" #include "robomongo/gui/AppStyle.h" #include "robomongo/utils/common.h" #include "robomongo/utils/qzip/qzipreader_p.h" #include "robomongo/utils/RoboCrypt.h" namespace Robomongo { // 3T config files auto const Studio3T_PropertiesDat { QString("%1/.3T/studio-3t/properties.dat").arg(QDir::homePath()) }; auto const DataMongodb_PropertiesDat { QString("%1/.3T/data-man-mongodb/properties.dat").arg(QDir::homePath()) }; auto const MongoChefPro_PropertiesDat { QString("%1/.3T/mongochef-pro/properties.dat").arg(QDir::homePath()) }; auto const MongoChefEnt_PropertiesDat { QString("%1/.3T/mongochef-enterprise/properties.dat").arg(QDir::homePath()) }; const std::vector> S_3T_ZipFile_And_ConfigFile_List { { Studio3T_PropertiesDat, "Studio3T.properties" }, { DataMongodb_PropertiesDat, "3T.data-man-mongodb.properties" }, { MongoChefPro_PropertiesDat, "3T.mongochef-pro.properties" }, { MongoChefEnt_PropertiesDat, "3T.mongochef-enterprise.properties" } }; // Extract zipFile and find the value of "anonymousID" field in propFile QString extractAnonymousIDFromZip(QString const& zipFile, QString const& propfile); // Extract "anonymousID" from a config file QString extractAnonymousID(QString const& configFile); /** * @brief Version of schema */ const QString SchemaVersion = "2.0"; const auto CONFIG_FILE_0_8_5 { QString("%1/.config/robomongo/robomongo.json").arg(QDir::homePath()) }; const auto CONFIG_FILE_1_0_RC1 { QString("%1/.config/robomongo/1.0/robomongo.json").arg(QDir::homePath()) }; const auto CONFIG_FILE_1_1_0_BETA { QString("%1/.3T/robomongo/1.1.0-Beta/robomongo.json").arg(QDir::homePath()) }; /** * @brief Robomongo config. files of old versions */ // Important: In order to import connections from a version, config. file path must // be defined and placed into the vector initializer list below in order. std::vector const SettingsManager::_configFilesOfOldVersions { QString("%1/.3T/robo-3t/1.4.3/robo3t.json").arg(QDir::homePath()), // CONFIG_FILE_1_4_3 QString("%1/.3T/robo-3t/1.4.2/robo3t.json").arg(QDir::homePath()), // CONFIG_FILE_1_4_2 QString("%1/.3T/robo-3t/1.4.1/robo3t.json").arg(QDir::homePath()), // CONFIG_FILE_1_4_1 QString("%1/.3T/robo-3t/1.4.0/robo3t.json").arg(QDir::homePath()), // CONFIG_FILE_1_4_0 QString("%1/.3T/robo-3t/1.3.1/robo3t.json").arg(QDir::homePath()), // CONFIG_FILE_1_3_1 QString("%1/.3T/robo-3t/1.3.0/robo3t.json").arg(QDir::homePath()), // CONFIG_FILE_1_3_0_BETA QString("%1/.3T/robo-3t/1.2.1/robo3t.json").arg(QDir::homePath()), // CONFIG_FILE_1_2_1 QString("%1/.3T/robo-3t/1.2.0/robo3t.json").arg(QDir::homePath()), // CONFIG_FILE_1_2_0_BETA QString("%1/.3T/robo-3t/1.1.1/robo3t.json").arg(QDir::homePath()), // CONFIG_FILE_1_1_1 CONFIG_FILE_1_1_0_BETA, // CONFIG_FILE_1_1_0_BETA QString("%1/.3T/robomongo/1.0.0/robomongo.json").arg(QDir::homePath()), // CONFIG_FILE_1_0_0 CONFIG_FILE_1_0_RC1, // CONFIG_FILE_1_0_RC1 QString("%1/.config/robomongo/0.9/robomongo.json").arg(QDir::homePath()), // CONFIG_FILE_0_9 CONFIG_FILE_0_8_5 // CONFIG_FILE_0_8_5 }; std::vector SettingsManager::_connections; /** * Creates SettingsManager for config file in default location * ~/.config/robomongo/robomongo.json */ SettingsManager::SettingsManager() : _version(SchemaVersion), _uuidEncoding(DefaultEncoding), _timeZone(Utc), _viewMode(Robomongo::Tree), _autocompletionMode(AutocompleteAll), _loadMongoRcJs(false), _minimizeToTray(false), _lineNumbers(false), _disableConnectionShortcuts(false), _batchSize(50), _textFontFamily(""), _textFontPointSize(-1), _mongoTimeoutSec(10), _shellTimeoutSec(15), _imported(false) { if (!QDir().mkpath(ConfigDir)) LOG_MSG("ERROR: Could not create settings path: " + ConfigDir, mongo::logger::LogSeverity::Error()); RoboCrypt::initKey(); if (!load()) { // if load fails (probably due to non-existing config. file or directory) save(); // create empty settings file load(); // try loading again for the purpose of import from previous Robomongo versions } LOG_MSG("SettingsManager initialized in " + ConfigFilePath, mongo::logger::LogSeverity::Info(), false); } SettingsManager::~SettingsManager() { std::for_each(_connections.begin(), _connections.end(), stdutils::default_delete()); } /** * Load settings from config file. * @return true if success, false otherwise */ bool SettingsManager::load() { if (!QFile::exists(ConfigFilePath)) return false; QFile f(ConfigFilePath); if (!f.open(QIODevice::ReadOnly)) return false; bool ok; QJson::Parser parser; QVariantMap map = parser.parse(f.readAll(), &ok).toMap(); if (!ok) return false; loadFromMap(map); return true; } /** * Saves all settings to config file. * @return true if success, false otherwise */ bool SettingsManager::save() { QVariantMap const& map = convertToMap(); QFile f(ConfigFilePath); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { LOG_MSG("ERROR: Could not write settings to: " + ConfigFilePath, mongo::logger::LogSeverity::Error()); return false; } bool ok; QJson::Serializer s; s.setIndentMode(QJson::IndentFull); s.serialize(map, &f, &ok); LOG_MSG("Settings saved to: " + ConfigFilePath, mongo::logger::LogSeverity::Info()); return ok; } void SettingsManager::addCacheData(QString const& key, QVariant const& value) { _cacheData.insert(key, value); } QVariant SettingsManager::cacheData(QString const& key) const { return _cacheData.value(key); } /** * Load settings from the map. Existings settings will be overwritten. */ void SettingsManager::loadFromMap(QVariantMap &map) { // 1. Load version _version = map.value("version").toString(); // 2. Load UUID encoding int encoding = map.value("uuidEncoding").toInt(); if (encoding > 3 || encoding < 0) encoding = 0; _uuidEncoding = (UUIDEncoding)encoding; // 3. Load view mode if (map.contains("viewMode")) { int viewMode = map.value("viewMode").toInt(); if (viewMode > 2 || viewMode < 0) viewMode = Custom; // Default View Mode _viewMode = (ViewMode)viewMode; } else { _viewMode = Custom; // Default View Mode } _autoExpand = map.contains("autoExpand") ? map.value("autoExpand").toBool() : true; _autoExec = map.contains("autoExec") ? map.value("autoExec").toBool() : true; _minimizeToTray = map.contains("minimizeToTray") ? map.value("minimizeToTray").toBool() : false; _lineNumbers = map.contains("lineNumbers") ? map.value("lineNumbers").toBool() : false; _imported = map.contains("imported") ? map.value("imported").toBool() : false; _programExitedNormally = map.contains("programExitedNormally") ? map.value("programExitedNormally").toBool() : true; _disableHttpsFeatures = map.contains("disableHttpsFeatures") ? map.value("disableHttpsFeatures").toBool() : false; _debugMode = map.contains("debugMode") ? map.value("debugMode").toBool() : false; // 4. Load TimeZone int timeZone = map.value("timeZone").toInt(); if (timeZone > 1 || timeZone < 0) timeZone = 0; _timeZone = (SupportedTimes)timeZone; _loadMongoRcJs = map.value("loadMongoRcJs").toBool(); _disableConnectionShortcuts = map.value("disableConnectionShortcuts").toBool(); if (map.contains("acceptedEulaVersions")) _acceptedEulaVersions = map.value("acceptedEulaVersions").toStringList().toSet(); if (map.contains("dbVersionsConnected")) _dbVersionsConnected = map.value("dbVersionsConnected").toStringList().toSet(); // Load anonymousID _anonymousID = getOrCreateAnonymousID(map); // Load AutocompletionMode if (map.contains("autocompletionMode")) { int autocompletionMode = map.value("autocompletionMode").toInt(); if (autocompletionMode < 0 || autocompletionMode > 2) autocompletionMode = AutocompleteAll; // Default Mode _autocompletionMode = (AutocompletionMode)autocompletionMode; } else { _autocompletionMode = AutocompleteAll; // Default Mode } // Load Batch Size _batchSize = map.value("batchSize").toInt(); if (_batchSize == 0) _batchSize = 50; if (map.contains("checkForUpdates")) _checkForUpdates = map.value("checkForUpdates").toBool(); _currentStyle = map.value("style").toString(); if (_currentStyle.isEmpty()) { _currentStyle = AppStyle::StyleName; } // Load font information _textFontFamily = map.value("textFontFamily").toString(); _textFontPointSize = map.value("textFontPointSize").toInt(); if (map.contains("mongoTimeoutSec")) { _mongoTimeoutSec = map.value("mongoTimeoutSec").toInt(); } if (map.contains("shellTimeoutSec")) { _shellTimeoutSec = map.value("shellTimeoutSec").toInt(); } // 5. Load connections _connections.clear(); QVariantList const& list = map.value("connections").toList(); for (auto const& conn : list) { auto connSettings = new ConnectionSettings(false); connSettings->fromVariant(conn.toMap()); addConnection(connSettings); } _toolbars = map.value("toolbars").toMap(); ToolbarSettingsContainerType::const_iterator it = _toolbars.find("connect"); if (_toolbars.end() == it) _toolbars["connect"] = true; it = _toolbars.find("open_save"); if (_toolbars.end() == it) _toolbars["open_save"] = true; it = _toolbars.find("exec"); if (_toolbars.end() == it) _toolbars["exec"] = true; it = _toolbars.find("explorer"); if (_toolbars.end() == it) _toolbars["explorer"] = true; it = _toolbars.find("logs"); if (_toolbars.end() == it) _toolbars["logs"] = false; _cacheData = map.value("cacheData").toMap(); // Load connection settings from previous versions of Robomongo importFromOldVersion(); } /** * Save all settings to map. */ QVariantMap SettingsManager::convertToMap() const { QVariantMap map; // 1. Save schema version map.insert("version", SchemaVersion); // 2. Save UUID encoding map.insert("uuidEncoding", _uuidEncoding); // 3. Save TimeZone encoding map.insert("timeZone", _timeZone); // 4. Save view mode map.insert("viewMode", _viewMode); map.insert("autoExpand", _autoExpand); map.insert("lineNumbers", _lineNumbers); // 5. Save Autocompletion mode map.insert("autocompletionMode", _autocompletionMode); // 6. Save loadInitJs map.insert("loadMongoRcJs", _loadMongoRcJs); // 7. Save disableConnectionShortcuts map.insert("disableConnectionShortcuts", _disableConnectionShortcuts); // 8. Save acceptedEulaVersions array QJsonArray arr; for (auto const& str : _acceptedEulaVersions) arr.push_back(str); map.insert("acceptedEulaVersions", arr.toVariantList()); // x. Save unique set of db versions connected QJsonArray dbVersionsArr; for (auto const& version : _dbVersionsConnected) dbVersionsArr.push_back(version); map.insert("dbVersionsConnected", dbVersionsArr.toVariantList()); // 9. Save batchSize map.insert("batchSize", _batchSize); map.insert("checkForUpdates", _checkForUpdates); map.insert("mongoTimeoutSec", _mongoTimeoutSec); map.insert("shellTimeoutSec", _shellTimeoutSec); // 10. Save style map.insert("style", _currentStyle); // 11. Save font information map.insert("textFontFamily", _textFontFamily); map.insert("textFontPointSize", _textFontPointSize); // 12. Save connections QVariantList list; for (auto const conn : _connections) list.append(conn->toVariant().toMap()); map.insert("connections", list); map.insert("autoExec", _autoExec); map.insert("minimizeToTray", _minimizeToTray); map.insert("toolbars", _toolbars); map.insert("imported", _imported); map.insert("anonymousID", _anonymousID); map.insert("cacheData", _cacheData); map.insert("programExitedNormally", _programExitedNormally); map.insert("disableHttpsFeatures", _disableHttpsFeatures); map.insert("debugMode", _debugMode); return map; } QString SettingsManager::getOrCreateAnonymousID(QVariantMap const& map) const { QString anonymousID = ""; // If anonymousID has never been created or is empty, create a new one. Otherwise load the existing. if (map.contains("anonymousID")) { QUuid id = map.value("anonymousID").toString(); if (!id.isNull()) anonymousID = id.toString(); } // Search and import "anonymousID" from other Studio 3T config files for (auto const& zipFileAndConfigFile : S_3T_ZipFile_And_ConfigFile_List) { if (!anonymousID.isEmpty()) break; QUuid const& id = extractAnonymousIDFromZip(zipFileAndConfigFile.first, zipFileAndConfigFile.second); if (!id.isNull()) anonymousID = id.toString(); } // Search and import "anonymousID" from other Robo 3T old config files starting from latest for (auto const& oldConfigFile : _configFilesOfOldVersions) { if (!anonymousID.isEmpty()) break; // Don't import from 1.1-Beta due to a problem where Beta might have redundantly created new UUID if (oldConfigFile == CONFIG_FILE_1_1_0_BETA) continue; // Stop searching in 1_0_RC1 or older versions, "anonymousID" is introduced in version 1.0 if (oldConfigFile == CONFIG_FILE_1_0_RC1) break; anonymousID = extractAnonymousID(oldConfigFile); } // Search and import "anonymousID" from any other (ideally newer version) Robo 3T config files if (anonymousID.isEmpty()) { auto const dir1 = QString("%1/.3T/robo-3t").arg(QDir::homePath()); QDirIterator iter1 { dir1, QStringList() << "robo*.json", QDir::Files, QDirIterator::Subdirectories }; while (iter1.hasNext()) { anonymousID = extractAnonymousID(iter1.next()); if (!anonymousID.isEmpty()) break; } if (anonymousID.isEmpty()) { auto const dir2 = QString("%1/.3T/robomongo").arg(QDir::homePath()); QDirIterator iter2 { dir2, QStringList() << "robo*.json", QDir::Files, QDirIterator::Subdirectories }; while (iter2.hasNext()) { anonymousID = extractAnonymousID(iter2.next()); if (!anonymousID.isEmpty()) break; } } } // Couldn't find/import any, create a new anonymousID if (anonymousID.isEmpty()) anonymousID = QUuid::createUuid().toString(); anonymousID.remove('{'); anonymousID.remove('}'); return anonymousID; } /** * Adds connection to the end of list and set it's uniqueID */ void SettingsManager::addConnection(ConnectionSettings *connection) { _connections.push_back(connection); } /** * Removes connection by index */ void SettingsManager::removeConnection(ConnectionSettings *connection) { ConnectionSettingsContainerType::iterator it = std::find(_connections.begin(), _connections.end(), connection); if (it != _connections.end()) { _connections.erase(it); delete connection; } } ConnectionSettings* SettingsManager::getConnectionSettingsByUuid(QString const& uuid) const { for (auto const connSettings : _connections){ if (connSettings->uuid() == uuid) return connSettings; } LOG_MSG("Failed to find connection settings object by UUID.", mongo::logger::LogSeverity::Warning()); return nullptr; } ConnectionSettings* SettingsManager::getConnectionSettingsByUuid(std::string const& uuid) const { return getConnectionSettingsByUuid(QString::fromStdString(uuid)); } void SettingsManager::setCurrentStyle(const QString& style) { _currentStyle = style; } void SettingsManager::setTextFontFamily(const QString& fontFamily) { _textFontFamily = fontFamily; } void SettingsManager::setTextFontPointSize(int pointSize) { _textFontPointSize = pointSize > 0 ? pointSize : -1; } void SettingsManager::reorderConnections(const ConnectionSettingsContainerType &connections) { _connections = connections; } void SettingsManager::setToolbarSettings(const QString toolbarName, const bool visible) { _toolbars[toolbarName] = visible; } void SettingsManager::importFromOldVersion() { if (_imported) return; // Import only from the latest version for (auto const& configFile : _configFilesOfOldVersions) { if (QFile::exists(configFile)) { importFromFile(configFile); setImported(true); return; } } } bool SettingsManager::importConnectionsFrom_0_8_5() { // Load old configuration file (used till version 0.8.5) if (!QFile::exists(CONFIG_FILE_0_8_5)) return false; QFile oldConfigFile(CONFIG_FILE_0_8_5); if (!oldConfigFile.open(QIODevice::ReadOnly)) return false; bool ok; QJson::Parser parser; QVariantMap vmap = parser.parse(oldConfigFile.readAll(), &ok).toMap(); if (!ok) return false; QVariantList vconns = vmap.value("connections").toList(); for (QVariantList::iterator itconn = vconns.begin(); itconn != vconns.end(); ++itconn) { QVariantMap vconn = (*itconn).toMap(); auto conn = new ConnectionSettings(false); conn->setImported(true); conn->setConnectionName(QtUtils::toStdString(vconn.value("connectionName").toString())); conn->setServerHost(QtUtils::toStdString(vconn.value("serverHost").toString().left(300))); conn->setServerPort(vconn.value("serverPort").toInt()); conn->setDefaultDatabase(QtUtils::toStdString(vconn.value("defaultDatabase").toString())); // SSH settings if (vconn.contains("sshAuthMethod")) { SshSettings *ssh = conn->sshSettings(); ssh->setHost(QtUtils::toStdString(vconn.value("sshHost").toString())); ssh->setUserName(QtUtils::toStdString(vconn.value("sshUserName").toString())); ssh->setPort(vconn.value("sshPort").toInt()); ssh->setUserPassword(QtUtils::toStdString(vconn.value("sshUserPassword").toString())); ssh->setPublicKeyFile(QtUtils::toStdString(vconn.value("sshPublicKey").toString())); ssh->setPrivateKeyFile(QtUtils::toStdString(vconn.value("sshPrivateKey").toString())); ssh->setPassphrase(QtUtils::toStdString(vconn.value("sshPassphrase").toString())); int const auth = vconn.value("sshAuthMethod").toInt(); ssh->setEnabled(auth == 1 || auth == 2); ssh->setAuthMethod(auth == 2 ? "publickey" : "password"); } // SSL settings if (vconn.contains("sshEnabled")) { SslSettings *ssl = conn->sslSettings(); ssl->enableSSL(vconn.value("enabled").toBool()); ssl->setPemKeyFile(QtUtils::toStdString(vconn.value("sslPemKeyFile").toString())); } // Credentials QVariantList vcreds = vconn.value("credentials").toList(); for (QVariantList::const_iterator itcred = vcreds.begin(); itcred != vcreds.end(); ++itcred) { QVariantMap vcred = (*itcred).toMap(); auto cred = new CredentialSettings(); cred->setUserName(vcred.value("userName").toString().toStdString()); cred->setUserPassword(vcred.value("userPassword").toString().toStdString()); cred->setDatabaseName(vcred.value("databaseName").toString().toStdString()); cred->setMechanism("MONGODB-CR"); cred->setUseManuallyVisibleDbs(vcred.value("useManuallyVisibleDbs").toBool()); cred->setManuallyVisibleDbs(vcred.value("manuallyVisibleDbs").toString().toStdString()); cred->setEnabled(vcred.value("enabled").toBool()); conn->addCredential(cred); } // Check that we didn't have similar connection bool matched = false; for (std::vector::const_iterator it = _connections.begin(); it != _connections.end(); ++it) { ConnectionSettings *econn = *it; // Existing connection if (conn->serverPort() != econn->serverPort() || conn->serverHost() != econn->serverHost() || conn->defaultDatabase() != econn->defaultDatabase()) continue; CredentialSettings *cred = conn->primaryCredential(); CredentialSettings *ecred = econn->primaryCredential(); if (cred->databaseName() != ecred->databaseName() || cred->userName() != ecred->userName() || cred->userPassword() != ecred->userPassword() || cred->enabled() != ecred->enabled()) continue; SshSettings *ssh = conn->sshSettings(); SshSettings *essh = econn->sshSettings(); if (ssh->enabled() != essh->enabled() || ssh->port() != essh->port() || ssh->host() != essh->host() || ssh->privateKeyFile() != essh->privateKeyFile() || ssh->userPassword() != essh->userPassword() || ssh->userName() != essh->userName()) continue; matched = true; break; } // Import connection only if we didn't find similar one if (!matched) addConnection(conn); } return true; } bool SettingsManager::importFromFile(QString const& oldConfigFilePath) { if (oldConfigFilePath == CONFIG_FILE_0_8_5) { importConnectionsFrom_0_8_5(); return true; } if (!QFile::exists(oldConfigFilePath)) return false; QFile oldConfigFile(oldConfigFilePath); if (!oldConfigFile.open(QIODevice::ReadOnly)) return false; bool ok; QJson::Parser parser; QVariantMap vmap = parser.parse(oldConfigFile.readAll(), &ok).toMap(); if (!ok) return false; //// Import keys _autoExpand = vmap.value("autoExpand").toBool(); _lineNumbers = vmap.value("lineNumbers").toBool(); _debugMode = vmap.value("debugMode").toBool(); _shellTimeoutSec = vmap.value("shellTimeoutSec").toInt(); //// Import connections for (auto const& vcon : vmap.value("connections").toList()) { QVariantMap const& vconn = vcon.toMap(); auto connSettings = new ConnectionSettings(false); connSettings->fromVariant(vconn); connSettings->setImported(true); addConnection(connSettings); } return true; } int SettingsManager::importedConnectionsCount() { return (std::count_if(_connections.cbegin(), _connections.cend(), [](auto conn) { return conn->imported(); } )); } QString extractAnonymousIDFromZip(QString const& zipFile, QString const& propfile) { QZipReader zipReader(zipFile); if (!zipReader.exists() || !zipReader.isReadable()) return QString(""); QXmlStreamReader reader(zipReader.fileData(propfile)); while (!reader.atEnd()) { reader.readNext(); if (reader.text().toString() == "AnonymousID") { reader.readNext(); reader.readNext(); reader.readNext(); reader.readNext(); return reader.text().toString(); } } return QString(""); } QString extractAnonymousID(QString const& configFilePath) { if (!QFile::exists(configFilePath)) return QString(""); QFile oldConfigFile(configFilePath); if (!oldConfigFile.open(QIODevice::ReadOnly)) return QString(""); bool ok = false; QJson::Parser parser; QVariantMap const& map = parser.parse(oldConfigFile.readAll(), &ok).toMap(); if (!ok) return QString(""); QString anonymousID; if (map.contains("anonymousID")) { QUuid const& id = map.value("anonymousID").toString(); if (!id.isNull()) anonymousID = id.toString(); } anonymousID.remove('{'); anonymousID.remove('}'); return anonymousID; } } ================================================ FILE: src/robomongo/core/settings/SettingsManager.h ================================================ #pragma once #include #include #include #include #include #include #include "robomongo/core/Enums.h" namespace Robomongo { class ConnectionSettings; struct ConfigFileAndImportFunction; // Current cache directory auto const CacheDir = QString("%1/.3T/robo-3t/%2/cache/").arg(QDir::homePath()) .arg(PROJECT_VERSION); // Current config file auto const ConfigFilePath = QString("%1/.3T/robo-3t/%2/robo3t.json").arg(QDir::homePath()) .arg(PROJECT_VERSION); // Current config file directory auto const ConfigDir = QString("%1/.3T/robo-3t/%2/").arg(QDir::homePath()) .arg(PROJECT_VERSION); /* ----------------------------- SettingsManager ------------------------------ */ /** * @brief SettingsManager gives you access to all settings, that is used * by Robomongo. It can load() and save() them. Config file usually * located here: ~/.config/robomongo/robomongo.json * * You can access this manager via: * AppRegistry::instance().settingsManager() * * @threadsafe no */ class SettingsManager { public: typedef std::vector ConnectionSettingsContainerType; typedef QMap ToolbarSettingsContainerType; /** * @brief Creates SettingsManager for config file in default location * (usually ~/.config/robomongo/robomongo.json) */ SettingsManager(); /** * @brief Cleanup owned objects */ ~SettingsManager(); /** * @brief Load settings from config file. * @return true if success, false otherwise */ bool load(); /** * @brief Saves all settings to config file. * @return true if success, false otherwise */ bool save(); /** * @brief Adds connection to the end of list. * Connection now will be owned by SettingsManager. */ static void addConnection(ConnectionSettings *connection); /** * @brief Removes connection by index */ void removeConnection(ConnectionSettings *connection); /** * @brief Finds and returns original (non-clone) connection settings which is * loaded/saved from/into Robomongo config. file. * @return If uniqueID is valid returns original connection settings, * nullptr otherwise. */ ConnectionSettings* getConnectionSettingsByUuid(QString const& uuid) const; ConnectionSettings* getConnectionSettingsByUuid(std::string const& uuid) const; void reorderConnections(const ConnectionSettingsContainerType &connections); void setToolbarSettings(QString toolbarName, bool visible); /** * @brief Returns list of connections */ ConnectionSettingsContainerType connections() const { return _connections; } ToolbarSettingsContainerType toolbars() const { return _toolbars; } void setUuidEncoding(UUIDEncoding encoding) { _uuidEncoding = encoding; } UUIDEncoding uuidEncoding() const { return _uuidEncoding; } void setTimeZone(SupportedTimes timeZ) { _timeZone = timeZ; } SupportedTimes timeZone() const { return _timeZone; } void setViewMode(ViewMode viewMode) { _viewMode = viewMode; } ViewMode viewMode() const { return _viewMode; } void setAutocompletionMode(AutocompletionMode mode) { _autocompletionMode = mode; } AutocompletionMode autocompletionMode() const { return _autocompletionMode; } void setAutoExpand(bool isExpand) { _autoExpand = isExpand; } bool autoExpand() const { return _autoExpand; } void setAutoExec(bool isAutoExec) { _autoExec = isAutoExec; } bool autoExec() const { return _autoExec; } void setMinimizeToTray(bool isMinimizingToTray) { _minimizeToTray = isMinimizingToTray; } bool minimizeToTray() const { return _minimizeToTray; } void setLineNumbers(bool showLineNumbers) { _lineNumbers = showLineNumbers; } bool lineNumbers() const { return _lineNumbers; } void setLoadMongoRcJs(bool isLoadJs) { _loadMongoRcJs = isLoadJs; } bool loadMongoRcJs() const { return _loadMongoRcJs; } void setDisableConnectionShortcuts(bool isDisable) { _disableConnectionShortcuts = isDisable; } bool disableConnectionShortcuts() const { return _disableConnectionShortcuts; } void addAcceptedEulaVersion(QString const& version) { _acceptedEulaVersions.insert(version); } QSet const& acceptedEulaVersions() const { return _acceptedEulaVersions; } QSet::iterator addDbVersionConnected(QString const& version) { return _dbVersionsConnected.insert(version); } QSet const& dbVersionsConnected() const { return _dbVersionsConnected; } void setCheckForUpdates(bool checkForUpdates) { _checkForUpdates = checkForUpdates; } bool checkForUpdates() const { return _checkForUpdates; } void setBatchSize(int batchSize) { _batchSize = batchSize; } int batchSize() const { return _batchSize; } QString currentStyle() const { return _currentStyle; } void setCurrentStyle(const QString& style); QString textFontFamily() const { return _textFontFamily; } void setTextFontFamily(const QString& fontFamily); int textFontPointSize() const { return _textFontPointSize; } void setTextFontPointSize(int pointSize); int mongoTimeoutSec() const { return _mongoTimeoutSec; } int shellTimeoutSec() const { return _shellTimeoutSec; } void setShellTimeoutSec(int newValue) { _shellTimeoutSec = std::abs(newValue); } // True when settings from previous versions of Robomongo are imported void setImported(bool imported) { _imported = imported; } bool imported() const { return _imported; } QString anonymousID() const { return _anonymousID; } void addCacheData(QString const& key, QVariant const& value); QVariant cacheData(QString const& key) const; void setProgramExitedNormally(bool value) { _programExitedNormally = value; } bool programExitedNormally() const { return _programExitedNormally; } // Designed to be set only by human users bool disableHttpsFeatures() const { return _disableHttpsFeatures; } bool debugMode() const { return _debugMode; } /** * Returns number of imported connections */ int importedConnectionsCount(); private: /** * Load settings from the map. Existing settings will be overwritten. */ void loadFromMap(QVariantMap &map); /** * Save all settings to map. */ QVariantMap convertToMap() const; // Find existing anonymousID from Robomongo and 3T config files, if not found create // a new anonymousID. QString getOrCreateAnonymousID(QVariantMap const& map) const; /** * Load connection settings from previous versions of Robomongo */ void importFromOldVersion(); // Imports connections from oldConfigFilePath into current config file bool importFromFile(QString const& oldConfigFilePath); static bool importConnectionsFrom_0_8_5(); /** * @brief Version of settings schema currently loaded */ QString _version; UUIDEncoding _uuidEncoding; SupportedTimes _timeZone; ViewMode _viewMode; AutocompletionMode _autocompletionMode; bool _loadMongoRcJs; bool _autoExpand; bool _autoExec; bool _minimizeToTray; bool _lineNumbers; bool _disableConnectionShortcuts; bool _programExitedNormally = true; bool _disableHttpsFeatures = false; bool _debugMode = false; QSet _acceptedEulaVersions; QSet _dbVersionsConnected; int _batchSize; bool _checkForUpdates = true; QString _currentStyle; QString _textFontFamily; int _textFontPointSize; int _mongoTimeoutSec; int _shellTimeoutSec; // True when settings from previous versions of Robomongo are imported bool _imported; /** * @brief This is an anonymous string taken from QUuid that is generated when Robomongo * is first installed on a user's machine and then launched for the first time * It stays the same throughout all upgrades. */ QString _anonymousID; // Various cache data QMap _cacheData; /** * @brief List of connections */ static std::vector _connections; ToolbarSettingsContainerType _toolbars; // List of config. file absolute paths of old versions // Must be updated with care and with every new version. Details on cpp file. static std::vector const _configFilesOfOldVersions; }; } ================================================ FILE: src/robomongo/core/settings/SshSettings.cpp ================================================ #include "robomongo/core/settings/SshSettings.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/utils/RoboCrypt.h" namespace Robomongo { SshSettings::SshSettings() : _port(22), _authMethod("publickey"), _enabled(false), _askPassword(false), _logLevel(1) { } SshSettings *SshSettings::clone() const { SshSettings *cloned = new SshSettings(*this); return cloned; } QVariant SshSettings::toVariant() const { QVariantMap map; map.insert("host", QtUtils::toQString(host())); map.insert("port", port()); map.insert("userName", QtUtils::toQString(userName())); map.insert("userPasswordEncrypted", userPassword().empty() ? "" : QtUtils::toQString(RoboCrypt::encrypt(userPassword()))); map.insert("privateKeyFile", QtUtils::toQString(privateKeyFile())); map.insert("publicKeyFile", QtUtils::toQString(publicKeyFile())); map.insert("passphraseEncrypted", passphrase().empty() ? "" : QtUtils::toQString(RoboCrypt::encrypt(passphrase()))); map.insert("method", QtUtils::toQString(authMethod())); map.insert("enabled", enabled()); map.insert("askPassword", askPassword()); return map; } void SshSettings::fromVariant(const QVariantMap &map) { setHost(QtUtils::toStdString(map.value("host").toString())); setPort(map.value("port").toInt()); setUserName(QtUtils::toStdString(map.value("userName").toString())); // From version Robo 1.3 "userPasswordEncrypted" is used instead of "userPassword" in config. file if (map.contains("userPassword")) // Robo 1.2 and below setUserPassword((map.value("userPassword").toString().toStdString())); else if (map.contains("userPasswordEncrypted")) // From Robo 1.3 setUserPassword(RoboCrypt::decrypt((map.value("userPasswordEncrypted").toString().toStdString()))); setPrivateKeyFile(QtUtils::toStdString(map.value("privateKeyFile").toString())); setPublicKeyFile(QtUtils::toStdString(map.value("publicKeyFile").toString())); // From version Robo 1.3 "passphraseEncrypted" is used instead of "passphrase" in config. file if (map.contains("passphrase")) // Robo 1.2 and below setPassphrase((map.value("passphrase").toString().toStdString())); else if (map.contains("passphraseEncrypted")) // From Robo 1.3 setPassphrase(RoboCrypt::decrypt((map.value("passphraseEncrypted").toString().toStdString()))); setAuthMethod(QtUtils::toStdString(map.value("method").toString())); setEnabled(map.value("enabled").toBool()); setAskPassword(map.value("askPassword").toBool()); } } ================================================ FILE: src/robomongo/core/settings/SshSettings.h ================================================ #pragma once #include #include #include namespace Robomongo { class SshSettings { public: SshSettings(); /** * Clones credential settings. */ SshSettings *clone() const; /** * Converts to QVariantMap */ QVariant toVariant() const; void fromVariant(const QVariantMap &map); /* * Domain or IPv4 or IPv6 address */ std::string host() const { return _host; } void setHost(const std::string &host) { _host = host; } int port() const { return _port; } void setPort(const int port) { _port = port; } std::string userName() const { return _userName; } void setUserName(const std::string &userName) { _userName = userName; } std::string userPassword() const { return _userPassword; } void setUserPassword(const std::string &userPassword) { _userPassword = userPassword; } std::string privateKeyFile() const { return _privateKeyFile; } void setPrivateKeyFile(const std::string &path) { _privateKeyFile = path; } std::string publicKeyFile() const { return _publicKeyFile; } void setPublicKeyFile(const std::string &path) { _publicKeyFile = path; } std::string passphrase() const { return _passphrase; } void setPassphrase(const std::string &passphrase) { _passphrase = passphrase; } // "password" or "publickey" std::string authMethod() const { return _authMethod.empty() ? "publickey" : _authMethod; } void setAuthMethod(const std::string &method) { _authMethod = method; } /** * @brief Flag, indecating whether we should use this * credentials to perform authentication, or not. */ bool enabled() const { return _enabled; } void setEnabled(bool enabled) { _enabled = enabled; } bool askPassword() const { return _askPassword; } void setAskPassword(bool ask) { _askPassword = ask; } std::string askedPassword() const { return _askedPassword; } void setAskedPassword(const std::string &asked) { _askedPassword = asked; } int logLevel() const { return _logLevel; } void setLogLevel(const int logLevel) { _logLevel = logLevel; } private: std::string _host; // domain or IPv4/v6 int _port; std::string _userName; std::string _userPassword; // clear text password (decrypted) std::string _privateKeyFile; std::string _publicKeyFile; std::string _passphrase; // clear text passphrase (decrypted) std::string _authMethod; // "password" or "publickey" // Should we ask user about password or passphrase // each time when we try to connect bool _askPassword; /** * Flag, indicating whether we should use * this SSH tunnel or not. */ bool _enabled; int _logLevel; // this property is not persisted std::string _askedPassword; // this property is not persisted }; } ================================================ FILE: src/robomongo/core/settings/SslSettings.cpp ================================================ #include "robomongo/core/settings/SslSettings.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/utils/RoboCrypt.h" namespace Robomongo { SslSettings::SslSettings() : _sslEnabled(false), _caFile(""), _pemKeyFile(""), _pemPassPhrase(""), _crlFile(""), _allowInvalidHostnames(false), _allowInvalidCertificates(false), _usePemFile(false), _useAdvancedOptions(false), _askPassphrase(false) {} SslSettings *SslSettings::clone() const { SslSettings *cloned = new SslSettings(*this); return cloned; } QVariant SslSettings::toVariant() const { QVariantMap map; map.insert("sslEnabled", sslEnabled()); map.insert("caFile", QtUtils::toQString(caFile())); map.insert("usePemFile", usePemFile()); map.insert("pemKeyFile", QtUtils::toQString(pemKeyFile())); map.insert("pemPassPhraseEncrypted", pemPassPhrase().empty() ? "" : QtUtils::toQString(RoboCrypt::encrypt(pemPassPhrase()))); map.insert("useAdvancedOptions", useAdvancedOptions()); map.insert("crlFile", QtUtils::toQString(crlFile())); map.insert("allowInvalidHostnames", allowInvalidHostnames()); map.insert("allowInvalidCertificates", allowInvalidCertificates()); map.insert("askPassphrase", askPassphrase()); return map; } void SslSettings::fromVariant(const QVariantMap &map) { enableSSL(map.value("sslEnabled").toBool()); setCaFile(QtUtils::toStdString(map.value("caFile").toString())); setUsePemFile(map.value("usePemFile").toBool()); setPemKeyFile(QtUtils::toStdString(map.value("pemKeyFile").toString())); // From version Robo 1.3 "pemPassPhraseEncrypted" is used instead of "pemPassPhrase" in config. file if (map.contains("pemPassPhrase")) // Robo 1.2 and below setPemPassPhrase((map.value("pemPassPhrase").toString().toStdString())); else if (map.contains("pemPassPhraseEncrypted")) // From Robo 1.3 setPemPassPhrase(RoboCrypt::decrypt((map.value("pemPassPhraseEncrypted").toString().toStdString()))); setUseAdvancedOptions(map.value("useAdvancedOptions").toBool()); setCrlFile(QtUtils::toStdString(map.value("crlFile").toString())); setAllowInvalidHostnames(map.value("allowInvalidHostnames").toBool()); setAllowInvalidCertificates(map.value("allowInvalidCertificates").toBool()); setAskPassphrase(map.value("askPassphrase").toBool()); } } ================================================ FILE: src/robomongo/core/settings/SslSettings.h ================================================ #pragma once #include #include #include namespace Robomongo { class SslSettings { public: SslSettings(); /** * Clones credential settings. */ SslSettings *clone() const; /** * Converts to QVariantMap */ QVariant toVariant() const; void fromVariant(const QVariantMap &map); // Getters for mongo:: SSLGlobalParams related settings std::string pemKeyFile() const { return _pemKeyFile; } std::string caFile() const { return _caFile; } std::string pemPassPhrase() const { return _pemPassPhrase; } std::string crlFile() const { return _crlFile; } bool allowInvalidHostnames() const { return _allowInvalidHostnames; } bool allowInvalidCertificates() const { return _allowInvalidCertificates; } // Getters for helper SSL settings bool usePemFile() const { return _usePemFile; } bool useAdvancedOptions() const { return _useAdvancedOptions; } bool askPassphrase() const { return _askPassphrase; } // Setters for mongo:: SSLGlobalParams related settings void setPemKeyFile(const std::string &path) { _pemKeyFile = path; } void setCaFile(const std::string &path) { _caFile = path; } void setPemPassPhrase(const std::string &pemPassPhrase) { _pemPassPhrase = pemPassPhrase; } void setCrlFile(const std::string &path) { _crlFile = path; } void setAllowInvalidHostnames(const bool state) { _allowInvalidHostnames = state; } void setAllowInvalidCertificates(const bool state) { _allowInvalidCertificates = state; } // Setters for helper SSL settings void setUsePemFile(const bool state) { _usePemFile = state; } void setUseAdvancedOptions(const bool state) { _useAdvancedOptions = state; } void setAskPassphrase(const bool state) { _askPassphrase = state; } /** * Flag, indicating whether we should use * this SSL settings or not. */ bool sslEnabled() const { return _sslEnabled; } void enableSSL(bool enabled) { _sslEnabled = enabled; } private: // mongo:: SSL Global params related settings std::string _caFile; std::string _pemKeyFile; std::string _pemPassPhrase; std::string _crlFile; bool _allowInvalidHostnames; bool _allowInvalidCertificates; // Helper settings indirectly effecting what to pass to SSL global params bool _usePemFile; bool _useAdvancedOptions; bool _askPassphrase; /** * Flag, indicating whether SSL enabled for related connection */ bool _sslEnabled; }; } ================================================ FILE: src/robomongo/core/utils/BsonUtils.cpp ================================================ #include "robomongo/core/utils/BsonUtils.h" #include //#include #include "mongo/util/base64.h" #include "mongo/util/str.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/HexUtils.h" // v0.9 #include "robomongo/shell/db/ptimeutil.h" using namespace mongo; namespace Robomongo { namespace BsonUtils { namespace detail { template<> mongo::BSONObj getField(const mongo::BSONElement &elem) { return elem.embeddedObject(); } template<> bool getField(const mongo::BSONElement &elem) { return elem.Bool(); } template<> std::string getField(const mongo::BSONElement &elem) { return elem.String(); } template<> std::vector getField >(const mongo::BSONElement &elem) { return elem.Array(); } template<> int getField(const mongo::BSONElement &elem) { return elem.Int(); } template<> double getField(const mongo::BSONElement &elem) { return elem.numberDouble(); } template<> long long getField(const mongo::BSONElement &elem) { return elem.safeNumberLong(); } } std::string jsonString(const BSONObj &obj, JsonStringFormat format, int pretty, UUIDEncoding uuidEncoding, SupportedTimes timeFormat, bool isArray) { using namespace std; // Use of method, that is implemented in Robomongo Shell // Method "isArray()" is not part of MongoDB. // In order for this method to work, someone should // explicetly call "markAsArray()" method on BSONObj. // This is done in the Robomongo Shell (MongoDB fork) if (obj.isArray()) { isArray = true; } if ( obj.isEmpty() ) { return isArray? "[]" : "{}"; } StringBuilder s; s << (isArray ? "[" : "{"); BSONObjIterator i(obj); BSONElement e = i.next(); if ( !e.eoo() ) { while ( 1 ) { if ( pretty ) { s << '\n'; for( int x = 0; x < pretty; x++ ) { s << " "; } } else { s << " "; } s << jsonString(e, format, true, pretty ? pretty + 1 : 0, uuidEncoding, timeFormat, isArray); e = i.next(); if (e.eoo()) { s << '\n'; for( int x = 0; x < pretty - 1; x++ ) { s << " "; } s << (isArray ? "]" : "}"); break; } s << ","; } } return s.str(); } std::string jsonString(const BSONElement &elem, JsonStringFormat format, bool includeFieldNames, int pretty, UUIDEncoding uuidEncoding, SupportedTimes timeFormat, bool isArray) { using namespace std; BSONType t = elem.type(); std::stringstream s; if ( includeFieldNames && !isArray) s << '"' << mongo::str::escape(elem.fieldName()) << "\" : "; switch ( t ) { case Undefined: s << "undefined"; break; case mongo::String: case Symbol: s << '"' << mongo::str::escape(std::string(elem.valuestr(), elem.valuestrsize() - 1)) << '"'; break; case NumberLong: s << "NumberLong(" << elem._numberLong() << ")"; break; case NumberInt: s << elem._numberInt(); break; case NumberDouble: { if ( elem.number() >= -std::numeric_limits< double >::max() && elem.number() <= std::numeric_limits< double >::max() ) { std::stringstream ss; ss.precision(std::numeric_limits::digits10); ss << elem.Double(); std::string const str = reformatDoubleString(QString::fromStdString(ss.str()), elem.Double()); s << (str); } else if (std::isnan(elem.number()) ) { s << "NaN"; } else if (std::isinf(elem.number()) ) { s << (std::to_string(elem.number()) == "inf" ? "Infinity" : "-Infinity"); } else { StringBuilder ss; ss << "BsonUtils::jsonString(): Number " << elem.number() << " cannot be represented in JSON"; LOG_MSG(ss.str(), mongo::logger::LogSeverity::Error()); } break; } case NumberDecimal: s << "NumberDecimal(\"" << elem._numberDecimal().toString() << "\")"; break; case mongo::Bool: s << ( elem.boolean() ? "true" : "false" ); break; case jstNULL: s << "null"; break; case Object: { BSONObj obj = elem.embeddedObject(); s << jsonString(obj, format, pretty, uuidEncoding, timeFormat); } break; case mongo::Array: { if ( elem.embeddedObject().isEmpty() ) { s << "[]"; break; } s << "[ "; BSONObjIterator i( elem.embeddedObject() ); BSONElement e = i.next(); if ( !e.eoo() ) { int count = 0; while ( 1 ) { if ( pretty ) { s << '\n'; for( int x = 0; x < pretty; x++ ) s << " "; } if (strtol(e.fieldName(), 0, 10) > count) { s << "undefined"; } else { s << jsonString(e, format, false, pretty ? pretty + 1 : 0, uuidEncoding, timeFormat, true); e = i.next(); } count++; if ( e.eoo() ) { s << '\n'; for( int x = 0; x < pretty - 1; x++ ) s << " "; s << "]"; break; } s << ", "; } } //s << " ]"; break; } case DBRef: { mongo::OID *x = (mongo::OID *) (elem.valuestr() + elem.valuestrsize()); if ( format == TenGen ) s << "DBRef("; else s << "{ \"$ref\" : "; s << '"' << elem.valuestr() << "\", "; if ( format != TenGen ) s << "\"$id\" : "; s << '"' << *x << "\""; if ( format == TenGen ) s << ')'; else s << '}'; break; } case jstOID: if ( format == TenGen ) { s << "ObjectId("; } else { s << "{ \"$oid\" : "; } s << '"' << elem.__oid() << '"'; if ( format == TenGen ) { s << ")"; } else { s << " }"; } break; case BinData: { int len = *(int *)( elem.value() ); BinDataType type = BinDataType( *(char *)( (int *)( elem.value() ) + 1 ) ); if (type == mongo::bdtUUID || type == mongo::newUUID) { s << HexUtils::formatUuid(elem, uuidEncoding); break; } s << "{ \"$binary\" : \""; char *start = ( char * )( elem.value() ) + sizeof( int ) + 1; base64::encode( s , start , len ); s << "\", \"$type\" : \"" << hex; s.width( 2 ); s.fill( '0' ); s << type << dec; s << "\" }"; break; } case mongo::Date: { Date_t d = elem.date(); long long ms = d.toMillisSinceEpoch(); //static_cast(d.millis); bool isSupportedDate = miutil::minDate < ms && ms < miutil::maxDate; if ( format == Strict ) s << "{ \"$date\" : "; else{ if (isSupportedDate) { s << "ISODate("; } else{ s << "Date("; } } if ( pretty && isSupportedDate) { boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); boost::posix_time::time_duration diff = boost::posix_time::millisec(ms); boost::posix_time::ptime time = epoch + diff; std::string timestr = miutil::isotimeString(time, true, timeFormat == LocalTime); s << '"' << timestr << '"'; } else s << ms; if ( format == Strict ) s << " }"; else s << ")"; break; } case RegEx: if ( format == Strict ) { s << "{ \"$regex\" : \"" << mongo::str::escape(elem.regex()); s << "\", \"$options\" : \"" << elem.regexFlags() << "\" }"; } else { s << "/" << mongo::str::escape(elem.regex(), true) << "/"; // FIXME Worry about alpha order? for ( const char *f = elem.regexFlags(); *f; ++f ) { switch ( *f ) { case 'g': case 'i': case 'm': s << *f; default: break; } } } break; case CodeWScope: { BSONObj scope = elem.codeWScopeObject(); if ( ! scope.isEmpty() ) { s << "{ \"$code\" : " << elem._asCode() << " , " << " \"$scope\" : " << scope.jsonString() << " }"; break; } } case Code: s << elem._asCode(); break; case bsonTimestamp: if ( format == TenGen ) s << "Timestamp(" << elem.timestamp().getSecs() << ", " << elem.timestampInc() << ")"; else s << "{ \"$timestamp\" : { \"t\" : " << elem.timestamp().getSecs() << ", \"i\" : " << elem.timestampInc() << " } }"; break; case MinKey: s << "{ \"$minKey\" : 1 }"; break; case MaxKey: s << "{ \"$maxKey\" : 1 }"; break; default: StringBuilder ss; ss << "Cannot create a properly formatted JSON string with " << "element: " << elem.toString() << " of type: " << elem.type(); } return s.str(); } bool isArray(const mongo::BSONElement &elem) { return isArray(elem.type()); } bool isArray(mongo::BSONType type) { return type == mongo::Array ; } bool isDocument(const mongo::BSONElement &elem) { return isDocument(elem.type()); } bool isDocument(mongo::BSONType type) { switch( type ) { case mongo::Object: case mongo::Array: return true; default: return false; } } bool isSimpleType(const mongo::BSONType type) { switch( type ) { case NumberLong: case NumberDouble: case NumberDecimal: case NumberInt: case mongo::String: case mongo::Bool: case mongo::Date: case jstOID: return true; default: return false; } } bool isUuidType(const mongo::BSONType type, mongo::BinDataType binDataType) { if (type != mongo::BinData) return false; return (binDataType == mongo::newUUID || binDataType == mongo::bdtUUID); } bool isSimpleType(const mongo::BSONElement &elem) { return isSimpleType(elem.type()); } bool isUuidType(const mongo::BSONElement &elem) { if (elem.type() != mongo::BinData) return false; mongo::BinDataType binType = elem.binDataType(); return (binType == mongo::newUUID || binType == mongo::bdtUUID); } const char* BSONTypeToString(mongo::BSONType type, mongo::BinDataType binDataType, UUIDEncoding uuidEncoding) { switch (type) { /** double precision floating point value */ case NumberDouble: { return "Double"; } /** double precision floating point value */ case NumberDecimal: { return "Decimal128"; } /** character string, stored in utf8 */ case String: { return "String"; } /** an embedded object */ case Object: { return "Object"; } /** an embedded array */ case Array: { return "Array"; } case BinData: { if (binDataType == mongo::newUUID) { return "UUID"; } else if (binDataType == mongo::bdtUUID) { const char* type; switch(uuidEncoding) { case DefaultEncoding: type = "Legacy UUID"; break; case JavaLegacy: type = "Java UUID (Legacy)"; break; case CSharpLegacy: type = ".NET UUID (Legacy)"; break; case PythonLegacy: type = "Python UUID (Legacy)"; break; default: type = "Legacy UUID"; break; } return type; } else { return "Binary"; } } /** Undefined type */ case Undefined: { return "Undefined"; } /** ObjectId */ case jstOID: { return "ObjectId"; } /** boolean type */ case Bool: { return "Boolean"; } /** date type */ case Date: { return "Date"; } /** null type */ case jstNULL: { return "Null"; } break; /** regular expression, a pattern with options */ case RegEx: { return "Regular Expression"; } /** deprecated / will be redesigned */ case DBRef: { return "DBRef"; } /** deprecated / use CodeWScope */ case Code: { return "Code"; } break; /** a programming language (e.g., Python) symbol */ case Symbol: { return "Symbol"; } /** javascript code that can execute on the database server, with SavedContext */ case CodeWScope: { return "CodeWScope"; } /** 32 bit signed integer */ case NumberInt: { return "Int32"; } /** Updated to a Date with value next OpTime on insert */ case bsonTimestamp: { return "Timestamp"; } /** 64 bit integer */ case NumberLong: { return "Int64"; } break; default: { return "Type is not supported"; } } } void buildJsonString(const mongo::BSONObj &obj, std::string &con, UUIDEncoding uuid, SupportedTimes tz) { mongo::BSONObjIterator iterator(obj); con.append("{ \n"); while (iterator.more()) { mongo::BSONElement e = iterator.next(); con.append("\""); con.append(e.fieldName()); con.append("\""); con.append(" : "); buildJsonString(e, con, uuid, tz); con.append(", \n"); } con.append("\n}\n\n"); } void buildJsonString(const mongo::BSONElement &elem, std::string &con, UUIDEncoding uuid, SupportedTimes tz) { switch (elem.type()) { case NumberDouble: { if (elem.number() >= -std::numeric_limits< double >::max() && elem.number() <= std::numeric_limits< double >::max()) { std::stringstream ss; ss.precision(std::numeric_limits::digits10); ss << elem.Double(); std::string const str = reformatDoubleString(QString::fromStdString(ss.str()), elem.Double()); con.append(str); } else if (std::isnan(elem.number())) { con.append("NaN"); } else if (std::isinf(elem.number())) { con.append(std::to_string(elem.number()) == "inf" ? "Infinity" : "-Infinity"); } else { StringBuilder ss; ss << "BsonUtils::buildJsonString(): Number " << elem.number() << " cannot be represented in JSON"; LOG_MSG(ss.str(), mongo::logger::LogSeverity::Error()); } } break; case String: { con.append(elem.valuestr(), elem.valuestrsize() - 1); } break; case Object: { buildJsonString(elem.Obj(), con, uuid, tz); } break; case Array: { buildJsonString(elem.Obj(), con, uuid, tz); } break; case BinData: { mongo::BinDataType binType = elem.binDataType(); if (binType == mongo::newUUID || binType == mongo::bdtUUID) { std::string uu = HexUtils::formatUuid(elem, uuid); con.append(uu); break; } con.append(""); } break; case Undefined: con.append("undefined"); break; case jstOID: { std::string idValue = elem.OID().toString(); char buff[256] = {0}; sprintf(buff, "ObjectId(\"%s\")", idValue.c_str()); con.append(buff); } break; case Bool: con.append(elem.Bool() ? "true" : "false"); break; case Date: { long long ms = (long long) elem.Date().toMillisSinceEpoch(); bool isSupportedDate = miutil::minDate < ms && ms < miutil::maxDate; boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); boost::posix_time::time_duration diff = boost::posix_time::millisec(ms); boost::posix_time::ptime time = epoch + diff; std::string date; if (isSupportedDate) date = miutil::isotimeString(time, false, tz == LocalTime); else date = boost::lexical_cast(ms); con.append(date); break; } case jstNULL: con.append("null"); break; case RegEx: { con.append("/" + std::string(elem.regex()) + "/"); for ( const char *f = elem.regexFlags(); *f; ++f ) { switch ( *f ) { case 'g': case 'i': case 'm': con += *f; default: break; } } } break; case DBRef: break; case Code: con.append(elem._asCode()); break; case Symbol: con.append(elem.valuestr(), elem.valuestrsize() - 1); break; case CodeWScope: { mongo::BSONObj scope = elem.codeWScopeObject(); if (!scope.isEmpty() ) { con.append(elem._asCode()); break; } } break; case NumberInt: { char num[16] = {0}; sprintf(num, "%d", elem.Int()); con.append(num); break; } case bsonTimestamp: { Date_t date = elem.timestampTime(); unsigned long long millis = date.toMillisSinceEpoch(); // millis; if ((long long)millis >= 0 && ((long long)millis/1000) < (std::numeric_limits::max)()) { con.append(date.toString()); } break; } case NumberLong: { char num[32] = {0}; sprintf(num, "%lld", elem.Long()); con.append(num); break; } case NumberDecimal: { con.append(elem.numberDecimal().toString()); break; } default: con.append(""); break; } } mongo::BSONElement indexOf(const mongo::BSONObj &doc, int index) { mongo::BSONObjIterator iterator(doc); for (int i = 0; iterator.more(); ++i) { mongo::BSONElement element = iterator.next(); if (i == index) { return element; } } return mongo::BSONElement(); } int elementsCount(const mongo::BSONObj &doc) { mongo::BSONObjIterator iterator(doc); int i = 0; for (; iterator.more(); ++i) { iterator.next(); } return i; } std::string reformatDoubleString(QString str, double elemDouble) { // Leave trailing zero if needed if (!str.contains("e+", Qt::CaseInsensitive) && !str.contains("e-", Qt::CaseInsensitive) && elemDouble == (long long)elemDouble) str.append(".0"); else if (str.endsWith("e+15", Qt::CaseInsensitive) || str.endsWith("e+16", Qt::CaseInsensitive)) { // Disable scientific format std::stringstream ss2; ss2.precision(std::numeric_limits::digits10); ss2 << std::fixed << elemDouble; str = QString::fromStdString(ss2.str()); while (str.contains('.') && str.endsWith("00")) str.chop(1); } return str.toStdString(); } } // BsonUtils } // Robomongo ================================================ FILE: src/robomongo/core/utils/BsonUtils.h ================================================ #pragma once #include #include #include "robomongo/core/Enums.h" class QString; namespace Robomongo { namespace BsonUtils { namespace detail { template struct bson_convert_traits { typedef mongo::BSONObj type; enum { mongo_type = BSONType_t }; }; template<> struct bson_convert_traits { typedef bool type; enum { mongo_type = mongo::Bool }; }; template<> struct bson_convert_traits { typedef std::string type; enum { mongo_type = mongo::String }; }; template<> struct bson_convert_traits { typedef std::vector type; enum { mongo_type = mongo::Array }; }; template<> struct bson_convert_traits { typedef int type; enum { mongo_type = mongo::NumberInt }; }; template<> struct bson_convert_traits { typedef double type; enum { mongo_type = mongo::NumberDouble }; }; template<> struct bson_convert_traits { typedef long long type; enum { mongo_type = mongo::NumberLong }; }; template type_t getField(const mongo::BSONElement &elem); } template type_t bsonelement_cast(const mongo::BSONElement &elem) { type_t result = type_t(); bool isEoo = !elem.eoo(); if (isEoo) { result = detail::getField(elem); } return result; } template typename detail::bson_convert_traits::type getField(const mongo::BSONObj &obj, const char *data) { mongo::BSONElement elem = obj.getField(data); return bsonelement_cast::type>(elem); } std::string jsonString(const mongo::BSONObj &obj, mongo::JsonStringFormat format, int pretty, UUIDEncoding uuidEncoding, SupportedTimes timeFormat, bool isArray = false); std::string jsonString(const mongo::BSONElement &elem, mongo::JsonStringFormat format, bool includeFieldNames, int pretty, UUIDEncoding uuidEncoding, SupportedTimes timeFormat, bool isArray = false); bool isArray(const mongo::BSONElement &elem); bool isArray(mongo::BSONType type); bool isDocument(const mongo::BSONElement &elem); bool isDocument(mongo::BSONType type); bool isSimpleType(const mongo::BSONType type); bool isUuidType(const mongo::BSONType type, mongo::BinDataType binDataType); bool isSimpleType(const mongo::BSONElement &elem); bool isUuidType(const mongo::BSONElement &elem); const char* BSONTypeToString(mongo::BSONType type, mongo::BinDataType binDataType, UUIDEncoding uuidEncoding); void buildJsonString(const mongo::BSONObj &obj, std::string &con, UUIDEncoding uuid, SupportedTimes tz); void buildJsonString(const mongo::BSONElement &elem, std::string &con, UUIDEncoding uuid, SupportedTimes tz); mongo::BSONElement indexOf(const mongo::BSONObj &doc, int index); int elementsCount(const mongo::BSONObj &doc); std::string reformatDoubleString(QString str, double elemDouble); } } ================================================ FILE: src/robomongo/core/utils/Logger.cpp ================================================ #include "robomongo/core/utils/Logger.h" #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { Logger::Logger() { QFile file { QString("%1/" PROJECT_NAME_LOWERCASE ".log").arg(QDir::tempPath()) }; //delete file if it size more than 5mb if (file.exists() && file.size() > 5 * 1024 * 1024) file.remove(); } Logger::~Logger() { } void Logger::print(const char *mess, mongo::logger::LogSeverity level, bool notify) { print(std::string(mess), level, notify); } void Logger::print(const std::string &mess, mongo::logger::LogSeverity level, bool notify) { print(QtUtils::toQString(mess), level, notify); } void Logger::print(const QString &msg, mongo::logger::LogSeverity level, bool notify) { if (!notify) return; // Make uniform log level strings e.g "Error: ", "Info: " etc... auto logLevelStr = QString::fromStdString(level.toStringData().toString()); if (!logLevelStr.isEmpty()) { logLevelStr = logLevelStr.toLower(); logLevelStr[0] = logLevelStr[0].toUpper(); logLevelStr += ": "; } emit printed(logLevelStr + msg.simplified(), level); } void sendLog( QObject *sender, LogEvent::LogLevel const& severity, std::string const& msg, bool const informUser /*= false*/) { AppRegistry::instance().bus()->send( AppRegistry::instance().app(), new LogEvent(sender, msg, severity, informUser) ); } void debugLog(std::string_view msg) { if (!AppRegistry::instance().settingsManager()->debugMode()) return; std::cout << msg << std::endl; } } ================================================ FILE: src/robomongo/core/utils/Logger.h ================================================ #pragma once #include #include #include #include #include "robomongo/core/events/MongoEvents.h" #include "robomongo/core/utils/SingletonPattern.hpp" namespace Robomongo { class Logger : public QObject, public Patterns::LazySingleton { Q_OBJECT friend class Patterns::LazySingleton; public: void print(const char *msg, mongo::logger::LogSeverity level, bool notify); void print(const std::string &msg, mongo::logger::LogSeverity level, bool notify); void print(const QString &msg, mongo::logger::LogSeverity level, bool notify); Q_SIGNALS: void printed(const QString &msg, mongo::logger::LogSeverity level); private: Logger(); ~Logger(); }; // Use in main thread template inline void LOG_MSG(const T &msg, mongo::logger::LogSeverity level, bool notify = true) { return Logger::instance().print(msg, level, notify); } // Use in worker threads (e.g. MongoWorker) to log anything // Sends LogEvent to main thread (App class) void sendLog( QObject *sender, LogEvent::LogLevel const& severity, std::string const& msg, bool const informUser = false ); void debugLog(std::string_view msg); } ================================================ FILE: src/robomongo/core/utils/QtUtils.cpp ================================================ #include "robomongo/core/utils/QtUtils.h" #include #include namespace Robomongo { namespace QtUtils { template<> QString toQString(const std::string &value) { //static QTextCodec *LOCALECODEC = QTextCodec::codecForLocale(); return QString::fromUtf8(value.c_str(), value.size()); } template<> QString toQString(const std::wstring &value) { return QString((const QChar*)value.c_str(), value.length()); } std::string toStdString(const QString &value) { QByteArray sUtf8 = value.toUtf8(); return std::string(sUtf8.constData(), sUtf8.length()); } std::string toStdStringSafe(const QString &value) { #ifdef Q_OS_WIN QByteArray sUtf8 = value.toLocal8Bit(); #else QByteArray sUtf8 = value.toUtf8(); #endif return std::string(sUtf8.constData(), sUtf8.length()); } void cleanUpThread(QThread *const thread) { if (thread && thread->isRunning()) { //thread->stop(); thread->wait(); } } void clearChildItems(QTreeWidgetItem *const root) { int itemCount = root->childCount(); for (int i = 0; i < itemCount; ++i) { QTreeWidgetItem *item = root->child(0); root->removeChild(item); delete item; } } } } ================================================ FILE: src/robomongo/core/utils/QtUtils.h ================================================ #pragma once #include #include QT_BEGIN_NAMESPACE class QThread; class QTreeWidgetItem; class QAbstractItemModel; QT_END_NAMESPACE #ifdef QT_NO_DEBUG #define VERIFY(x) (x) #else //QT_NO_DEBUG #define VERIFY(x) Q_ASSERT(x) #endif //QT_NO_DEBUG namespace Robomongo { namespace QtUtils { template QString toQString(const T &value); std::string toStdString(const QString &value); std::string toStdStringSafe(const QString &value); void cleanUpThread(QThread *const thread); void clearChildItems(QTreeWidgetItem *root); template inline Type item(const QModelIndex &index) { return static_cast(index.internalPointer()); } struct HackQModelIndex { int r, c; void* i; const QAbstractItemModel *m; }; } } ================================================ FILE: src/robomongo/core/utils/SingletonPattern.hpp ================================================ #pragma once namespace Patterns { template class Singleton { static T* _self; public: void freeInstance(); static T* instance(); protected: virtual ~Singleton() { _self=NULL; } Singleton() { } }; template T* Singleton::_self = NULL; template T* Singleton::instance() { if(!_self) _self=new T; return _self; } template void Singleton::freeInstance() { delete this; } template class LazySingleton { public: typedef LazySingleton class_type; static T &instance(); protected: LazySingleton() { } ~LazySingleton(){} private: LazySingleton(class_type const&); LazySingleton& operator=(class_type const& rhs); }; template T &LazySingleton::instance() { static T _self; return _self; } } ================================================ FILE: src/robomongo/core/utils/StdUtils.cpp ================================================ #include "robomongo/core/utils/StdUtils.h" namespace Robomongo { namespace stdutils { } } ================================================ FILE: src/robomongo/core/utils/StdUtils.h ================================================ #pragma once #include #include namespace Robomongo { namespace stdutils { template inline void destroy(T *&v) { delete v; v = NULL; } template struct RemoveIfFound { RemoveIfFound(T whatSearch) : _whatSearch(whatSearch) {} bool operator()(T item) const { if (item == _whatSearch) { destroy(item); return true; } return false; } T _whatSearch; }; template struct default_delete { inline void operator ()(T *ptr) const { destroy(ptr); } }; template struct default_delete { inline void operator ()(T *ptr) const { destroy(ptr); } }; template struct default_delete { inline void operator ()(const T ptr) const { delete [] ptr; } }; } } ================================================ FILE: src/robomongo/gui/AppStyle.cpp ================================================ #include "robomongo/gui/AppStyle.h" #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/settings/SettingsManager.h" namespace Robomongo { const QString AppStyle::StyleName = "Native"; namespace AppStyleUtils { void applyStyle(const QString &styleName) { if (styleName == "Native") { QApplication::setStyle(new AppStyle); return; } QApplication::setStyle(QStyleFactory::create(styleName)); } QStringList getSupportedStyles() { static QStringList result = QStringList() << AppStyle::StyleName << QStyleFactory::keys(); return result; } void initStyle() { AppRegistry::instance().settingsManager()->save(); QString style = AppRegistry::instance().settingsManager()->currentStyle(); applyStyle(style); } } void AppStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { return QProxyStyle::drawControl(element, option, painter, widget); } void AppStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { #ifdef Q_OS_WIN if (element == QStyle::PE_FrameFocusRect) return; #endif return QProxyStyle::drawPrimitive(element, option, painter, widget); } QRect AppStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget /*= 0 */) const { return QProxyStyle::subElementRect(element, option, widget); } } ================================================ FILE: src/robomongo/gui/AppStyle.h ================================================ #pragma once #include #include namespace Robomongo { namespace AppStyleUtils { void initStyle(); void applyStyle(const QString &styleName); QStringList getSupportedStyles(); } class AppStyle : public QProxyStyle { Q_OBJECT public: static const QString StyleName; virtual void drawControl(ControlElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget) const; virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const; virtual QRect subElementRect( SubElement element, const QStyleOption * option, const QWidget * widget = 0 ) const; }; } ================================================ FILE: src/robomongo/gui/GuiRegistry.cpp ================================================ #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/settings/SettingsManager.h" #include #include #include namespace Robomongo { /** * @brief This is a private constructor, because GuiRegistry is a singleton */ GuiRegistry::GuiRegistry() { } /** * @brief Functions that provide access to various icons */ void GuiRegistry::setAlternatingColor(QAbstractItemView *view) { #if defined(Q_OS_MAC) view->setAlternatingRowColors(true); QPalette p = view->palette(); // p.setColor(QPalette::AlternateBase, QColor(243, 246, 250)); p.setColor(QPalette::AlternateBase, QColor(245, 245, 245)); p.setColor(QPalette::Active, QPalette::Highlight, QColor(16, 108, 214)); view->setPalette(p); #endif } const QIcon &GuiRegistry::serverIcon() const { static const QIcon serverIc = QIcon(":/robomongo/icons/server_16x16.png"); return serverIc; } const QIcon &GuiRegistry::serverImportedIcon() const { static const QIcon importedIcon = QIcon(":/robomongo/icons/server_imported_16x16.png"); return importedIcon; } const QIcon &GuiRegistry::serverPrimaryIcon() const { static const QIcon icon(":/robomongo/icons/server_primary_16x16.png"); return icon; } const QIcon &GuiRegistry::serverSecondaryIcon() const { static const QIcon icon(":/robomongo/icons/server_secondary_16x16.png"); return icon; } const QIcon &GuiRegistry::replicaSetIcon() const { static const QIcon replicaSetIc(":/robomongo/icons/replica_set_16x16.png"); return replicaSetIc; } const QIcon &GuiRegistry::replicaSetOfflineIcon() const { static const QIcon icon(":/robomongo/icons/replica_set_offline_16x16.png"); return icon; } const QIcon &GuiRegistry::openIcon() const { static const QIcon openIc = qApp->style()->standardIcon(QStyle::SP_DialogOpenButton); //static const QIcon openIc = QIcon(":/robomongo/icons/open_32x32.png"); return openIc; } const QIcon &GuiRegistry::saveIcon() const { static const QIcon saveIc = qApp->style()->standardIcon(QStyle::SP_DialogSaveButton); // static const QIcon saveIc = QIcon(":/robomongo/icons/save_32x32.png"); return saveIc; } const QIcon &GuiRegistry::databaseIcon() const { static const QIcon databaseIc = QIcon(":/robomongo/icons/database_16x16.png"); return databaseIc; } const QIcon &GuiRegistry::collectionIcon() const { static const QIcon collectionIc = QIcon(":/robomongo/icons/collection_16x16.png"); return collectionIc; } const QIcon &GuiRegistry::indexIcon() const { static const QIcon collectionIc = QIcon(":/robomongo/icons/index_16x16.png"); return collectionIc; } const QIcon &GuiRegistry::userIcon() const { static const QIcon userIc = QIcon(":/robomongo/icons/user_16x16.png"); return userIc; } const QIcon &GuiRegistry::functionIcon() const { static const QIcon functionIc = QIcon(":/robomongo/icons/function_16x16.png"); return functionIc; } const QIcon &GuiRegistry::maximizeIcon() const { static const QIcon maximizeIc(":/robomongo/icons/maximize.png"); return maximizeIc; } const QIcon &GuiRegistry::minimizeIcon() const { static const QIcon minimizeIc(":/robomongo/icons/minimize.png"); return minimizeIc; } const QIcon &GuiRegistry::undockIcon() const { static const QIcon undockIc(":/robomongo/icons/undock.png"); return undockIc; } const QIcon &GuiRegistry::dockIcon() const { static const QIcon dockIc(":/robomongo/icons/dock.png"); return dockIc; } const QIcon &GuiRegistry::textIcon() const { static const QIcon textIc = QIcon(":/robomongo/icons/text_16x16.png"); return textIc; } const QIcon &GuiRegistry::textHighlightedIcon() const { static const QIcon textHighlightedIc = QIcon(":/robomongo/icons/text_highlighted_16x16.png"); return textHighlightedIc; } const QIcon &GuiRegistry::treeIcon() const { static const QIcon treeIc = QIcon(":/robomongo/icons/tree_16x16.png"); return treeIc; } const QIcon &GuiRegistry::treeHighlightedIcon() const { static const QIcon treeHighlightedIc = QIcon(":/robomongo/icons/tree_highlighted_16x16.png"); return treeHighlightedIc; } const QIcon &GuiRegistry::tableIcon() const { static const QIcon treeIc = QIcon(":/robomongo/icons/table_16x16.png"); return treeIc; } const QIcon &GuiRegistry::tableHighlightedIcon() const { static const QIcon treeHighlightedIc = QIcon(":/robomongo/icons/table_highlighted_16x16.png"); return treeHighlightedIc; } const QIcon &GuiRegistry::customIcon() const { static const QIcon customIc = QIcon(":/robomongo/icons/custom_16x16.png"); return customIc; } const QIcon &GuiRegistry::customHighlightedIcon() const { static const QIcon customHighlightedIc = QIcon(":/robomongo/icons/custom_highlighted_16x16.png"); return customHighlightedIc; } const QIcon &GuiRegistry::rotateIcon() const { static const QIcon rotateIc = QIcon(":/robomongo/icons/rotate_16x16.png"); return rotateIc; } const QIcon &GuiRegistry::visualIcon() const { static const QIcon visualIc = QIcon(":/robomongo/icons/visual_16x16.png"); return visualIc; } const QIcon &GuiRegistry::circleIcon() const { static const QIcon circleIc = QIcon(":/robomongo/icons/bson_unsupported_16x16.png"); return circleIc; } const QIcon &GuiRegistry::bsonArrayIcon() const { static const QIcon bsonArrayIc = QIcon(":/robomongo/icons/bson_array_16x16.png"); return bsonArrayIc; } const QIcon &GuiRegistry::bsonObjectIcon() const { static const QIcon bsonObjectIc = QIcon(":/robomongo/icons/bson_object_16x16.png"); return bsonObjectIc; } const QIcon &GuiRegistry::bsonStringIcon() const { static const QIcon bsonStringIc = QIcon(":/robomongo/icons/bson_string_16x16.png"); return bsonStringIc; } const QIcon &GuiRegistry::folderIcon() const { static const QIcon folderIc = qApp->style()->standardIcon(QStyle::SP_DirClosedIcon); return folderIc; } const QIcon &GuiRegistry::bsonIntegerIcon() const { static const QIcon bsonIntegerIc = QIcon(":/robomongo/icons/bson_integer_16x16.png"); return bsonIntegerIc; } const QIcon &GuiRegistry::bsonDoubleIcon() const { static const QIcon bsonDoubleIc = QIcon(":/robomongo/icons/bson_double_16x16.png"); return bsonDoubleIc; } const QIcon &GuiRegistry::bsonNumberDecimalIcon() const { static const QIcon icon(":/robomongo/icons/bson_decimal128_16x16.png"); return icon; } const QIcon &GuiRegistry::bsonDateTimeIcon() const { static const QIcon bsonDateTimeIc = QIcon(":/robomongo/icons/bson_datetime_16x16.png"); return bsonDateTimeIc; } const QIcon &GuiRegistry::bsonBinaryIcon() const { static const QIcon bsonBinaryIc = QIcon(":/robomongo/icons/bson_binary_16x16.png"); return bsonBinaryIc; } const QIcon &GuiRegistry::bsonNullIcon() const { static const QIcon bsonNullIc = QIcon(":/robomongo/icons/bson_null_16x16.png"); return bsonNullIc; } const QIcon &GuiRegistry::bsonBooleanIcon() const { static const QIcon bsonBooleanIc = QIcon(":/robomongo/icons/bson_bool_16x16.png"); return bsonBooleanIc; } const QIcon &GuiRegistry::noMarkIcon() const { static const QIcon noMarkIc = QIcon(":/robomongo/icons/no_mark_24x24.png"); return noMarkIc; } const QIcon &GuiRegistry::yesMarkIcon() const { static const QIcon yesMarkIc = QIcon(":/robomongo/icons/yes_mark_24x24.png"); return yesMarkIc; } const QIcon &GuiRegistry::skipMarkIcon() const { static const QIcon skipMarkIc = QIcon(":/robomongo/icons/skip_mark_24x24.png"); return skipMarkIc; } const QIcon &GuiRegistry::questionMarkIcon() const { static const QIcon questionMarkIc = QIcon(":/robomongo/icons/question_mark_24x24.png"); return questionMarkIc; } const QIcon &GuiRegistry::timeIcon() const { static const QIcon timeIc = QIcon(":/robomongo/icons/time_16x16.png"); return timeIc; } const QIcon &GuiRegistry::keyIcon() const { static const QIcon keyIc = QIcon(":/robomongo/icons/key_16x16.png"); return keyIc; } const QIcon &GuiRegistry::showIcon() const { static const QIcon icon = QIcon(":/robomongo/icons/show_64x64.png"); return icon; } const QIcon &GuiRegistry::hideIcon() const { static const QIcon icon = QIcon(":/robomongo/icons/hide_64x64.png"); return icon; } const QIcon &GuiRegistry::plusIcon() const { static const QIcon icon {":/robomongo/icons/plus_sign.png"}; return icon; } const QIcon &GuiRegistry::minusIcon() const { static const QIcon icon {":/robomongo/icons/minus_sign.png"}; return icon; } const QBrush &GuiRegistry::typeBrush() const { static const QBrush typeBrush = QBrush(QColor(150, 150, 150)); return typeBrush; } const QIcon &GuiRegistry::leftIcon() const { static const QIcon leftIc = QIcon(":/robomongo/icons/left_16x16.png"); return leftIc; } const QIcon &GuiRegistry::rightIcon() const { static const QIcon rightIc = QIcon(":/robomongo/icons/right_16x16.png"); return rightIc; } const QIcon &GuiRegistry::mongodbIcon() const { static const QIcon mongodbIc = QIcon(":/robomongo/icons/mongodb_16x16.png"); return mongodbIc; } const QIcon &GuiRegistry::mongodbIconForMAC() const { static const QIcon mongodbIc = QIcon(":/robomongo/icons/mongodb_icon_for_MAC.png"); return mongodbIc; } const QIcon &GuiRegistry::connectIcon() const { static const QIcon connectIc = QIcon(":/robomongo/icons/connect_24x24.png"); return connectIc; } const QIcon &GuiRegistry::executeIcon() const { static const QIcon executeIc = QIcon(":/robomongo/icons/execute_24x24.png"); return executeIc; } const QIcon &GuiRegistry::stopIcon() const { static const QIcon stopIc = QIcon(":/robomongo/icons/stop_24x24.png"); return stopIc; } const QIcon &GuiRegistry::exportIcon() const { static const QIcon exportIc = QIcon(":/robomongo/icons/export_64x64.png"); return exportIc; } const QIcon &GuiRegistry::importIcon() const { static const QIcon importIc = QIcon(":/robomongo/icons/import_64x64.png"); return importIc; } const QIcon &GuiRegistry::deleteIcon() const { static const QIcon icon(":/robomongo/icons/delete.png"); return icon; } const QIcon &GuiRegistry::deleteIconRed() const { static const QIcon icon(":/robomongo/icons/delete_red_color.png"); return icon; } const QIcon &GuiRegistry::deleteIconMouseHovered() const { static const QIcon icon(":/robomongo/icons/delete_mouse_hovered.png"); return icon; } const QIcon &GuiRegistry::mainWindowIcon() const { static const QIcon mainWindowIc = QIcon(":/robomongo/icons/logo-256x256.png"); return mainWindowIc; } const QIcon& GuiRegistry::welcomeTabIcon() const { static const QIcon icon(":/robomongo/icons/welcome_tab_icon.png"); return icon; } const QFont &GuiRegistry::font() const { QString family = AppRegistry::instance().settingsManager()->textFontFamily(); if (family.isEmpty()) { #if defined(Q_OS_MAC) family = "Monaco"; #elif defined(Q_OS_UNIX) family = "Monospace"; #elif defined(Q_OS_WIN) family = "Courier"; #endif } int pointSize = AppRegistry::instance().settingsManager()->textFontPointSize(); if (pointSize < 1) { #if defined(Q_OS_MAC) pointSize = 12; #elif defined(Q_OS_UNIX) pointSize = -1; #elif defined(Q_OS_WIN) pointSize = 10; #endif } static QFont textFont = QFont(family, pointSize); #if defined(Q_OS_UNIX) textFont.setFixedPitch(true); #endif return textFont; } } ================================================ FILE: src/robomongo/gui/GuiRegistry.h ================================================ #pragma once #include #include #include namespace Robomongo { /** * @brief GuiRegistry is a simple registry-like singleton, that provides access to * to another various singletons (including access to the data that is stored in * resources (i.e. gui.qrc) and caches it, if needed) */ class GuiRegistry { public: /** * @brief Returns single instance of GuiRegistry */ static GuiRegistry& instance() { static GuiRegistry _instance; return _instance; } void setAlternatingColor(QAbstractItemView *view); /** * @brief Functions that provide access to various icons */ const QIcon& serverIcon() const; const QIcon& serverImportedIcon() const; const QIcon& serverPrimaryIcon() const; const QIcon& serverSecondaryIcon() const; const QIcon& replicaSetIcon() const; const QIcon& replicaSetOfflineIcon() const; const QIcon& saveIcon() const; const QIcon& openIcon() const; const QIcon& databaseIcon() const; const QIcon& collectionIcon() const; const QIcon& indexIcon() const; const QIcon& userIcon() const; const QIcon& functionIcon() const; const QIcon& maximizeIcon() const; const QIcon& minimizeIcon() const; const QIcon& undockIcon() const; const QIcon& dockIcon() const; const QIcon& textIcon() const; const QIcon& textHighlightedIcon() const; const QIcon& treeIcon() const; const QIcon& treeHighlightedIcon() const; const QIcon& tableIcon() const; const QIcon& tableHighlightedIcon() const; const QIcon& customIcon() const; const QIcon& customHighlightedIcon() const; const QIcon& rotateIcon() const; const QIcon& visualIcon() const; const QIcon& folderIcon() const; const QIcon& circleIcon() const; const QIcon& leftIcon() const; const QIcon& rightIcon() const; const QIcon& mongodbIcon() const; const QIcon& mongodbIconForMAC() const; const QIcon& connectIcon() const; const QIcon& executeIcon() const; const QIcon& stopIcon() const; const QIcon& exportIcon() const; const QIcon& importIcon() const; const QIcon& deleteIcon() const; const QIcon& deleteIconRed() const; const QIcon& deleteIconMouseHovered() const; const QIcon& mainWindowIcon() const; const QIcon& bsonObjectIcon() const; const QIcon& bsonArrayIcon() const; const QIcon& bsonStringIcon() const; const QIcon& bsonIntegerIcon() const; const QIcon& bsonDoubleIcon() const; const QIcon& bsonNumberDecimalIcon() const; const QIcon& bsonDateTimeIcon() const; const QIcon& bsonBinaryIcon() const; const QIcon& bsonNullIcon() const; const QIcon& bsonBooleanIcon() const; const QIcon& noMarkIcon() const; const QIcon& yesMarkIcon() const; const QIcon& skipMarkIcon() const; const QIcon& questionMarkIcon() const; const QIcon& timeIcon() const; const QIcon& keyIcon() const; const QIcon& showIcon() const; const QIcon& hideIcon() const; const QIcon& plusIcon() const; const QIcon& minusIcon() const; const QIcon& welcomeTabIcon() const; const QBrush& typeBrush() const; const QFont& font() const; private: /** * @brief Private, because this is singleton */ GuiRegistry(); }; } ================================================ FILE: src/robomongo/gui/MainWindow.cpp ================================================ #include "robomongo/gui/MainWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/settings/SshSettings.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/gui/widgets/LogWidget.h" #include "robomongo/gui/widgets/explorer/ExplorerWidget.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerTreeWidget.h" #include "robomongo/gui/widgets/workarea/WorkAreaTabWidget.h" #include "robomongo/gui/widgets/workarea/QueryWidget.h" #include "robomongo/gui/widgets/workarea/QueryWidget.h" #include "robomongo/gui/widgets/workarea/WelcomeTab.h" #include "robomongo/gui/dialogs/ConnectionsDialog.h" #include "robomongo/gui/dialogs/AboutDialog.h" #include "robomongo/gui/dialogs/PreferencesDialog.h" #include "robomongo/gui/dialogs/ExportDialog.h" #include "robomongo/gui/dialogs/ChangeShellTimeoutDialog.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/AppStyle.h" namespace { void setToolBarIconSize(QToolBar *toolBar) { #if defined(Q_OS_MAC) const int size = 20; #else const int size = 24; #endif toolBar->setIconSize(QSize(size, size)); } void saveViewMode(Robomongo::ViewMode mode) { Robomongo::AppRegistry::instance().settingsManager()->setViewMode(mode); Robomongo::AppRegistry::instance().settingsManager()->save(); } void saveAutoExpand(bool isExpand) { Robomongo::AppRegistry::instance().settingsManager()->setAutoExpand(isExpand); Robomongo::AppRegistry::instance().settingsManager()->save(); } void saveAutoExec(bool isAutoExec) { Robomongo::AppRegistry::instance().settingsManager()->setAutoExec(isAutoExec); Robomongo::AppRegistry::instance().settingsManager()->save(); } void saveMinimizeToTraySettings(bool isMinimizingToTray) { Robomongo::AppRegistry::instance().settingsManager()->setMinimizeToTray(isMinimizingToTray); Robomongo::AppRegistry::instance().settingsManager()->save(); } void saveLineNumbers(bool showLineNumbers) { Robomongo::AppRegistry::instance().settingsManager()->setLineNumbers(showLineNumbers); Robomongo::AppRegistry::instance().settingsManager()->save(); } } /* End of anonymous namespace */ namespace Robomongo { /* -------------------------------- ConnectionMenu ---------------------------- */ class ConnectionMenu : public QMenu { public: ConnectionMenu(QWidget *parent) : QMenu(parent) {} protected: virtual void keyPressEvent(QKeyEvent *event) override { if (event->key() == Qt::Key_F12) { hide(); } else { QMenu::keyPressEvent(event); } } }; /* -------------------------------- MainWindow --------------------------------- */ // In milliseconds constexpr int ONE_HOUR = 3'600'000; // (3'600'000 msec = 60 * 60 * 1000 msec) constexpr int THIRTY_SECONDS = 30'000; constexpr int ONE_SECOND = 1'000; MainWindow::MainWindow() : BaseClass(), _logDock(nullptr), _workArea(nullptr), _explorer(nullptr), _app(AppRegistry::instance().app()), _connectionsMenu(nullptr), _connectButton(nullptr), _viewMenu(nullptr), _toolbarsMenu(nullptr), _connectAction(nullptr), _openAction(nullptr), _saveAction(nullptr), _saveAsAction(nullptr), _executeAction(nullptr), _stopAction(nullptr), _orientationAction(nullptr), _execToolBar(nullptr), #if defined(Q_OS_WIN) _trayIcon(nullptr), #endif _allowExit(false) { QColor background = palette().window().color(); QString controlKey = "Ctrl"; #if defined(Q_OS_MAC) QString explorerColor = "#EFEFEF"; // was #CED6DF" controlKey = QChar(0x2318); // "Command" key aka Cauliflower setUnifiedTitleAndToolBarOnMac(true); #elif defined(Q_OS_LINUX) QString explorerColor = background.darker(103).name(); #else QString explorerColor = background.lighter(103).name(); #endif qApp->setStyleSheet(QString( "QWidget#queryWidget { background-color:#E7E5E4; margin: 0px; padding:0px; } \n" "Robomongo--ExplorerTreeWidget#explorerTree { padding: 1px 0px 0px 0px; background-color: %1; border: 0px; } \n" "QMainWindow::separator { background: #E7E5E4; width: 1px; } \n" "QMessageBox { messagebox-text-interaction-flags: 5; }" // Make QMessageBox text selectable ).arg(explorerColor)); _openAction = new QAction(GuiRegistry::instance().openIcon(), tr("&Open..."), this); _openAction->setToolTip(QString("Load script from the file to the currently opened shell (%1 + O)").arg(controlKey)); _openAction->setShortcuts(QKeySequence::Open); VERIFY(connect(_openAction, SIGNAL(triggered()), this, SLOT(open()))); _saveAction = new QAction(GuiRegistry::instance().saveIcon(), tr("&Save"), this); _saveAction->setShortcuts(QKeySequence::Save); _saveAction->setToolTip(QString("Save script of the currently opened shell to the file (%1 + S)").arg(controlKey)); VERIFY(connect(_saveAction, SIGNAL(triggered()), this, SLOT(save()))); _saveAsAction = new QAction(tr("Save &As..."), this); _saveAsAction->setShortcuts(QKeySequence::SaveAs); VERIFY(connect(_saveAsAction, SIGNAL(triggered()), this, SLOT(saveAs()))); // Exit action QAction *exitAction = new QAction("&Exit", this); exitAction->setShortcuts(QKeySequence::Quit); VERIFY(connect(exitAction, SIGNAL(triggered()), this, SLOT(exit()))); // Connect action _connectAction = new QAction("&Connect...", this); _connectAction->setShortcuts(QKeySequence::New); _connectAction->setIcon(GuiRegistry::instance().connectIcon()); _connectAction->setIconText("Connect"); _connectAction->setToolTip(QString("Connect to local or remote MongoDB instance (%1 + N)").arg(controlKey)); VERIFY(connect(_connectAction, SIGNAL(triggered()), this, SLOT(manageConnections()))); _connectionsMenu = new ConnectionMenu(this); VERIFY(connect(_connectionsMenu, SIGNAL(triggered(QAction*)), this, SLOT(connectToServer(QAction*)))); updateConnectionsMenu(); _connectButton = new QToolButton(); _connectButton->setText("&Connect..."); _connectButton->setIcon(GuiRegistry::instance().connectIcon()); _connectButton->setFocusPolicy(Qt::NoFocus); _connectButton->setToolTip(QString("Connect to local or remote MongoDB instance (%1 + N)").arg(controlKey)); _connectButton->setToolButtonStyle(Qt::ToolButtonIconOnly); #if !defined(Q_OS_MAC) _connectButton->setMenu(_connectionsMenu); _connectButton->setPopupMode(QToolButton::MenuButtonPopup); #endif VERIFY(connect(_connectButton, SIGNAL(clicked()), this, SLOT(manageConnections()))); QWidgetAction *connectButtonAction = new QWidgetAction(this); connectButtonAction->setDefaultWidget(_connectButton); // Tray icon #if defined(Q_OS_WIN) _trayIcon = new QSystemTrayIcon(GuiRegistry::instance().mainWindowIcon()); VERIFY(connect(_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)))); auto trayMinimizeAction = new QAction("Minimize to Tray", _trayIcon); VERIFY(connect(trayMinimizeAction, SIGNAL(triggered()), this, SLOT(toggleMinimize()))); auto trayExitAction = new QAction("Exit", _trayIcon); VERIFY(connect(trayExitAction, SIGNAL(triggered()), this, SLOT(exit()))); auto contextMenu = new QMenu(); contextMenu->addAction(trayMinimizeAction); contextMenu->addSeparator(); contextMenu->addAction(trayExitAction); _trayIcon->setContextMenu(contextMenu); #endif // Orientation action _orientationAction = new QAction("&Rotate", this); _orientationAction->setShortcut(Qt::Key_F10); _orientationAction->setIcon(GuiRegistry::instance().rotateIcon()); _orientationAction->setToolTip("Toggle orientation of results view (F10)"); VERIFY(connect(_orientationAction, SIGNAL(triggered()), this, SLOT(toggleOrientation()))); // read view mode setting ViewMode viewMode = AppRegistry::instance().settingsManager()->viewMode(); // Text mode action auto textModeAction = new QAction("&Text Mode", this); textModeAction->setShortcut(Qt::Key_F4); textModeAction->setIcon(GuiRegistry::instance().textHighlightedIcon()); textModeAction->setToolTip("Show current tab in text mode, and make this mode default for all subsequent queries (F4)"); textModeAction->setCheckable(true); textModeAction->setChecked(viewMode == Text); VERIFY(connect(textModeAction, SIGNAL(triggered()), this, SLOT(enterTextMode()))); // Tree mode action QAction *treeModeAction = new QAction("&Tree Mode", this); treeModeAction->setShortcut(Qt::Key_F2); treeModeAction->setIcon(GuiRegistry::instance().treeHighlightedIcon()); treeModeAction->setToolTip("Show current tab in tree mode, and make this mode default for all subsequent queries (F3)"); treeModeAction->setCheckable(true); treeModeAction->setChecked(viewMode == Tree); VERIFY(connect(treeModeAction, SIGNAL(triggered()), this, SLOT(enterTreeMode()))); // Tree mode action QAction *tableModeAction = new QAction("T&able Mode", this); tableModeAction->setShortcut(Qt::Key_F3); tableModeAction->setIcon(GuiRegistry::instance().tableHighlightedIcon()); tableModeAction->setToolTip("Show current tab in table mode, and make this mode default for all subsequent queries (F3)"); tableModeAction->setCheckable(true); tableModeAction->setChecked(viewMode == Table); VERIFY(connect(tableModeAction, SIGNAL(triggered()), this, SLOT(enterTableMode()))); // Custom mode action QAction *customModeAction = new QAction("&Custom Mode", this); //customModeAction->setShortcut(Qt::Key_F2); customModeAction->setIcon(GuiRegistry::instance().customHighlightedIcon()); customModeAction->setToolTip("Show current tab in custom mode if possible, and make this mode default for all subsequent queries (F2)"); customModeAction->setCheckable(true); customModeAction->setChecked(viewMode == Custom); VERIFY(connect(customModeAction, SIGNAL(triggered()), this, SLOT(enterCustomMode()))); // Execute action _executeAction = new QAction(this); _executeAction->setData("Execute"); _executeAction->setIcon(GuiRegistry::instance().executeIcon()); _executeAction->setShortcut(Qt::Key_F5); _executeAction->setToolTip(QString("Execute query for current tab. If you have some selection in query text - only selection will be executed (F5 or %1 + Enter)").arg(controlKey)); VERIFY(connect(_executeAction, SIGNAL(triggered()), SLOT(executeScript()))); // Stop action _stopAction = new QAction(this); _stopAction->setData("Stop"); _stopAction->setIcon(GuiRegistry::instance().stopIcon()); _stopAction->setShortcut(Qt::Key_F6); _stopAction->setToolTip("Stop execution of currently running script. (F6)"); _stopAction->setDisabled(true); VERIFY(connect(_stopAction, SIGNAL(triggered()), SLOT(stopScript()))); // Refresh action QAction *refreshAction = new QAction("Refresh", this); refreshAction->setIcon(qApp->style()->standardIcon(QStyle::SP_BrowserReload)); VERIFY(connect(refreshAction, SIGNAL(triggered()), this, SLOT(refreshConnections()))); /*** File menu ***/ QMenu *fileMenu = menuBar()->addMenu("File"); fileMenu->addAction(_connectAction); fileMenu->addSeparator(); fileMenu->addAction(_openAction); fileMenu->addAction(_saveAction); fileMenu->addAction(_saveAsAction); fileMenu->addSeparator(); fileMenu->addAction(exitAction); /*** View menu ***/ _viewMenu = menuBar()->addMenu("View"); // adds option to toggle Explorer and Logs panels createDatabaseExplorer(); _toolbarsMenu = _viewMenu->addMenu(tr("Toolbars")); // adds Themes submenu createStylesMenu(); /*** Options menu ***/ QMenu *optionsMenu = menuBar()->addMenu("Options"); // View Mode QMenu *defaultViewModeMenu = optionsMenu->addMenu("Default View Mode"); defaultViewModeMenu->addAction(customModeAction); defaultViewModeMenu->addAction(treeModeAction); defaultViewModeMenu->addAction(tableModeAction); defaultViewModeMenu->addAction(textModeAction); optionsMenu->addSeparator(); QActionGroup *modeGroup = new QActionGroup(this); modeGroup->addAction(textModeAction); modeGroup->addAction(treeModeAction); modeGroup->addAction(tableModeAction); modeGroup->addAction(customModeAction); // Time Zone QAction *utcTime = new QAction(convertTimesToString(Utc), this); utcTime->setCheckable(true); utcTime->setChecked(AppRegistry::instance().settingsManager()->timeZone() == Utc); VERIFY(connect(utcTime, SIGNAL(triggered()), this, SLOT(setUtcTimeZone()))); QAction *localTime = new QAction(convertTimesToString(LocalTime), this); localTime->setCheckable(true); localTime->setChecked(AppRegistry::instance().settingsManager()->timeZone() == LocalTime); VERIFY(connect(localTime, SIGNAL(triggered()), this, SLOT(setLocalTimeZone()))); QMenu *timeMenu = optionsMenu->addMenu("Display Dates In..."); timeMenu->addAction(utcTime); timeMenu->addAction(localTime); QActionGroup *timeZoneGroup = new QActionGroup(this); timeZoneGroup->addAction(utcTime); timeZoneGroup->addAction(localTime); // UUID encoding QAction *defaultEncodingAction = new QAction("Do not decode (show as is)", this); defaultEncodingAction->setCheckable(true); defaultEncodingAction->setChecked(AppRegistry::instance().settingsManager()->uuidEncoding() == DefaultEncoding); VERIFY(connect(defaultEncodingAction, SIGNAL(triggered()), this, SLOT(setDefaultUuidEncoding()))); QAction *javaLegacyEncodingAction = new QAction("Use Java Encoding", this); javaLegacyEncodingAction->setCheckable(true); javaLegacyEncodingAction->setChecked(AppRegistry::instance().settingsManager()->uuidEncoding() == JavaLegacy); VERIFY(connect(javaLegacyEncodingAction, SIGNAL(triggered()), this, SLOT(setJavaUuidEncoding()))); QAction *csharpLegacyEncodingAction = new QAction("Use .NET Encoding", this); csharpLegacyEncodingAction->setCheckable(true); csharpLegacyEncodingAction->setChecked(AppRegistry::instance().settingsManager()->uuidEncoding() == CSharpLegacy); VERIFY(connect(csharpLegacyEncodingAction, SIGNAL(triggered()), this, SLOT(setCSharpUuidEncoding()))); QAction *pythonEncodingAction = new QAction("Use Python Encoding", this); pythonEncodingAction->setCheckable(true); pythonEncodingAction->setChecked(AppRegistry::instance().settingsManager()->uuidEncoding() == PythonLegacy); VERIFY(connect(pythonEncodingAction, SIGNAL(triggered()), this, SLOT(setPythonUuidEncoding()))); QMenu *uuidMenu = optionsMenu->addMenu("Legacy UUID Encoding"); uuidMenu->addAction(defaultEncodingAction); uuidMenu->addAction(javaLegacyEncodingAction); uuidMenu->addAction(csharpLegacyEncodingAction); uuidMenu->addAction(pythonEncodingAction); // Read Autocompletion Mode AutocompletionMode autocompletionMode = AppRegistry::instance().settingsManager()->autocompletionMode(); // Autocompletion QAction *autocompletionAllAction = new QAction("All", this); autocompletionAllAction->setCheckable(true); autocompletionAllAction->setChecked(autocompletionMode == AutocompleteAll); VERIFY(connect(autocompletionAllAction, SIGNAL(triggered()), this, SLOT(setShellAutocompletionAll()))); QAction *autocompletionNoCollectionNamesAction = new QAction("All (Except Collection Names)", this); autocompletionNoCollectionNamesAction->setCheckable(true); autocompletionNoCollectionNamesAction->setChecked(autocompletionMode == AutocompleteNoCollectionNames); VERIFY(connect(autocompletionNoCollectionNamesAction, SIGNAL(triggered()), this, SLOT(setShellAutocompletionNoCollectionNames()))); QAction *autocompletionNoneAction = new QAction("None", this); autocompletionNoneAction->setCheckable(true); autocompletionNoneAction->setChecked(autocompletionMode == AutocompleteNone); VERIFY(connect(autocompletionNoneAction, SIGNAL(triggered()), this, SLOT(setShellAutocompletionNone()))); QMenu *autocompletionMenu = optionsMenu->addMenu("Autocompletion Mode"); autocompletionMenu->addAction(autocompletionAllAction); autocompletionMenu->addAction(autocompletionNoCollectionNamesAction); autocompletionMenu->addAction(autocompletionNoneAction); QActionGroup *autocompletionGroup = new QActionGroup(this); autocompletionGroup->addAction(autocompletionAllAction); autocompletionGroup->addAction(autocompletionNoCollectionNamesAction); autocompletionGroup->addAction(autocompletionNoneAction); QAction *loadMongoRcJs = new QAction("Load .mongorc.js", this); loadMongoRcJs->setCheckable(true); loadMongoRcJs->setChecked(AppRegistry::instance().settingsManager()->loadMongoRcJs()); VERIFY(connect(loadMongoRcJs, SIGNAL(triggered()), this, SLOT(setLoadMongoRcJs()))); optionsMenu->addSeparator(); optionsMenu->addAction(loadMongoRcJs); optionsMenu->addSeparator(); QAction *autoExpand = new QAction("Auto Expand First Document", this); autoExpand->setCheckable(true); autoExpand->setChecked(AppRegistry::instance().settingsManager()->autoExpand()); VERIFY(connect(autoExpand, SIGNAL(triggered()), this, SLOT(toggleAutoExpand()))); optionsMenu->addAction(autoExpand); QAction *showLineNumbers = new QAction("Show Line Numbers By Default", this); showLineNumbers->setCheckable(true); showLineNumbers->setChecked(AppRegistry::instance().settingsManager()->lineNumbers()); VERIFY(connect(showLineNumbers, SIGNAL(triggered()), this, SLOT(toggleLineNumbers()))); optionsMenu->addAction(showLineNumbers); QAction *disabelConnectionShortcuts = new QAction("Disable Connection Shortcuts", this); disabelConnectionShortcuts->setCheckable(true); disabelConnectionShortcuts->setChecked(AppRegistry::instance().settingsManager()->disableConnectionShortcuts()); VERIFY(connect(disabelConnectionShortcuts, SIGNAL(triggered()), this, SLOT(setDisableConnectionShortcuts()))); optionsMenu->addAction(disabelConnectionShortcuts); QAction *autoExec = new QAction(tr("Automatically execute code in new tab"), this); autoExec->setCheckable(true); autoExec->setChecked(AppRegistry::instance().settingsManager()->autoExec()); VERIFY(connect(autoExec, SIGNAL(triggered()), this, SLOT(toggleAutoExec()))); optionsMenu->addAction(autoExec); #if defined(Q_OS_WIN) QAction *minimizeTray = new QAction("Close button should minimize to system tray"); minimizeTray->setCheckable(true); minimizeTray->setChecked(AppRegistry::instance().settingsManager()->minimizeToTray()); VERIFY(connect(minimizeTray, SIGNAL(triggered()), this, SLOT(toggleMinimizeToTray()))); optionsMenu->addAction(minimizeTray); #endif auto checkUpdates = new QAction(tr("Check For Updates"), this); checkUpdates->setCheckable(true); checkUpdates->setChecked(AppRegistry::instance().settingsManager()->checkForUpdates()); VERIFY(connect(checkUpdates, SIGNAL(triggered()), this, SLOT(toggleCheckUpdates()))); optionsMenu->addAction(checkUpdates); auto changeShellTimeout = new QAction(tr("Change Shell Timeout..."), this); VERIFY(connect(changeShellTimeout, SIGNAL(triggered()), this, SLOT(openShellTimeoutDialog()))); optionsMenu->addAction(changeShellTimeout); QAction *preferencesAction = new QAction("Preferences", this); VERIFY(connect(preferencesAction, SIGNAL(triggered()), this, SLOT(openPreferences()))); preferencesAction->setVisible(false); optionsMenu->addAction(preferencesAction); QActionGroup *uuidEncodingGroup = new QActionGroup(this); uuidEncodingGroup->addAction(defaultEncodingAction); uuidEncodingGroup->addAction(javaLegacyEncodingAction); uuidEncodingGroup->addAction(csharpLegacyEncodingAction); uuidEncodingGroup->addAction(pythonEncodingAction); /*** Window menu ***/ // Full screen action QAction *fullScreenAction = new QAction("&Full Screen", this); #if !defined(Q_OS_MAC) fullScreenAction->setShortcut(Qt::Key_F11); #else fullScreenAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F11)); #endif fullScreenAction->setVisible(true); VERIFY(connect(fullScreenAction, SIGNAL(triggered()), this, SLOT(toggleFullScreen2()))); // Minimize window QAction *minimizeAction = new QAction("&Minimize", this); minimizeAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M)); minimizeAction->setVisible(true); VERIFY(connect(minimizeAction, SIGNAL(triggered()), this, SLOT(showMinimized()))); // Next tab QAction *nexttabAction = new QAction("Select Next Tab", this); nexttabAction->setShortcuts(QKeySequence::NextChild); nexttabAction->setVisible(true); VERIFY(connect(nexttabAction, SIGNAL(triggered()), this, SLOT(selectNextTab()))); // Previous tab QAction *prevtabAction = new QAction("Select Previous Tab", this); prevtabAction->setShortcuts(QKeySequence::PreviousChild); prevtabAction->setVisible(true); VERIFY(connect(prevtabAction, SIGNAL(triggered()), this, SLOT(selectPrevTab()))); // Reload action (currently a re-execute, as does not "reload" files per issue #447) QAction *reloadAction = new QAction("Re-execute Query in Current Tab", this); reloadAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); reloadAction->setVisible(true); VERIFY(connect(reloadAction, SIGNAL(triggered()), SLOT(executeScript()))); // Duplicate tab action QAction *duplicateAction = new QAction("Duplicate Query in New Tab", this); duplicateAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_T); duplicateAction->setVisible(true); VERIFY(connect(duplicateAction, SIGNAL(triggered()), SLOT(duplicateTab()))); // Window menu QMenu *windowMenu = menuBar()->addMenu("Window"); //minimize windowMenu->addAction(fullScreenAction); windowMenu->addAction(minimizeAction); windowMenu->addSeparator(); windowMenu->addAction(nexttabAction); windowMenu->addAction(prevtabAction); windowMenu->addSeparator(); windowMenu->addAction(reloadAction); windowMenu->addAction(duplicateAction); windowMenu->addSeparator(); auto const& settings { AppRegistry::instance().settingsManager() }; if (!settings->disableHttpsFeatures()) { // Open welcome tab action auto openWelcomeTabAction = new QAction("Open/Refresh Welcome Tab", this); openWelcomeTabAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_W); openWelcomeTabAction->setVisible(true); VERIFY(connect(openWelcomeTabAction, SIGNAL(triggered()), SLOT(openWelcomeTab()))); windowMenu->addAction(openWelcomeTabAction); } auto toolbarsSettings = AppRegistry::instance().settingsManager()->toolbars(); /*** About menu ***/ QAction *aboutRobomongoAction = new QAction("&About Robo 3T...", this); VERIFY(connect(aboutRobomongoAction, SIGNAL(triggered()), this, SLOT(aboutRobomongo()))); // Options menu QMenu *helpMenu = menuBar()->addMenu("Help"); helpMenu->addAction(aboutRobomongoAction); // Toolbar QToolBar *connectToolBar = new QToolBar(tr("Connections Toolbar"), this); connectToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); connectToolBar->addAction(connectButtonAction); connectToolBar->setShortcutEnabled(1, true); connectToolBar->setMovable(false); connectToolBar->setVisible(true /*toolbarsSettings["connect"].toBool()*/); _toolbarsMenu->addAction(connectToolBar->toggleViewAction()); VERIFY(connect(connectToolBar->toggleViewAction(), SIGNAL(triggered(bool)), this, SLOT(onConnectToolbarVisibilityChanged(bool)))); setToolBarIconSize(connectToolBar); addToolBar(connectToolBar); QToolBar *openSaveToolBar = new QToolBar(tr("Open/Save Toolbar"), this); openSaveToolBar->addAction(_openAction); openSaveToolBar->addAction(_saveAction); openSaveToolBar->setMovable(false); openSaveToolBar->setVisible(true /*toolbarsSettings["open_save"].toBool()*/); _toolbarsMenu->addAction(openSaveToolBar->toggleViewAction()); VERIFY(connect(openSaveToolBar->toggleViewAction(), SIGNAL(triggered(bool)), this, SLOT(onOpenSaveToolbarVisibilityChanged(bool)))); setToolBarIconSize(openSaveToolBar); addToolBar(openSaveToolBar); _execToolBar = new QToolBar(tr("Execution Toolbar"), this); _execToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); _execToolBar->addAction(_executeAction); _execToolBar->addAction(_stopAction); _execToolBar->addAction(_orientationAction); _execToolBar->setShortcutEnabled(1, true); _execToolBar->setMovable(false); _execToolBar->setVisible(true /*toolbarsSettings["exec"].toBool()*/); setToolBarIconSize(_execToolBar); addToolBar(_execToolBar); _updateLabel = new QLabel; _updateLabel->setWordWrap(true); _updateLabel->setOpenExternalLinks(true); _updateLabel->setTextFormat(Qt::TextFormat::RichText); _updateLabel->setIndent(_updateLabel->fontMetrics().width("T")); _closeButton = new QPushButton; _closeButton->setIcon(QIcon(":/robomongo/icons/close_hover_16x16.png")); _closeButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum); _closeButton->setMouseTracking(true); _closeButton->setAttribute(Qt::WA_Hover); _closeButton->installEventFilter(this); VERIFY(connect(_closeButton, SIGNAL(clicked()), this, SLOT(on_closeButton_clicked()))); auto updateBarLay = new QHBoxLayout; updateBarLay->addWidget(_updateLabel); updateBarLay->addWidget(_closeButton, Qt::AlignRight); updateBarLay->setSpacing(0); updateBarLay->setMargin(0); auto updateBarWid = new QWidget; updateBarWid->setLayout(updateBarLay); _updateBar = new QToolBar("Updates Toolbar"); _updateBar->addWidget(updateBarWid); _updateBar->setStyleSheet("background-color: #b3e0ff; border: none;"); // blue addToolBarBreak(); addToolBar(_updateBar); _updateBar->setHidden(true); _updateBar->setMovable(false); _toolbarsMenu->addAction(_execToolBar->toggleViewAction()); VERIFY(connect(_execToolBar->toggleViewAction(), SIGNAL(triggered(bool)), this, SLOT(onExecToolbarVisibilityChanged(bool)))); createTabs(); createStatusBar(); setWindowTitle("Robo 3T - " + QString(PROJECT_VERSION_SHORT)); setWindowIcon(GuiRegistry::instance().mainWindowIcon()); QTimer::singleShot(0, this, SLOT(manageConnections())); updateMenus(); _updateMenusAtStart = false; AppRegistry::instance().bus()->subscribe(this, ConnectionFailedEvent::Type); AppRegistry::instance().bus()->subscribe(this, ScriptExecutedEvent::Type); AppRegistry::instance().bus()->subscribe(this, ScriptExecutingEvent::Type); AppRegistry::instance().bus()->subscribe(this, QueryWidgetUpdatedEvent::Type); AppRegistry::instance().bus()->subscribe(this, OperationFailedEvent::Type); restoreWindowSettings(); // Catch application windows focus changes VERIFY(connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(on_focusChanged()))); _networkAccessManager = new QNetworkAccessManager; VERIFY(connect(_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_networkReply(QNetworkReply*)))); if (!settings->disableHttpsFeatures() && settings->checkForUpdates()) { // First check for updates THIRTY_SECONDS after program start QTimer::singleShot(THIRTY_SECONDS, this, SLOT(checkUpdates())); // Then, check for updates every 1 hour auto const timer { new QTimer(this) }; VERIFY(connect(timer, SIGNAL(timeout()), this, SLOT(checkUpdates()))); timer->start(ONE_HOUR); } setUnifiedTitleAndToolBarOnMac(false); // https://bugreports.qt.io/browse/QTBUG-68946 } void MainWindow::createStylesMenu() { _viewMenu->addSeparator(); QMenu *styles = _viewMenu->addMenu("Theme"); QStringList supportedStyles = AppStyleUtils::getSupportedStyles(); QActionGroup *styleGroup = new QActionGroup(this); VERIFY(connect(styleGroup, SIGNAL(triggered(QAction *)), this, SLOT(changeStyle(QAction *)))); const QString ¤tStyle = AppRegistry::instance().settingsManager()->currentStyle(); for (QStringList::const_iterator it = supportedStyles.begin(); it != supportedStyles.end(); ++it) { const QString &style = *it; QAction *styleAction = new QAction(style, this); styleAction->setCheckable(true); styleAction->setChecked(style == currentStyle); styleGroup->addAction(styleAction); styles->addAction(styleAction); } } void MainWindow::createStatusBar() { QColor windowColor = palette().window().color(); QColor buttonBgColor = windowColor.lighter(105); QColor buttonBorderBgColor = windowColor.darker(112); QColor buttonPressedColor = windowColor.darker(102); QToolButton *log = new QToolButton(this); log->setText("Logs"); log->setCheckable(true); log->setDefaultAction(_logDock->toggleViewAction()); log->setStyleSheet(QString( "QToolButton {" " background-color: %1;" " border-style: outset;" " border-width: 1px;" " border-radius: 4px;" " border-color: %2;" " padding: 1px 10px 1px 10px;" "} \n" "" "QToolButton:checked, QToolButton:pressed {" " background-color: %3;" " border-style: inset;" "}") .arg(buttonBgColor.name()) .arg(buttonBorderBgColor.name()) .arg(buttonPressedColor.name())); statusBar()->insertWidget(0, log); statusBar()->setStyleSheet("QStatusBar::item { border: 0px solid black };"); } void MainWindow::changeStyle(QAction *ac) { const QString &text = ac->text(); AppStyleUtils::applyStyle(text); AppRegistry::instance().settingsManager()->setCurrentStyle(text); AppRegistry::instance().settingsManager()->save(); } void MainWindow::exit() { _allowExit = true; close(); } void MainWindow::restoreWindowSettings() { QSettings settings("3T", "Robomongo"); // Restore settings if registery key exists, otherwise resize as app started for the first time. if (settings.contains("MainWindow/geometry")) { restoreGeometry(settings.value("MainWindow/geometry").toByteArray()); } else { // Resize main window. We are trying to keep it "almost" maximized. QRect screenGeometry = QApplication::desktop()->availableGeometry(); int horizontalMargin = static_cast(screenGeometry.width() * 0.1); int verticalMargin = static_cast(screenGeometry.height() * 0.1); int _width = screenGeometry.width() - horizontalMargin; int _height = screenGeometry.height() - verticalMargin; resize(QSize(_width, _height)); // Center main window int x = (screenGeometry.width() - width()) / 2; int y = (screenGeometry.height() - height()) / 2; move(x, y); } } void MainWindow::saveWindowSettings() const { QSettings settings("3T", "Robomongo"); settings.setValue("MainWindow/geometry", saveGeometry()); } void MainWindow::adjustUpdatesBarHeight() { if (!AppRegistry::instance().settingsManager()->checkForUpdates() || !_updateBar->isVisible()) return; QTextDocument doc; doc.setHtml(_updateLabel->text()); int const strWidth = _updateLabel->fontMetrics().width(doc.toPlainText()); int const lineHeight = _updateLabel->fontMetrics().height(); int const widthForUpdateStr = width() - _closeButton->width(); if (0 == widthForUpdateStr) return; #ifdef __APPLE__ _updateLabel->setFixedHeight((strWidth / widthForUpdateStr + 1) * lineHeight * 1.3); #else _updateLabel->setFixedHeight((strWidth / widthForUpdateStr + 1) * lineHeight); #endif } void MainWindow::open() { QueryWidget *wid = _workArea->currentQueryWidget(); if (wid) { wid->openFile(); } else { // todo: currently this case not handled properly, since "Open" button is // disabled when no tabs exist auto connectionsCopy = AppRegistry::instance().settingsManager()->connections(); if (connectionsCopy.size() == 1) { ScriptInfo inf = ScriptInfo(QString()); if (inf.loadFromFile()) { // todo: for now do not open new shell _app->openShell(nullptr, connectionsCopy.at(0), inf); } } } } void MainWindow::save() { QueryWidget *wid = _workArea->currentQueryWidget(); if (wid) { wid->saveToFile(); } } void MainWindow::saveAs() { QueryWidget *wid = _workArea->currentQueryWidget(); if (wid) { wid->savebToFileAs(); } } void MainWindow::updateConnectionsMenu() { _connectionsMenu->clear(); int number = 1; // Populate list with connections SettingsManager::ConnectionSettingsContainerType connections = AppRegistry::instance().settingsManager()->connections(); for (SettingsManager::ConnectionSettingsContainerType::const_iterator it = connections.begin(); it != connections.end(); ++it) { ConnectionSettings *connection = *it; QAction *action = new QAction(QtUtils::toQString(connection->getReadableName()), this); action->setData(QVariant::fromValue(connection)); if (number <= 9 && !AppRegistry::instance().settingsManager()->disableConnectionShortcuts()) { action->setShortcut(QKeySequence(QString("Alt+").append(QString::number(number)))); } _connectionsMenu->addAction(action); ++number; } if (AppRegistry::instance().settingsManager()->connections().size() > 0) _connectionsMenu->addSeparator(); // Connect action QAction *connectAction = new QAction("&Manage Connections...", this); connectAction->setIcon(GuiRegistry::instance().connectIcon()); connectAction->setToolTip("Connect to MongoDB"); VERIFY(connect(connectAction, SIGNAL(triggered()), this, SLOT(manageConnections()))); _connectionsMenu->addAction(connectAction); } WelcomeTab* MainWindow::getWelcomeTab() { return _workArea->getWelcomeTab(); } void MainWindow::showQueryWidgetProgressBar() const { if (QueryWidget *widget = _workArea->currentQueryWidget()) widget->showProgress(); } void MainWindow::hideQueryWidgetProgressBar() const { if (QueryWidget *widget = _workArea->currentQueryWidget()) widget->hideProgress(); } void MainWindow::manageConnections() { #if defined(Q_OS_WIN) _trayIcon->hide(); // hide the tray icon so the main window can't be hidden behind the connections dialog #endif static bool checkForImported = true; ConnectionsDialog dialog(AppRegistry::instance().settingsManager(), checkForImported, this); int result = dialog.exec(); checkForImported = false; // save settings and update connection menu AppRegistry::instance().settingsManager()->save(); updateConnectionsMenu(); if (result == QDialog::Accepted) { ConnectionSettings *selected = dialog.selectedConnection(); selected->sshSettings()->setLogLevel(1); if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) { // Enable debug level of logging for SSH if (selected->sshSettings()->enabled()) { selected->sshSettings()->setLogLevel(100); } // Show log pannel toggleLogs(true); } try { _app->openServer(selected, ConnectionPrimary); } catch(const std::exception &) { QString message = QString("Cannot connect to MongoDB (%1)").arg(QtUtils::toQString(selected->getFullAddress())); QMessageBox::information(this, "Error", message); } } #if defined(Q_OS_WIN) _trayIcon->show(); // show the tray icon once the connections dialog is gone #endif // on linux focus is lost - we need to activate main window back activateWindow(); } void MainWindow::toggleOrientation() { QueryWidget *widget = _workArea->currentQueryWidget(); if (!widget) return; widget->toggleOrientation(); } void MainWindow::enterTextMode() { saveViewMode(Text); QueryWidget *widget = _workArea->currentQueryWidget(); if (!widget) return; widget->enterTextMode(); } void MainWindow::enterTreeMode() { saveViewMode(Tree); QueryWidget *widget = _workArea->currentQueryWidget(); if (!widget) return; widget->enterTreeMode(); } void MainWindow::enterTableMode() { saveViewMode(Table); QueryWidget *widget = _workArea->currentQueryWidget(); if (!widget) return; widget->enterTableMode(); } void MainWindow::enterCustomMode() { saveViewMode(Custom); QueryWidget *widget = _workArea->currentQueryWidget(); if (!widget) return; widget->enterCustomMode(); } void MainWindow::toggleAutoExpand() { QAction *send = qobject_cast(sender()); saveAutoExpand(send->isChecked()); } void MainWindow::toggleAutoExec() { QAction *send = qobject_cast(sender()); saveAutoExec(send->isChecked()); } void MainWindow::toggleCheckUpdates() { auto action = qobject_cast(sender()); AppRegistry::instance().settingsManager()->setCheckForUpdates(action->isChecked()); AppRegistry::instance().settingsManager()->save(); QTimer::singleShot(ONE_SECOND, this, SLOT(checkUpdates())); } void MainWindow::openShellTimeoutDialog() { changeShellTimeoutDialog(); } void MainWindow::toggleLineNumbers() { QAction *send = qobject_cast(sender()); saveLineNumbers(send->isChecked()); } void MainWindow::executeScript() { QueryWidget *widget = _workArea->currentQueryWidget(); if (!widget) return; widget->execute(); } void MainWindow::stopScript() { QueryWidget *widget = _workArea->currentQueryWidget(); if (!widget) return; widget->stop(); } void MainWindow::toggleFullScreen2() { if (windowState() == Qt::WindowFullScreen) showNormal(); else showFullScreen(); } void MainWindow::selectNextTab() { _workArea->nextTab(); } void MainWindow::selectPrevTab() { _workArea->previousTab(); } void MainWindow::duplicateTab() { QueryWidget *widget = _workArea->currentQueryWidget(); if (!widget) return; widget->duplicate(); } void MainWindow::refreshConnections() { QToolTip::showText(QPoint(0, 0), QString("Refresh not working yet... :
Ctrl+D : push Button")); } void MainWindow::aboutRobomongo() { AboutDialog dlg(this); dlg.exec(); } void MainWindow::openPreferences() { PreferencesDialog dlg(this); dlg.exec(); } void MainWindow::openWelcomeTab() { _workArea->openWelcomeTab(); } void MainWindow::setDefaultUuidEncoding() { AppRegistry::instance().settingsManager()->setUuidEncoding(DefaultEncoding); AppRegistry::instance().settingsManager()->save(); } void MainWindow::setJavaUuidEncoding() { AppRegistry::instance().settingsManager()->setUuidEncoding(JavaLegacy); AppRegistry::instance().settingsManager()->save(); } void MainWindow::setCSharpUuidEncoding() { AppRegistry::instance().settingsManager()->setUuidEncoding(CSharpLegacy); AppRegistry::instance().settingsManager()->save(); } void MainWindow::setPythonUuidEncoding() { AppRegistry::instance().settingsManager()->setUuidEncoding(PythonLegacy); AppRegistry::instance().settingsManager()->save(); } void MainWindow::setShellAutocompletionAll() { AppRegistry::instance().settingsManager()->setAutocompletionMode(AutocompleteAll); AppRegistry::instance().settingsManager()->save(); } void MainWindow::setShellAutocompletionNoCollectionNames() { AppRegistry::instance().settingsManager()->setAutocompletionMode(AutocompleteNoCollectionNames); AppRegistry::instance().settingsManager()->save(); } void MainWindow::setShellAutocompletionNone() { AppRegistry::instance().settingsManager()->setAutocompletionMode(AutocompleteNone); AppRegistry::instance().settingsManager()->save(); } void MainWindow::setUtcTimeZone() { AppRegistry::instance().settingsManager()->setTimeZone(Utc); AppRegistry::instance().settingsManager()->save(); } void MainWindow::setLocalTimeZone() { AppRegistry::instance().settingsManager()->setTimeZone(LocalTime); AppRegistry::instance().settingsManager()->save(); } void MainWindow::setDisableConnectionShortcuts() { QAction *send = qobject_cast(sender()); AppRegistry::instance().settingsManager()->setDisableConnectionShortcuts(send->isChecked()); AppRegistry::instance().settingsManager()->save(); updateConnectionsMenu(); } void MainWindow::setLoadMongoRcJs() { QAction *send = qobject_cast(sender()); AppRegistry::instance().settingsManager()->setLoadMongoRcJs(send->isChecked()); AppRegistry::instance().settingsManager()->save(); } void MainWindow::toggleLogs(bool show) { _logDock->setVisible(show); } void MainWindow::connectToServer(QAction *connectionAction) { QVariant data = connectionAction->data(); ConnectionSettings *ptr = data.value(); try { _app->openServer(ptr, ConnectionPrimary); } catch(const std::exception &) { QString message = QString("Cannot connect to the MongoDB at %1.") .arg(QtUtils::toQString(ptr->getFullAddress())); QMessageBox::information(this, "Error", message); } } void MainWindow::handle(ConnectionFailedEvent *event) { // Handle only Primary connections if (event->connectionType != ConnectionPrimary) return; // Very temporary solution to prevent multiple connection error messages // from both SshTunnelWorker and MongoWorker static int lastServerHandle = -1; if (event->serverHandle <= lastServerHandle) return; lastServerHandle = event->serverHandle; QMessageBox::critical(this, "Error", QtUtils::toQString(event->message)); } void MainWindow::handle(ScriptExecutingEvent *) { _stopAction->setDisabled(false); _executeAction->setDisabled(true); } void MainWindow::handle(ScriptExecutedEvent *) { _stopAction->setDisabled(true); _executeAction->setDisabled(false); } void MainWindow::handle(OperationFailedEvent *event) { std::stringstream ss; ss << event->userFriendlyErrorMessage << std::endl << std::endl << "Error:" << std::endl << event->technicalErrorMessage; QMessageBox::critical(NULL, "Operation failed", QtUtils::toQString(ss.str())); } void MainWindow::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_F12) { _connectButton->showMenu(); return; } BaseClass::keyPressEvent(event); } void MainWindow::closeEvent(QCloseEvent *event) { AppRegistry::instance().settingsManager()->setProgramExitedNormally(true); AppRegistry::instance().settingsManager()->save(); saveWindowSettings(); #if defined(Q_OS_WIN) if (AppRegistry::instance().settingsManager()->minimizeToTray() && !_allowExit) { event->ignore(); hide(); // hide the window because it can be reopened with the tray icon } else { if (_trayIcon->isVisible()) _trayIcon->hide(); QMainWindow::closeEvent(event); } #else QMainWindow::closeEvent(event); #endif } void MainWindow::hideEvent(QHideEvent *event) { #if defined(Q_OS_WIN) if (_trayIcon->contextMenu()->actions().size() > 0 && isHidden()) { _trayIcon->contextMenu()->actions().at(0)->setText("Show Robo 3T"); } #endif } void MainWindow::showEvent(QShowEvent *event) { #if defined(Q_OS_WIN) if (_trayIcon->contextMenu()->actions().size() > 0) { _trayIcon->contextMenu()->actions().at(0)->setText("Minimize to Tray"); } #endif } bool MainWindow::eventFilter(QObject *target, QEvent *event) { auto closeUpdatesBarButton = qobject_cast(target); if (!closeUpdatesBarButton) return false; if (event->type() == QEvent::HoverEnter) { closeUpdatesBarButton->setIcon(QIcon(":/robomongo/icons/close_hover_16x16_original.png")); return true; } else if (event->type() == QEvent::HoverLeave) { closeUpdatesBarButton->setIcon(QIcon(":/robomongo/icons/close_hover_16x16.png")); return true; } return QWidget::eventFilter(target, event); } void MainWindow::resizeEvent(QResizeEvent* event) { QMainWindow::resizeEvent(event); adjustUpdatesBarHeight(); } void MainWindow::handle(QueryWidgetUpdatedEvent *event) { _orientationAction->setEnabled(event->numOfResults() > 1); } void MainWindow::createDatabaseExplorer() { _explorer = new ExplorerWidget(this); AppRegistry::instance().bus()->subscribe(_explorer, ConnectingEvent::Type); AppRegistry::instance().bus()->subscribe(_explorer, ConnectionFailedEvent::Type); AppRegistry::instance().bus()->subscribe(_explorer, ConnectionEstablishedEvent::Type); QDockWidget *explorerDock = new QDockWidget(tr("Database Explorer")); explorerDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); explorerDock->setWidget(_explorer); explorerDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); QWidget *titleWidget = new QWidget(this); // this lines simply remove explorerDock->setTitleBarWidget(titleWidget); // title bar widget. explorerDock->setVisible(true); // Prior to v0.9 it was: // explorerDock->setVisible(AppRegistry::instance().settingsManager()->toolbars()["explorer"].toBool()); QAction *actionExp = explorerDock->toggleViewAction(); // Adjust any parameter you want. actionExp->setText(QString("&Explorer")); actionExp->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E)); actionExp->setStatusTip(QString("Press to show/hide Database Explorer panel.")); actionExp->setChecked(explorerDock->isVisible()); VERIFY(connect(actionExp, SIGNAL(triggered(bool)), this, SLOT(onExplorerVisibilityChanged(bool)))); // Install action in the menu. _viewMenu->addAction(actionExp); addDockWidget(Qt::LeftDockWidgetArea, explorerDock); LogWidget *log = new LogWidget(this); VERIFY(connect(&Logger::instance(), SIGNAL(printed(const QString&, mongo::logger::LogSeverity)), log, SLOT(addMessage(const QString&, mongo::logger::LogSeverity)))); _logDock = new QDockWidget(tr("Logs")); _logDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea); _logDock->setWidget(log); _logDock->setFeatures(QDockWidget::DockWidgetClosable); _logDock->setVisible(false); QAction *action = _logDock->toggleViewAction(); action->setText(QString("&Logs")); action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); //action->setStatusTip(QString("Press to show/hide Logs panel.")); //commented for now because this message hides Logs button in status bar :) action->setChecked(_logDock->isVisible()); // Install action in the menu. _viewMenu->addAction(action); addDockWidget(Qt::BottomDockWidgetArea, _logDock); } void MainWindow::updateMenus() { if (!_workArea) return; bool isEnable = false; if (_updateMenusAtStart) isEnable = false; else { if (getWelcomeTab() && getWelcomeTab()->isVisible()) isEnable = false; else if (_workArea->count() == 0) isEnable = false; else isEnable = true; } _execToolBar->setEnabled(isEnable); _openAction->setEnabled(isEnable); _saveAction->setEnabled(isEnable); _saveAsAction->setEnabled(isEnable); } void MainWindow::createTabs() { _workArea = new WorkAreaTabWidget(this); AppRegistry::instance().bus()->subscribe(_workArea, OpeningShellEvent::Type); VERIFY(connect(_workArea, SIGNAL(currentChanged(int)), this, SLOT(updateMenus()))); VERIFY(connect(_workArea, SIGNAL(currentChanged(int)), this, SLOT(on_tabChange()))); QHBoxLayout *hlayout = new QHBoxLayout; hlayout->setContentsMargins(0, 3, 0, 0); hlayout->addWidget(_workArea); QWidget *window = new QWidget; window->setLayout(hlayout); setCentralWidget(window); } void MainWindow::onConnectToolbarVisibilityChanged(bool isVisible) { AppRegistry::instance().settingsManager()->setToolbarSettings("connect", isVisible); AppRegistry::instance().settingsManager()->save(); } void MainWindow::onOpenSaveToolbarVisibilityChanged(bool isVisible) { AppRegistry::instance().settingsManager()->setToolbarSettings("open_save", isVisible); AppRegistry::instance().settingsManager()->save(); } void MainWindow::onExecToolbarVisibilityChanged(bool isVisible) { AppRegistry::instance().settingsManager()->setToolbarSettings("exec", isVisible); AppRegistry::instance().settingsManager()->save(); } void MainWindow::onExplorerVisibilityChanged(bool isVisible) { AppRegistry::instance().settingsManager()->setToolbarSettings("explorer", isVisible); AppRegistry::instance().settingsManager()->save(); } void MainWindow::on_tabChange() { auto activeTab = dynamic_cast(_workArea->currentWidget()); if (activeTab) { activeTab->bringDockToFront(); } } void MainWindow::toggleMinimize() { if (isHidden()) { show(); } else { hide(); } } void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason) { if (QSystemTrayIcon::DoubleClick == reason && isHidden()) { show(); } } void MainWindow::toggleMinimizeToTray() { QAction *send = qobject_cast(sender()); saveMinimizeToTraySettings(send->isChecked()); } void MainWindow::on_focusChanged() { // If focus is on floating output window, make it's parent (which is a QueryWidget tab) as active tab auto const activeDock = dynamic_cast(qApp->activeWindow()); if (activeDock) { _workArea->setCurrentWidget(activeDock->getParentQueryWidget()); } } void MainWindow::on_networkReply(QNetworkReply* reply) { QString str(QUrl::fromPercentEncoding(reply->readAll())); if (str.contains("NO-UPDATES") || reply->error() != QNetworkReply::NoError || str.isEmpty() ) { _updateLabel->setText(""); _updateBar->setVisible(false); return; } str.replace('+', ' '); str.remove("Update,"); _updateLabel->setText(str); _updateBar->setVisible(true); adjustUpdatesBarHeight(); } void MainWindow::on_closeButton_clicked() { _updateBar->setVisible(false); } void MainWindow::checkUpdates() { auto const& settings { AppRegistry::instance().settingsManager() }; if (!settings->checkForUpdates() || settings->disableHttpsFeatures()) return; #ifdef _WIN32 QString const OS = "win"; #elif __APPLE__ QString const OS = "osx"; #elif __linux__ QString const OS = "linux"; #else QString const OS = "unknown"; #endif // Build dbVersionsConnected in following format: "3.4.3,2.6.0,..." QString dbVersionsConnected; for (auto const& version : settings->dbVersionsConnected()) dbVersionsConnected.append(version + ','); if (dbVersionsConnected.endsWith(',')) dbVersionsConnected.chop(1); // softwareId=8: Robomongo product ID QUrl url("https://updates.3t.io/check.php?os=" + OS + "&softwareId=8&softwareVersion=" + QString(PROJECT_VERSION) + "&licenseInfo=FREE&setup=" + settings->anonymousID() + "&dbVersionsConnected=" + dbVersionsConnected + "¬ify=true#"); _networkAccessManager->get(QNetworkRequest(url)); } } ================================================ FILE: src/robomongo/gui/MainWindow.h ================================================ #pragma once #include #include QT_BEGIN_NAMESPACE class QLabel; class QToolBar; class QDockWidget; class QToolButton; class QPushButton; class QTreeWidgetItem; class QNetworkReply; class QNetworkAccessManager; QT_END_NAMESPACE namespace Robomongo { class ConnectionFailedEvent; class ScriptExecutingEvent; class ScriptExecutedEvent; class OperationFailedEvent; class QueryWidgetUpdatedEvent; class WorkAreaTabWidget; class ConnectionMenu; class App; class ExplorerWidget; class WelcomeTab; class MainWindow : public QMainWindow { Q_OBJECT public: typedef QMainWindow BaseClass; MainWindow(); WelcomeTab* getWelcomeTab(); void showQueryWidgetProgressBar() const; void hideQueryWidgetProgressBar() const; public Q_SLOTS: void manageConnections(); void toggleOrientation(); void enterTextMode(); void enterTreeMode(); void enterTableMode(); void enterCustomMode(); void toggleAutoExpand(); void toggleAutoExec(); void toggleLineNumbers(); void executeScript(); void stopScript(); void toggleFullScreen2(); void selectNextTab(); void selectPrevTab(); void duplicateTab(); void refreshConnections(); void aboutRobomongo(); void open(); void save(); void saveAs(); void changeStyle(QAction *); void exit(); void setDefaultUuidEncoding(); void setJavaUuidEncoding(); void setCSharpUuidEncoding(); void setPythonUuidEncoding(); void setShellAutocompletionAll(); void setShellAutocompletionNoCollectionNames(); void setShellAutocompletionNone(); void setLoadMongoRcJs(); void setDisableConnectionShortcuts(); void toggleLogs(bool show); void connectToServer(QAction *action); void handle(ConnectionFailedEvent *event); void handle(ScriptExecutingEvent *event); void handle(ScriptExecutedEvent *event); void handle(QueryWidgetUpdatedEvent *event); void handle(OperationFailedEvent *event); protected: void keyPressEvent(QKeyEvent *event) override; void closeEvent(QCloseEvent *event) override; void hideEvent(QHideEvent *event) override; void showEvent(QShowEvent *event) override; bool eventFilter(QObject *target, QEvent *event) override; void resizeEvent(QResizeEvent* event) override; private Q_SLOTS: void updateMenus(); void setUtcTimeZone(); void setLocalTimeZone(); void openPreferences(); void openWelcomeTab(); void onConnectToolbarVisibilityChanged(bool isVisisble); void onOpenSaveToolbarVisibilityChanged(bool isVisisble); void onExecToolbarVisibilityChanged(bool isVisisble); void onExplorerVisibilityChanged(bool isVisisble); void on_tabChange(); void toggleMinimize(); void trayActivated(QSystemTrayIcon::ActivationReason reason); void toggleMinimizeToTray(); // On application focus changes void on_focusChanged(); void on_networkReply(QNetworkReply* reply); void on_closeButton_clicked(); void checkUpdates(); void toggleCheckUpdates(); void openShellTimeoutDialog(); private: void updateConnectionsMenu(); void createDatabaseExplorer(); void createTabs(); void createStylesMenu(); void createStatusBar(); void restoreWindowSettings(); void saveWindowSettings() const; void adjustUpdatesBarHeight(); QDockWidget *_logDock; WorkAreaTabWidget *_workArea; ExplorerWidget* _explorer; App *_app; ConnectionMenu *_connectionsMenu; QToolButton *_connectButton; QMenu *_viewMenu; QMenu *_toolbarsMenu; QAction *_connectAction; // Open/Save tool bar QAction *_openAction; QAction *_saveAction; QAction *_saveAsAction; // Execution tool bar QAction *_executeAction; QAction *_stopAction; QAction *_orientationAction; QToolBar *_execToolBar; QToolBar *_updateBar; QLabel *_updateLabel; QPushButton* _closeButton; QNetworkAccessManager *_networkAccessManager; #if defined(Q_OS_WIN) QSystemTrayIcon *_trayIcon; #endif bool _allowExit; bool _updateMenusAtStart = true; }; } ================================================ FILE: src/robomongo/gui/dialogs/AboutDialog.cpp ================================================ #include "robomongo/gui/dialogs/AboutDialog.h" #include #include #include #include #include #include #include #include #include #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/utils/QtUtils.h" namespace { auto const YEAR { QString::number(QDate::currentDate().year()) }; auto const MONTH { QString::number(QDate::currentDate().month()) }; const QString description { "

" PROJECT_NAME_TITLE " " PROJECT_VERSION " (Build " BUILD_NUMBER + QString(" - ") + MONTH + "/" + YEAR + ")

" "Shell-centric MongoDB management tool.
" "Submit issues/proposals on GitHub.
" "
" "" PROJECT_DOMAIN "
" "Copyright 2014-" + YEAR + " " PROJECT_COMPANYNAME ". All rights reserved.
" "
" "The program is provided AS IS with NO WARRANTY OF ANY KIND, " "INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A " "PARTICULAR PURPOSE.
" "
" "Dependencies:
" "Mongo-Shell " MongoDB_VERSION "
" "Qt " PROJECT_QT_VERSION "
" "OpenSSL " OPENSSL_VERSION "
" "libssh2 " LIBSSH2_VERSION "
" "QJson " QJSON_VERSION "
" "QScintilla " QSCINTILLA_VERSION_STR "
" "Google Test " GOOGLE_TEST_VERSION "
" "ESPRIMA " ESPRIMA_VERSION "
" "
" "Credits:
" "Some icons are designed by Freepik www.flaticon.com" "
" }; } namespace Robomongo { AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent) { setWindowTitle("About " PROJECT_NAME_TITLE); //// About tab auto aboutTab = new QWidget; aboutTab->setWindowIcon(GuiRegistry::instance().mainWindowIcon()); aboutTab->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); auto layout = new QGridLayout(this); layout->setSizeConstraint(QLayout::SetFixedSize); auto copyRightLabel = new QLabel(description); copyRightLabel->setWordWrap(true); copyRightLabel->setOpenExternalLinks(true); copyRightLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); QIcon icon = GuiRegistry::instance().mainWindowIcon(); QPixmap iconPixmap = icon.pixmap(128, 128); auto logoLabel = new QLabel; logoLabel->setPixmap(iconPixmap); layout->addWidget(logoLabel, 0, 0, 1, 1); layout->addWidget(copyRightLabel, 0, 1, 4, 4); aboutTab->setLayout(layout); //// License Agreement tab auto licenseTab = new QWidget; auto textBrowser = new QTextBrowser; textBrowser->setOpenExternalLinks(true); textBrowser->setOpenLinks(true); QFile file(":gnu_gpl3_license.html"); if (file.open(QFile::ReadOnly | QFile::Text)) textBrowser->setText(file.readAll()); auto licenseTabLay = new QVBoxLayout; licenseTabLay->addWidget(textBrowser); licenseTab->setLayout(licenseTabLay); //// Button box auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole | QDialogButtonBox::AcceptRole)); VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); //// Main layout auto tabWidget = new QTabWidget; tabWidget->addTab(aboutTab, "About"); tabWidget->addTab(licenseTab, "License Agreement"); auto mainLayout = new QVBoxLayout; mainLayout->addWidget(tabWidget); mainLayout->addWidget(buttonBox); setLayout(mainLayout); } } ================================================ FILE: src/robomongo/gui/dialogs/AboutDialog.h ================================================ #pragma once #include namespace Robomongo { class AboutDialog : public QDialog { Q_OBJECT public: explicit AboutDialog(QWidget *parent); }; } ================================================ FILE: src/robomongo/gui/dialogs/ChangeShellTimeoutDialog.cpp ================================================ #include "robomongo/gui/dialogs/ChangeShellTimeoutDialog.h" #include #include #include #include #include #include #include #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/settings/SettingsManager.h" namespace Robomongo { void changeShellTimeoutDialog() { auto changeShellTimeoutDialog = new QDialog; auto settingsManager = AppRegistry::instance().settingsManager(); auto currentShellTimeout = new QLabel(QString::number(settingsManager->shellTimeoutSec())); auto newShellTimeout = new QLineEdit; newShellTimeout->setValidator(new QIntValidator(0, 100000)); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel); QObject::connect(buttonBox, SIGNAL(accepted()), changeShellTimeoutDialog, SLOT(accept())); QObject::connect(buttonBox, SIGNAL(rejected()), changeShellTimeoutDialog, SLOT(reject())); auto lay = new QGridLayout; auto firstLabel = new QLabel("Enter new value for Robo 3T shell timeout in seconds:\n"); lay->addWidget(firstLabel, 0, 0, 1, 2, Qt::AlignLeft); lay->addWidget(new QLabel("Current Value: "), 1, 0); lay->addWidget(currentShellTimeout, 1, 1); lay->addWidget(new QLabel("New Value: "), 2, 0); lay->addWidget(newShellTimeout, 2, 1); lay->addWidget(buttonBox, 3, 0, 1, 2, Qt::AlignRight); changeShellTimeoutDialog->setLayout(lay); changeShellTimeoutDialog->setWindowTitle("Robo 3T"); if (changeShellTimeoutDialog->exec()) { settingsManager->setShellTimeoutSec(newShellTimeout->text().toInt()); settingsManager->save(); auto subStr = settingsManager->shellTimeoutSec() > 1 ? " seconds." : " second."; LOG_MSG("Shell timeout value changed from " + currentShellTimeout->text() + " to " + QString::number(settingsManager->shellTimeoutSec()) + subStr, mongo::logger::LogSeverity::Info()); for (auto const& server : AppRegistry::instance().app()->getServers()) server->changeWorkerShellTimeout(std::abs(newShellTimeout->text().toInt())); } } } ================================================ FILE: src/robomongo/gui/dialogs/ChangeShellTimeoutDialog.h ================================================ #pragma once namespace Robomongo { void changeShellTimeoutDialog(); } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionAdvancedTab.cpp ================================================ #include "robomongo/gui/dialogs/ConnectionAdvancedTab.h" #include #include #include /* --- Disabling unfinished export URI connection string feature #include #include #include #include #include #include */ #include #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/utils/QtUtils.h" /* --- Disabling unfinished export URI connection string feature #include "robomongo/core/settings/CredentialSettings.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/core/settings/SslSettings.h" #include "robomongo/gui/utils/GuiConstants.h" */ namespace Robomongo { ConnectionAdvancedTab::ConnectionAdvancedTab(ConnectionSettings *settings) : _settings(settings) { /* --- Disabling unfinished export URI connection string feature _uriString = new QLineEdit; _uriString->setReadOnly(true); _includePasswordCheckBox = new QCheckBox("Include passwords"); VERIFY(connect(_includePasswordCheckBox, SIGNAL(toggled(bool)), this, SLOT(on_includePasswordsCheckBox_toggle(bool)))); auto generateButton = new QPushButton("Generate"); generateButton->setFixedWidth(70); generateButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); VERIFY(connect(generateButton, SIGNAL(clicked()), this, SLOT(on_generateButton_clicked()))); _copyButton = new QPushButton("Copy"); _copyButton->setFixedWidth(50); _copyButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); VERIFY(connect(_copyButton, SIGNAL(clicked()), this, SLOT(on_copyButton_clicked()))); auto hlay = new QHBoxLayout; hlay->addWidget(_includePasswordCheckBox); hlay->addWidget(generateButton, Qt::AlignLeft); hlay->addWidget(_copyButton, Qt::AlignLeft); */ auto defaultDatabaseDescriptionLabel = new QLabel( "Database, that will be default (db shell variable will point to this database). " "By default, default database will be the one you authenticate on, or test otherwise. " "Leave this field empty, if you want default behavior."); defaultDatabaseDescriptionLabel->setWordWrap(true); defaultDatabaseDescriptionLabel->setContentsMargins(0, -2, 0, 20); _defaultDatabaseName = new QLineEdit(QtUtils::toQString(_settings->defaultDatabase())); auto defaultDbLabel = new QLabel("Default Database:"); #ifdef _WIN32 defaultDbLabel->setMaximumWidth(100); // Win #elif __APPLE__ defaultDbLabel->setMaximumWidth(110); // MacOS #else defaultDbLabel->setMaximumWidth(140); // Linux #endif auto mainLayout = new QGridLayout; mainLayout->setAlignment(Qt::AlignTop); mainLayout->addWidget(defaultDbLabel, 1, 0); mainLayout->addWidget(_defaultDatabaseName, 1, 1, 1, 2); mainLayout->addWidget(defaultDatabaseDescriptionLabel, 2, 1, 1, 2); /* --- Disabling unfinished export URI connection string feature mainLayout->addWidget(new QLabel{ "URI Connection String:" }, 3, 0); mainLayout->addWidget(_uriString, 3, 1); mainLayout->addLayout(hlay, 4, 1); */ setLayout(mainLayout); } void ConnectionAdvancedTab::accept() { _settings->setDefaultDatabase(QtUtils::toStdString(_defaultDatabaseName->text())); } void ConnectionAdvancedTab::setDefaultDb(const QString& defaultDb) { _defaultDatabaseName->setText(defaultDb); } /* --- Disabling unfinished export URI connection string feature void ConnectionAdvancedTab::on_generateButton_clicked() { if (_settings->isReplicaSet()) { // todo: // handle set name will be empty initially. It will be available(cached) after first // successful connection. std::string connStr = { "mongodb://" }; // todo: which credential to use if there are more than one? if (_settings->hasEnabledPrimaryCredential() && !_settings->credentials().isEmpty()) { connStr.append(_settings->primaryCredential()->userName() + ":"); connStr.append(_includePasswordCheckBox->isChecked() ? _settings->primaryCredential()->userPassword() : "PASSWORD"); connStr.append("@"); } for (auto const& member : _settings->replicaSetSettings()->membersToHostAndPort()) { connStr.append(member.toString() + ","); } connStr.pop_back(); // delete last "," connStr.append("/admin?"); connStr.append("replicaSet=" + _settings->replicaSetSettings()->setName()); if (_settings->sslSettings()->sslEnabled()) connStr.append("&ssl=true"); if (_settings->hasEnabledPrimaryCredential() && !_settings->credentials().isEmpty()) connStr.append("&authSource=" + _settings->primaryCredential()->databaseName()); // todo: validate connStr _uriString->setText(QString::fromStdString(connStr)); _uriString->setCursorPosition(QTextCursor::Start); } else { // standalone server mongo::HostAndPort server = _settings->hostAndPort(); mongo::ConnectionString connStr{ server }; auto str1 = connStr.toString(); auto uriWithStatus = mongo::StatusWith(mongo::MongoURI::parse(connStr.toString())); auto str2 = uriWithStatus.getValue().toString(); _uriString->setText(QString::fromStdString("mongodb://" + str1 + "/admin")); _uriString->setCursorPosition(QTextCursor::Start); } } void ConnectionAdvancedTab::on_copyButton_clicked() { if (_uriString->text().isEmpty()) return; QClipboard *clipboard = qApp->clipboard(); clipboard->setText(_uriString->text()); auto posUnderGenerateButton = QPoint(_copyButton->pos().x()-100, _copyButton->y()+20); QToolTip::showText(mapToGlobal(posUnderGenerateButton), "Copied into clipboard", nullptr, QRect(), 2000); } void ConnectionAdvancedTab::on_includePasswordsCheckBox_toggle(bool checked) { if (_uriString->text().isEmpty()) return; on_generateButton_clicked(); } */ } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionAdvancedTab.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QLineEdit; class QCheckBox; class QPushButton; QT_END_NAMESPACE namespace Robomongo { class ConnectionSettings; class ConnectionAdvancedTab : public QWidget { Q_OBJECT public: ConnectionAdvancedTab(ConnectionSettings *settings); void accept(); void setDefaultDb(const QString& defaultDb); /* --- Disabling unfinished export URI connection string feature private Q_SLOTS : void on_generateButton_clicked(); void on_copyButton_clicked(); void on_includePasswordsCheckBox_toggle(bool checked); */ private: QLineEdit *_defaultDatabaseName; /* --- Disabling unfinished export URI connection string feature QLineEdit *_uriString; QCheckBox *_includePasswordCheckBox; QPushButton *_copyButton; */ ConnectionSettings *_settings; }; } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionAuthTab.cpp ================================================ #include "robomongo/gui/dialogs/ConnectionAuthTab.h" #include #include #include #include #include #include #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/CredentialSettings.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/GuiRegistry.h" namespace Robomongo { ConnectionAuthTab::ConnectionAuthTab(ConnectionSettings *settings) : _settings(settings) { _useAuth = new QCheckBox("Perform authentication"); _useAuth->setStyleSheet("margin-bottom: 7px"); VERIFY(connect(_useAuth, SIGNAL(toggled(bool)), this, SLOT(authChecked(bool)))); _databaseNameDescriptionLabel = new QLabel( "The admin database is unique in MongoDB. Users with normal access " "to the admin database have read and write access to all " "databases." ); _databaseNameDescriptionLabel->setWordWrap(true); _userName = new QLineEdit(); _userNameLabel = new QLabel("User Name"); _mechanismLabel = new QLabel("Auth Mechanism"); _userPassword = new QLineEdit(); _userPassword->setEchoMode(QLineEdit::Password); _userPasswordLabel = new QLabel("Password"); _databaseName = new QLineEdit("admin"); _databaseNameLabel = new QLabel("Database"); _mechanismComboBox = new QComboBox; _mechanismComboBox->addItem("SCRAM-SHA-1"); _mechanismComboBox->addItem("SCRAM-SHA-256"); _mechanismComboBox->addItem("MONGODB-CR"); _manuallyVisibleDbs = new QLineEdit; _manuallyVisibleDbs->setPlaceholderText("Comma-separated e.g. products, users"); _manuallyVisibleDbsLabel = new QLabel("Databases"); _manuallyVisibleDbsInfo = new QLabel( "Some MongoDB users might not have the permission to get the list of" " database names (listDatabases command). For this case, manually add" " the name of the database(s) that this user has access to." ); _manuallyVisibleDbsInfo->setWordWrap(true); _useManuallyVisibleDbs = new QCheckBox("Manually specify visible databases"); _useManuallyVisibleDbs->setStyleSheet("margin-bottom: 7px"); VERIFY(connect(_useManuallyVisibleDbs, SIGNAL(toggled(bool)), this, SLOT(useManuallyVisibleDbsChecked(bool)))); _echoModeButton = new QPushButton; _echoModeButton->setIcon(GuiRegistry::instance().hideIcon()); #ifdef Q_OS_MAC _echoModeButton->setMaximumWidth(50); #else _echoModeButton->setMinimumWidth(50); #endif // Attempt to fix the issue for Windows High DPI button height is slightly taller than other widgets #ifdef Q_OS_WIN _echoModeButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); #endif VERIFY(connect(_echoModeButton, SIGNAL(clicked()), this, SLOT(toggleEchoMode()))); _useAuth->setChecked(_settings->hasEnabledPrimaryCredential()); authChecked(_settings->hasEnabledPrimaryCredential()); if (_settings->credentialCount() > 0) { CredentialSettings *primaryCredential = _settings->primaryCredential(); _userName->setText(QtUtils::toQString(primaryCredential->userName())); _userPassword->setText(QtUtils::toQString(primaryCredential->userPassword())); _databaseName->setText(QtUtils::toQString(primaryCredential->databaseName())); _mechanismComboBox->setCurrentText(QtUtils::toQString(primaryCredential->mechanism())); _useManuallyVisibleDbs->setChecked(_settings->primaryCredential()->useManuallyVisibleDbs()); _manuallyVisibleDbs->setText(QtUtils::toQString(primaryCredential->manuallyVisibleDbs())); } useManuallyVisibleDbsChecked(_useManuallyVisibleDbs->isChecked()); auto horline = new QFrame; horline->setFrameShape(QFrame::HLine); horline->setFrameShadow(QFrame::Sunken); auto verSpacer { new QSpacerItem(0, 80, QSizePolicy::Minimum, QSizePolicy::Expanding) }; auto authLayout = new QGridLayout; authLayout->addWidget(_useAuth, 0, 0, 1, 3); authLayout->addWidget(_databaseNameLabel, 1, 0); authLayout->addWidget(_databaseName, 1, 1, 1, 2); authLayout->addWidget(_databaseNameDescriptionLabel, 2, 1, 1, 2); authLayout->addWidget(new QLabel, 3, 0); authLayout->addWidget(_userNameLabel, 4, 0); authLayout->addWidget(_userName, 4, 1, 1, 2); authLayout->addWidget(_userPasswordLabel, 5, 0); authLayout->addWidget(_userPassword, 5, 1); authLayout->addWidget(_echoModeButton, 5, 2); authLayout->addWidget(_mechanismLabel, 6, 0); authLayout->addWidget(_mechanismComboBox, 6, 1, 1, 2); authLayout->addWidget(new QLabel, 7, 0); authLayout->addWidget(horline, 8, 0, 1, 3); authLayout->addWidget(_useManuallyVisibleDbs, 9, 0, 1, 3); authLayout->addWidget(_manuallyVisibleDbsLabel, 10, 0); authLayout->addWidget(_manuallyVisibleDbs, 10, 1, 1, 2); authLayout->addWidget(_manuallyVisibleDbsInfo, 11, 1, 1, 2); authLayout->addItem(verSpacer, 12, 0, 1, 3); authLayout->setAlignment(Qt::AlignTop); setLayout(authLayout); } void ConnectionAuthTab::accept() { _settings->clearCredentials(); // If all fields is empty - do nothing if (_userName->text().isEmpty() && _userPassword->text().isEmpty() && _databaseName->text().isEmpty()) return; auto credential { new CredentialSettings }; credential->setEnabled(_useAuth->isChecked()); credential->setUserName(QtUtils::toStdString(_userName->text())); credential->setUserPassword(QtUtils::toStdString(_userPassword->text())); credential->setDatabaseName(QtUtils::toStdString(_databaseName->text())); credential->setMechanism(QtUtils::toStdString(_mechanismComboBox->currentText())); credential->setUseManuallyVisibleDbs( _useManuallyVisibleDbs->isChecked() && !_manuallyVisibleDbs->text().isEmpty() ); credential->setManuallyVisibleDbs( _manuallyVisibleDbs->text().simplified().replace(' ', "").toStdString() ); _settings->addCredential(credential); } void ConnectionAuthTab::setAuthTab( QString const db, QString const username, QString const pwd, AuthMechanism authMech ) { _useAuth->setChecked(true); _databaseName->setText(db); _userName->setText(username); _userPassword->setText(pwd); _mechanismComboBox->setCurrentIndex(int(authMech)); } void ConnectionAuthTab::clearTab() { _useAuth->setChecked(false); _databaseName->clear(); _userName->clear(); _userPassword->clear(); _mechanismComboBox->setCurrentIndex(0); _useManuallyVisibleDbs->setChecked(false); _manuallyVisibleDbs->clear(); } void ConnectionAuthTab::toggleEchoMode() { bool isPassword = _userPassword->echoMode() == QLineEdit::Password; _userPassword->setEchoMode(isPassword ? QLineEdit::Normal: QLineEdit::Password); _echoModeButton->setIcon(isPassword ? GuiRegistry::instance().showIcon() : GuiRegistry::instance().hideIcon()); } void ConnectionAuthTab::authChecked(bool checked) { _databaseName->setEnabled(checked); _databaseNameLabel->setEnabled(checked); _databaseNameDescriptionLabel->setEnabled(checked); _userName->setEnabled(checked); _userNameLabel->setEnabled(checked); _userPassword->setEnabled(checked); _userPasswordLabel->setEnabled(checked); _echoModeButton->setEnabled(checked); _mechanismLabel->setEnabled(checked); _mechanismComboBox->setEnabled(checked); _useManuallyVisibleDbs->setEnabled(checked); _manuallyVisibleDbs->setEnabled(checked); _manuallyVisibleDbsLabel->setEnabled(checked); _manuallyVisibleDbsInfo->setEnabled(checked); if (checked) _databaseName->setFocus(); } void ConnectionAuthTab::useManuallyVisibleDbsChecked(bool checked) { _manuallyVisibleDbs->setVisible(checked); _manuallyVisibleDbsLabel->setVisible(checked); _manuallyVisibleDbsInfo->setVisible(checked); } } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionAuthTab.h ================================================ #pragma once #include #include "robomongo/gui/utils/GuiConstants.h" QT_BEGIN_NAMESPACE class QLineEdit; class QLabel; class QCheckBox; class QPushButton; class QComboBox; QT_END_NAMESPACE namespace Robomongo { class ConnectionSettings; class ConnectionAuthTab : public QWidget { Q_OBJECT public: ConnectionAuthTab(ConnectionSettings *settings); void accept(); void setAuthTab( QString const db, QString const username, QString const pwd, AuthMechanism authMech ); void clearTab(); private Q_SLOTS: void toggleEchoMode(); void authChecked(bool checked); void useManuallyVisibleDbsChecked(bool checked); private: QLineEdit *_userName; QLabel *_userNameLabel; QLineEdit *_userPassword; QLabel *_userPasswordLabel; QLineEdit *_databaseName; QLabel *_databaseNameLabel; QLabel *_databaseNameDescriptionLabel; QCheckBox *_useAuth; QPushButton *_echoModeButton; QLabel *_mechanismLabel; QComboBox *_mechanismComboBox; QCheckBox *_useManuallyVisibleDbs; QLineEdit *_manuallyVisibleDbs; QLabel *_manuallyVisibleDbsLabel; QLabel *_manuallyVisibleDbsInfo; ConnectionSettings *const _settings; }; } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionBasicTab.cpp ================================================ #include "robomongo/gui/dialogs/ConnectionBasicTab.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/gui/dialogs/ConnectionDialog.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/utils/GuiConstants.h" #include "mongo/client/mongo_uri.h" namespace Robomongo { ConnectionBasicTab::ConnectionBasicTab(ConnectionSettings *settings, ConnectionDialog *connectionDialog) : _settings(settings), _connectionDialog(connectionDialog) { _typeLabel = new QLabel("Type:"); _connectionType = new QComboBox; _connectionType->addItem(tr("Direct Connection")); _connectionType->addItem(tr("Replica Set")); _connectionType->setCurrentIndex(static_cast(_settings->isReplicaSet())); VERIFY(connect(_connectionType, SIGNAL(currentIndexChanged(int)), this, SLOT(on_ConnectionTypeChange(int))) ); _nameLabel = new QLabel("Name:"); _connectionName = new QLineEdit(QtUtils::toQString(_settings->connectionName())); _connInfoLabel = new QLabel("Choose any connection name that will help you to identify this connection."); _connInfoLabel->setWordWrap(true); _addressLabel = new QLabel("Address:"); _serverAddress = new QLineEdit(QtUtils::toQString(_settings->serverHost())); _colon = new QLabel(":"); _serverPort = new QLineEdit(QString::number(_settings->serverPort())); _serverPort->setFixedWidth(80); QRegExp rx("\\d+"); //(0-65554) _serverPort->setValidator(new QRegExpValidator(rx, this)); _addInfoLabel = new QLabel("Specify host and port of MongoDB server. Host can be either IPv4, IPv6 or domain name."); _addInfoLabel->setWordWrap(true); _membersLabel = new QLabel("Members:"); _membersLabel->setFixedWidth(_membersLabel->sizeHint().width()); _members = new QTreeWidget; _members->setHeaderHidden(true); _members->setIndentation(0); #ifdef _WIN32 auto lineHeight = _members->fontMetrics().height(); _members->setFixedHeight(lineHeight * 8); #endif VERIFY(connect(_members, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(on_replicaMemberItemEdit(QTreeWidgetItem*, int)))); if (_settings->isReplicaSet() && _settings->replicaSetSettings()->members().size() > 0) { for (const std::string& str : _settings->replicaSetSettings()->members()) { if (!str.empty()) { auto item = new QTreeWidgetItem; item->setText(0, QString::fromStdString(str)); item->setFlags(item->flags() | Qt::ItemIsEditable); _members->addTopLevelItem(item); } } // To fix strange MAC alignment issue #ifdef __APPLE__ auto lineHeight = _members->fontMetrics().height(); _members->setFixedHeight(lineHeight * 8); #endif } else { // No members auto item = new QTreeWidgetItem; item->setText(0, "localhost:27017"); item->setFlags(item->flags() | Qt::ItemIsEditable); _members->addTopLevelItem(item); } int const BUTTON_SIZE = 60; _addButton = new QPushButton; _addButton->setIcon(GuiRegistry::instance().plusIcon()); _removeButton = new QPushButton; _removeButton->setIcon(GuiRegistry::instance().minusIcon()); VERIFY(connect(_addButton, SIGNAL(clicked()), this, SLOT(on_addButton_clicked()))); VERIFY(connect(_removeButton, SIGNAL(clicked()), this, SLOT(on_removeButton_clicked()))); _minusPlusButtonBox = new QDialogButtonBox(this); _minusPlusButtonBox->setOrientation(Qt::Horizontal); #ifdef _WIN32 _minusPlusButtonBox->addButton(_addButton, QDialogButtonBox::NoRole); _minusPlusButtonBox->addButton(_removeButton, QDialogButtonBox::NoRole); #else _minusPlusButtonBox->addButton(_removeButton, QDialogButtonBox::NoRole); _minusPlusButtonBox->addButton(_addButton, QDialogButtonBox::NoRole); #endif _setNameLabel = new QLabel("Set Name:"); _setNameEdit = new QLineEdit(QString::fromStdString(_settings->replicaSetSettings()->setNameUserEntered())); auto fakeSpacer = new QLabel(""); auto hline = new QFrame(); hline->setFrameShape(QFrame::HLine); hline->setFrameShadow(QFrame::Sunken); _uriEdit = new QLineEdit(); _uriEdit->setPlaceholderText("Import connection details from MongoDB URI connection string"); _uriButton = new QPushButton("From URI"); #ifdef _WIN32 _uriButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); _uriButton->setMinimumWidth(60); #else // MacOS _uriButton->setMaximumHeight(HighDpiConstants::MACOS_HIGH_DPI_BUTTON_HEIGHT); _uriButton->setMaximumWidth(90); #endif VERIFY(connect(_uriButton, SIGNAL(clicked()), this, SLOT(on_uriButton_clicked()))); auto connLayout = new QGridLayout; connLayout->setVerticalSpacing(8); connLayout->setAlignment(Qt::AlignTop); connLayout->addWidget(_typeLabel, 1, 0); connLayout->addWidget(_connectionType, 1, 1, 1, 3); connLayout->addWidget(_nameLabel, 3, 0); connLayout->addWidget(_connectionName, 3, 1, 1, 3); connLayout->addWidget(_addressLabel, 5, 0); connLayout->addWidget(_serverAddress, 5, 1); connLayout->addWidget(_colon, 5, 2); connLayout->addWidget(_serverPort, 5, 3); connLayout->addWidget(_addInfoLabel, 6, 1, 1, 3); connLayout->addWidget(_membersLabel, 7, 0, Qt::AlignTop); connLayout->addWidget(_members, 7, 1, 1, 3); connLayout->addWidget(_minusPlusButtonBox, 8, 3, Qt::AlignRight | Qt::AlignTop); connLayout->addWidget(_setNameLabel, 9, 0); connLayout->addWidget(_setNameEdit, 9, 1, 1, 3, Qt::AlignTop); connLayout->addWidget(fakeSpacer, 10, 0); connLayout->addWidget(hline, 11, 0, 1, 4); connLayout->addWidget(_uriButton, 13, 0); connLayout->addWidget(_uriEdit, 13, 1, 1, 3); connLayout->setRowStretch(10, 1); #ifdef __APPLE__ connLayout->setRowMinimumHeight(11, 20); #endif auto mainLayout = new QVBoxLayout; mainLayout->addLayout(connLayout); setLayout(mainLayout); #ifdef __APPLE__ mainLayout->setContentsMargins(-1, -1, -1, 10); #endif _connectionName->setFocus(); on_ConnectionTypeChange(_connectionType->currentIndex()); } bool ConnectionBasicTab::accept() { _settings->setReplicaSet(static_cast(_connectionType->currentIndex())); _settings->setConnectionName(QtUtils::toStdString(_connectionName->text())); if (_settings->isReplicaSet() && _members->topLevelItemCount() == 0) { QMessageBox::critical(this, "Error", "Replica set members cannot be empty. " "Please enter at least one member."); return false; } // Check and warn if there is duplicate member or // if any of the replica set member items does not contain ":" character between hostname and port. if (_settings->isReplicaSet() && _members->topLevelItemCount() > 1) { QStringList members; for (int i = 0; i < _members->topLevelItemCount(); ++i) { QTreeWidgetItem const* item = _members->topLevelItem(i); QStringList const hostAndPort = item->text(0).split(":"); if (hostAndPort.size() < 2) { QMessageBox::critical(this, "Error", "Replica set member items must all contain ':' between" " hostname and port."); return false; } if (!item->text(0).isEmpty()) members.push_back(item->text(0)); } if (members.size() > 1) { if (members.removeDuplicates() > 0) { QMessageBox::critical(this, "Error", "Please remove duplicate member, two replica" " set members cannot have the same hostname and port."); return false; } } } // Save to settings if (_settings->isReplicaSet() && _members->topLevelItemCount() > 0) { QStringList const hostAndPort = _members->topLevelItem(0)->text(0).split(":"); _settings->setServerHost(hostAndPort[0].toStdString()); _settings->setServerPort(hostAndPort[1].toInt()); } else { // Single server _settings->setServerHost(QtUtils::toStdString(_serverAddress->text())); _settings->setServerPort(_serverPort->text().toInt()); } if (_settings->isReplicaSet()) { // Save replica members std::vector members; for (int i = 0; i < _members->topLevelItemCount(); ++i) { QTreeWidgetItem const* item = _members->topLevelItem(i); if (!item->text(0).isEmpty()) members.push_back(item->text(0).toStdString()); } _settings->replicaSetSettings()->setMembers(members); _settings->replicaSetSettings()->setSetNameUserEntered(_setNameEdit->text().toStdString()); // Clear cached set name _settings->replicaSetSettings()->setCachedSetName(""); } return true; } void ConnectionBasicTab::clearTab() { _connectionType->setCurrentIndex(0); _connectionName->setText("New Connection"); _serverAddress->clear(); _serverPort->clear(); _members->clear(); _setNameEdit->clear(); } void ConnectionBasicTab::on_ConnectionTypeChange(int index) { bool const isReplica = static_cast(index); _connectionDialog->toggleSshSupport(isReplica); // Replica set _membersLabel->setVisible(isReplica); _members->setVisible(isReplica); _minusPlusButtonBox->setVisible(isReplica); _setNameLabel->setVisible(isReplica); _setNameEdit->setVisible(isReplica); // Direct Connection _addressLabel->setVisible(!isReplica); _serverAddress->setVisible(!isReplica); _serverPort->setVisible(!isReplica); _colon->setVisible(!isReplica); _addInfoLabel->setVisible(!isReplica); } void ConnectionBasicTab::deleteItem() { delete _members->currentItem(); // todo: refactor } void ConnectionBasicTab::on_addButton_clicked() { auto item = new QTreeWidgetItem; // Make member addition little smarter than expected if (_members->topLevelItemCount() < 1) { item->setText(0, "localhost:27017"); } else { // Add the next member using last entered hostname and incremented port by one QString const& lastMember = _members->topLevelItem(_members->topLevelItemCount()-1)->text(0); QStringList const& hostAndPort = lastMember.split(':'); if (hostAndPort.size() == 2) { auto const& hostName = hostAndPort[0]; auto const& port = hostAndPort[1].toInt(); item->setText(0, hostName + ':' + QString::number(port + 1)); } else { item->setText(0, "localhost:" + QString::number(_members->topLevelItemCount() + 27017)); } } item->setFlags(item->flags() | Qt::ItemIsEditable); _members->addTopLevelItem(item); } void ConnectionBasicTab::on_removeButton_clicked() { if (_members->topLevelItemCount() <= 0) return; if (_members->currentItem()) delete _members->currentItem(); else delete _members->topLevelItem(_members->topLevelItemCount() - 1); } void ConnectionBasicTab::on_replicaMemberItemEdit(QTreeWidgetItem* item, int column) { if (!item) return; auto str = item->text(0); // Remove white spaces str = str.simplified(); str.remove(" "); // Remove item from tree widget if it has empty text if (str.isEmpty()) { delete item; return; } // Force port as integer QStringList const& hostAndPort = str.split(':'); if (hostAndPort.size() >= 2) { auto const& hostName = hostAndPort[0]; auto portStr = hostAndPort[1]; portStr.remove(QRegExp("[^\\d]")); str = hostName + ':' + QString::number(portStr.toInt()); } else str += ":27017"; item->setText(0, str); } void ConnectionBasicTab::on_uriButton_clicked() { // Parse Mongo URI QString uriStr = _uriEdit->text().simplified(); uriStr.replace(" ", ""); auto const statusWithURI = mongo::MongoURI::parse(uriStr.toStdString()); if (!statusWithURI.isOK()) { QMessageBox errorBox; errorBox.critical( this, "Error", ("MongoDB URI:\n" + statusWithURI.getStatus().toString()).c_str() ); errorBox.show(); return; } auto const mongoUri = statusWithURI.getValue(); // Clear tabs clearTab(); _connectionDialog->clearConnAuthTab(); _connectionDialog->clearSslTab(); // Set Basic (this) Tab auto const isReplicaSet = mongoUri.type() == mongo::ConnectionString::ConnectionType::SET; if(isReplicaSet) { _connectionType->setCurrentIndex(1); // Switch to Replica Set for (auto const& hostAndPort : mongoUri.getServers()) { auto host = QString::fromStdString(hostAndPort.host()); host.endsWith('.') ? host.remove(host.size()-1, 1) : "no-op"; auto const newHostAndPort = host + ':' + QString::number(hostAndPort.port()); auto item = new QTreeWidgetItem; item->setText(0, newHostAndPort); item->setFlags(item->flags() | Qt::ItemIsEditable); _members->addTopLevelItem(item); } _setNameEdit->setText(QString::fromStdString(mongoUri.getSetName())); } else { // Standalone _connectionType->setCurrentIndex(0); _serverAddress->setText(QString::fromStdString(mongoUri.getServers()[0].host())); _serverPort->setText(QString::number(mongoUri.getServers()[0].port())); } // Set Auth Tab auto const user = QString::fromStdString(mongoUri.getUser()); auto const pwd = QString::fromStdString(mongoUri.getPassword()); auto const authDb = QString::fromStdString(mongoUri.getAuthenticationDatabase()); auto const authMechanism = mongoUri.getOption("authMechanism").get_value_or(""); if(!user.isEmpty()) _connectionDialog->setAuthTab( authDb, user, pwd, authMechanismFromStr(authMechanism) ); // Set SSL Tab if(mongoUri.getSSLMode() == mongo::transport::ConnectSSLMode::kEnableSSL) { auto tlsAllowInvalidCertificates = mongoUri.getOption("tlsAllowInvalidCertificates"); int const authMethodIndex = tlsAllowInvalidCertificates.get_value_or("") == "true" ? 0 : 1; auto const caFile = mongoUri.getOption("tlsCAFile").get_value_or(""); auto const certKeyFile = mongoUri.getOption("tlsCertificateKeyFile").get_value_or(""); auto const certKeyFilePwd = mongoUri.getOption("tlsCertificateKeyFilePassword").get_value_or(""); auto const allowInvalidHostnames = mongoUri.getOption("tlsAllowInvalidHostnames").get_value_or("") == "true"; _connectionDialog->setSslTab( authMethodIndex, allowInvalidHostnames, caFile, certKeyFile, certKeyFilePwd ); } // Advanced Tab _connectionDialog->setDefaultDb(QString::fromStdString(mongoUri.getDatabase())); } } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionBasicTab.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QLineEdit; class QLabel; class QCheckBox; class QPushButton; class QComboBox; class QTreeWidget; class QTreeWidgetItem; class QDialogButtonBox; QT_END_NAMESPACE namespace Robomongo { class ConnectionSettings; class ConnectionDialog; class ConnectionBasicTab : public QWidget { Q_OBJECT public: ConnectionBasicTab(ConnectionSettings *settings, ConnectionDialog *connectionDialog); bool accept(); void clearTab(); private Q_SLOTS: void on_ConnectionTypeChange(int index); void deleteItem(); void on_addButton_clicked(); void on_removeButton_clicked(); void on_replicaMemberItemEdit(QTreeWidgetItem* item, int column); void on_uriButton_clicked(); private: QLabel *_typeLabel; QComboBox *_connectionType; QLabel *_nameLabel; QLineEdit *_connectionName; QLabel *_connInfoLabel; QLabel *_addressLabel; QLineEdit *_serverAddress; QLabel *_colon; QLineEdit *_serverPort; QLabel *_addInfoLabel; QLabel *_membersLabel; QTreeWidget *_members; QPushButton *_addButton; QPushButton *_removeButton; QDialogButtonBox *_minusPlusButtonBox; QLabel *_setNameLabel; QLineEdit *_setNameEdit; QLineEdit *_uriEdit; QPushButton *_uriButton; QPushButton *_discoverButton; QLabel *_readPrefLabel; QComboBox *_readPreference; ConnectionSettings *const _settings; ConnectionDialog *_connectionDialog; }; } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionDiagnosticDialog.cpp ================================================ #include "robomongo/gui/dialogs/ConnectionDiagnosticDialog.h" #include #include #include #include #include #include #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/CredentialSettings.h" #include "robomongo/core/settings/SshSettings.h" #include "robomongo/core/settings/SslSettings.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/mongodb/SshTunnelWorker.h" namespace Robomongo { ConnectionDiagnosticDialog::ConnectionDiagnosticDialog(ConnectionSettings *connection, QWidget *parent) : QDialog(parent), _connSettings(connection->clone()), _server(NULL), _serverHandle(0), _continueExec(true) { AppRegistry::instance().bus()->subscribe(this, ConnectionEstablishedEvent::Type); AppRegistry::instance().bus()->subscribe(this, ConnectionFailedEvent::Type); setWindowTitle("Diagnostic"); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?) _yesIcon = GuiRegistry::instance().yesMarkIcon(); _noIcon = GuiRegistry::instance().noMarkIcon(); _questionIcon = GuiRegistry::instance().questionMarkIcon(); _yesPixmap = _yesIcon.pixmap(20, 20); _noPixmap = _noIcon.pixmap(20, 20); _questionPixmap = _questionIcon.pixmap(20, 20); QPushButton *closeButton = new QPushButton("&Close"); VERIFY(connect(closeButton, SIGNAL(clicked()), this, SLOT(accept()))); _connectionIconLabel = new QLabel; _connectionLabel = new QLabel; _authIconLabel = new QLabel; _authLabel = new QLabel; _sshIconLabel = new QLabel; _sshLabel = new QLabel; _listIconLabel = new QLabel; _listLabel = new QLabel; _viewErrorLink = new QLabel("Show error details"); VERIFY(connect(_viewErrorLink, SIGNAL(linkActivated(QString)), this, SLOT(errorLinkActivated(QString)))); _loadingMovie = new QMovie(":robomongo/icons/loading_ticks_40x40.gif", QByteArray(), this); _loadingMovie->setScaledSize(QSize(20, 20)); _loadingMovie->start(); _connectionIconLabel->setMovie(_loadingMovie); _sshIconLabel->setMovie(_loadingMovie); _authIconLabel->setMovie(_loadingMovie); _listIconLabel->setMovie(_loadingMovie); QGridLayout *layout = new QGridLayout(); layout->setContentsMargins(20, 20, 20, 10); layout->addWidget(_sshIconLabel, 0, 0); layout->addWidget(_sshLabel, 0, 1, Qt::AlignLeft); layout->addWidget(_connectionIconLabel, 1, 0); layout->addWidget(_connectionLabel, 1, 1, Qt::AlignLeft); layout->addWidget(_authIconLabel, 2, 0); layout->addWidget(_authLabel, 2, 1, Qt::AlignLeft); layout->addWidget(_listIconLabel, 3, 0); layout->addWidget(_listLabel, 3, 1, Qt::AlignLeft); layout->setColumnStretch(0, 0) ; // Give column 0 no stretch ability layout->setColumnStretch(1, 1) ; // Give column 1 stretch ability of ratio 1 QHBoxLayout *hbox = new QHBoxLayout; hbox->addSpacing(21); hbox->addWidget(_viewErrorLink, 1, Qt::AlignLeft); hbox->addWidget(closeButton, 0, Qt::AlignRight); QVBoxLayout *box = new QVBoxLayout; box->addLayout(layout); box->addSpacing(10); box->addLayout(hbox); setLayout(box); sshStatus(InitialState); connectionStatus(InitialState); authStatus(InitialState); listStatus(InitialState); _viewErrorLink->hide(); if (!AppRegistry::instance().app()->openServer(_connSettings, ConnectionTest)) { _continueExec = false; return; } _serverHandle = AppRegistry::instance().app()->getLastServerHandle(); } ConnectionDiagnosticDialog::~ConnectionDiagnosticDialog() { if (_server) AppRegistry::instance().app()->closeServer(_server); } void ConnectionDiagnosticDialog::errorLinkActivated(const QString &link) { if (_lastErrorMessage.empty()) return; QMessageBox::information(this, "Error details", QtUtils::toQString(_lastErrorMessage)); } void ConnectionDiagnosticDialog::sshStatus(State state) { if (!_connSettings->sshSettings()->enabled()) { _sshIconLabel->setVisible(false); _sshLabel->setVisible(false); return; } if (state == InitialState) { _sshIconLabel->setMovie(_loadingMovie); _sshLabel->setText(QString("Connecting to SSH server at %1:%2...") .arg(QtUtils::toQString(_connSettings->sshSettings()->host())) .arg(_connSettings->sshSettings()->port())); } else if (state == CompletedState) { _sshIconLabel->setPixmap(_yesPixmap); _sshLabel->setText(QString("Connected to SSH server at %1:%2") .arg(QtUtils::toQString(_connSettings->sshSettings()->host())) .arg(_connSettings->sshSettings()->port())); } else if (state == FailedState) { _sshIconLabel->setPixmap(_noPixmap); _sshLabel->setText(QString("Unable to connect to SSH server at %1:%2") .arg(QtUtils::toQString(_connSettings->sshSettings()->host())) .arg(_connSettings->sshSettings()->port())); } } void ConnectionDiagnosticDialog::connectionStatus(State state) { // Add info about tunneling if SSH or SSL is used QString tunnelNote(""); // No tunnel info when neither SSH nor SSL enabled if (_connSettings->sshSettings()->enabled()) tunnelNote = " via SSH tunnel"; else if (_connSettings->sslSettings()->sslEnabled()) tunnelNote = " via TLS tunnel"; else tunnelNote = ""; auto replicaSetStr = QString::fromStdString(_connSettings->connectionName()) + " [Replica Set]"; replicaSetStr = (_connSettings->replicaSetSettings()->members().size() > 0) ? replicaSetStr + '(' + QString::fromStdString( _connSettings->replicaSetSettings()->members()[0]) + ')' : replicaSetStr + ""; QString const& serverAddress = _connSettings->isReplicaSet() ? replicaSetStr : QString::fromStdString(_connSettings->getFullAddress()); // Set main info text at dialog if (state == InitialState) { _connectionIconLabel->setMovie(_loadingMovie); _connectionLabel->setText(QString("Connecting to %1%2...").arg(serverAddress, tunnelNote)); } else if (state == CompletedState) { _connectionIconLabel->setPixmap(_yesPixmap); _connectionLabel->setText(QString("Connected to %1%2").arg(serverAddress, tunnelNote)); } else if (state == FailedState) { _connectionIconLabel->setPixmap(_noPixmap); _connectionLabel->setText(QString("Failed to connect to %1%2").arg(serverAddress, tunnelNote)); } else if (state == NotPerformedState) { _connectionIconLabel->setPixmap(_questionPixmap); _connectionLabel->setText(QString("No chance to try connection to %1%2"). arg(serverAddress, tunnelNote)); } } void ConnectionDiagnosticDialog::authStatus(State state) { if (!_connSettings->hasEnabledPrimaryCredential()) { _authIconLabel->setVisible(false); _authLabel->setVisible(false); return; } if (state == InitialState) { _authIconLabel->setMovie(_loadingMovie); _authLabel->setText(QString("Authorizing on %1 database as %2...") .arg(QtUtils::toQString(_connSettings->primaryCredential()->databaseName())) .arg(QtUtils::toQString(_connSettings->primaryCredential()->userName()))); } else if (state == CompletedState) { _authIconLabel->setPixmap(_yesPixmap); _authLabel->setText(QString("Authorized on %1 database as %2") .arg(QtUtils::toQString(_connSettings->primaryCredential()->databaseName())) .arg(QtUtils::toQString(_connSettings->primaryCredential()->userName()))); } else if (state == FailedState) { _authIconLabel->setPixmap(_noPixmap); _authLabel->setText(QString("Authorization failed on %1 database as %2") .arg(QtUtils::toQString(_connSettings->primaryCredential()->databaseName())) .arg(QtUtils::toQString(_connSettings->primaryCredential()->userName()))); } else if (state == NotPerformedState) { _authIconLabel->setPixmap(_questionPixmap); _authLabel->setText(QString("No chance to authorize")); } } void ConnectionDiagnosticDialog::listStatus(State state) { if (_connSettings->hasEnabledPrimaryCredential()) { _listIconLabel->setVisible(false); _listLabel->setVisible(false); return; } if (state == InitialState) { _listIconLabel->setMovie(_loadingMovie); _listLabel->setText(QString("Loading list of databases...")); } else if (state == CompletedState) { _listIconLabel->setPixmap(_yesPixmap); _listLabel->setText(QString("Access to databases is available")); } else if (state == FailedState) { _listIconLabel->setPixmap(_noPixmap); _listLabel->setText(QString("Failed to load list of databases")); } else if (state == NotPerformedState) { _listIconLabel->setPixmap(_questionPixmap); _listLabel->setText(QString("No chance to load list of databases")); } } void ConnectionDiagnosticDialog::handle(ConnectionEstablishedEvent *event) { if (event->connectionType != ConnectionTest) return; sshStatus(CompletedState); connectionStatus(CompletedState); authStatus(CompletedState); listStatus(CompletedState); // Remember in order to delete on dialog close _server = static_cast(event->sender()); } void ConnectionDiagnosticDialog::handle(ConnectionFailedEvent *event) { if (event->connectionType != ConnectionTest || event->serverHandle != _serverHandle) return; sshStatus(CompletedState); connectionStatus(CompletedState); authStatus(CompletedState); listStatus(CompletedState); switch (event->reason) { case ConnectionFailedEvent::SshConnection: sshStatus(FailedState); connectionStatus(NotPerformedState); authStatus(NotPerformedState); listStatus(NotPerformedState); break; case ConnectionFailedEvent::SshChannel: case ConnectionFailedEvent::MongoConnection: connectionStatus(FailedState); authStatus(NotPerformedState); listStatus(NotPerformedState); break; case ConnectionFailedEvent::MongoAuth: authStatus(FailedState); listStatus(FailedState); break; case ConnectionFailedEvent::SslConnection: connectionStatus(FailedState); authStatus(NotPerformedState); listStatus(NotPerformedState); break; } // Show link for additional error details if (!event->message.empty()) { _lastErrorMessage = event->message; _viewErrorLink->show(); } } } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionDiagnosticDialog.h ================================================ #pragma once #include #include class QLabel; class QMovie; namespace Robomongo { struct ConnectionEstablishedEvent; class ConnectionFailedEvent; class ConnectionSettings; class MongoServer; class ConnectionDiagnosticDialog : public QDialog { Q_OBJECT public: ConnectionDiagnosticDialog(ConnectionSettings *connection, QWidget *parent = 0); ~ConnectionDiagnosticDialog(); bool continueExec() const { return _continueExec; } protected Q_SLOTS: void handle(ConnectionEstablishedEvent *event); void handle(ConnectionFailedEvent *event); void errorLinkActivated(const QString &link); private: enum State { InitialState, CompletedState, FailedState, NotPerformedState }; void sshStatus(State state); void connectionStatus(State state); void authStatus(State state); void listStatus(State state); ConnectionSettings *_connSettings; QIcon _yesIcon; QIcon _noIcon; QIcon _questionIcon; QMovie *_loadingMovie; QPixmap _yesPixmap; QPixmap _noPixmap; QPixmap _questionPixmap; QLabel *_sshIconLabel; QLabel *_sshLabel; QLabel *_connectionIconLabel; QLabel *_connectionLabel; QLabel *_authIconLabel; QLabel *_authLabel; QLabel *_listIconLabel; // List database names QLabel *_listLabel; QLabel *_viewErrorLink; std::string _lastErrorMessage; MongoServer *_server; int _serverHandle; bool _continueExec; }; } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionDialog.cpp ================================================ #include "robomongo/gui/dialogs/ConnectionDialog.h" #include #include #include #include #include #include #include #include #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/dialogs/ConnectionAuthTab.h" #include "robomongo/gui/dialogs/ConnectionBasicTab.h" #include "robomongo/gui/dialogs/ConnectionAdvancedTab.h" #include "robomongo/gui/dialogs/SSHTunnelTab.h" #include "robomongo/gui/dialogs/SSLTab.h" #include "robomongo/gui/dialogs/ConnectionDiagnosticDialog.h" namespace Robomongo { /** * @brief Constructs dialog with specified connection */ ConnectionDialog::ConnectionDialog(ConnectionSettings *connection, QWidget *parent /* = nullptr */) : QDialog(parent), _connection(connection) { setWindowTitle("Connection Settings"); setWindowIcon(GuiRegistry::instance().serverIcon()); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?) QPushButton *testButton = new QPushButton("&Test"); testButton->setIcon(qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation)); VERIFY(connect(testButton, SIGNAL(clicked()), this, SLOT(testConnection()))); QHBoxLayout *bottomLayout = new QHBoxLayout; bottomLayout->addWidget(testButton, 1, Qt::AlignLeft); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); VERIFY(connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); bottomLayout->addWidget(buttonBox); QTabWidget *tabWidget = new QTabWidget; _basicTab = new ConnectionBasicTab(_connection, this); _authTab = new ConnectionAuthTab(_connection); _advancedTab = new ConnectionAdvancedTab(_connection); _sshTab = new SshTunnelTab(_connection); _sslTab = new SSLTab(_connection); tabWidget->addTab(_basicTab, "Connection"); tabWidget->addTab(_authTab, "Authentication"); tabWidget->addTab(_sshTab, "SSH"); tabWidget->addTab(_sslTab, "TLS"); tabWidget->addTab(_advancedTab, "Advanced"); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(tabWidget); mainLayout->addLayout(bottomLayout); setLayout(mainLayout); _basicTab->setFocus(); adjustSize(); #ifdef _WIN32 setMinimumWidth(500); #elif __APPLE__ setMinimumWidth(660); #elif __linux__ setMinimumWidth(900); #endif restoreWindowSettings(); } /** * @brief Accept() is called when user agree with entered data. */ void ConnectionDialog::accept() { saveWindowSettings(); if (validateAndApply()) QDialog::accept(); } void ConnectionDialog::reject() { saveWindowSettings(); QDialog::reject(); } void ConnectionDialog::closeEvent(QCloseEvent *event) { saveWindowSettings(); QWidget::closeEvent(event); } void ConnectionDialog::setAuthTab( QString const& db, QString const& username, QString const& pwd, AuthMechanism authMech ) { _authTab->setAuthTab(db, username, pwd, authMech); } void ConnectionDialog::setDefaultDb(QString const& defaultDb) { _advancedTab->setDefaultDb(defaultDb); } void ConnectionDialog::toggleSshSupport(bool isReplicaSet) { if (!_sshTab) return; _sshTab->setDisabled(isReplicaSet); _sshTab->toggleSshCheckboxToolTip(isReplicaSet); } void ConnectionDialog::clearConnAuthTab() { _authTab->clearTab(); } void ConnectionDialog::clearSslTab() { _sslTab->clearTab(); } void ConnectionDialog::setSslTab( int index, bool allowInvalidHostnames, std::string_view caFile, std::string_view certPemFile, std::string_view certPemFilePwd ) { _sslTab->setSslOptions(index, allowInvalidHostnames, caFile, certPemFile, certPemFilePwd); } void ConnectionDialog::restoreWindowSettings() { QSettings settings("3T", "Robomongo"); resize(settings.value("ConnectionDialog/size").toSize()); } void ConnectionDialog::saveWindowSettings() const { QSettings settings("3T", "Robomongo"); settings.setValue("ConnectionDialog/size", size()); } bool ConnectionDialog::validateAndApply() { _authTab->accept(); _advancedTab->accept(); if (!_basicTab->accept() || !_sshTab->accept() || !_sslTab->accept()) return false; return true; } /** * @brief Test current connection */ void ConnectionDialog::testConnection() { if (!validateAndApply()) return; ConnectionDiagnosticDialog diag(_connection, this); if (!diag.continueExec()) return; diag.exec(); } } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionDialog.h ================================================ #pragma once #include #include "robomongo/core/Core.h" #include "robomongo/gui/utils/GuiConstants.h" namespace Robomongo { class ConnectionSettings; class ConnectionAuthTab; class ConnectionBasicTab; class ConnectionAdvancedTab; class SSLTab; class SshTunnelTab; /** * @brief This Dialog allows to edit single connection */ class ConnectionDialog : public QDialog { Q_OBJECT public: /** * @brief Constructs dialog with specified connection */ ConnectionDialog(ConnectionSettings *connection, QWidget *parent = nullptr); ConnectionSettings *const connection() const { return _connection; } void setAuthTab( QString const& db, QString const& username, QString const& pwd, AuthMechanism authMech ); void setDefaultDb(QString const& defaultDb); void toggleSshSupport(bool isReplicaSet); void clearConnAuthTab(); void clearSslTab(); void setSslTab( int index, bool allowInvalidHostnames, std::string_view caFile, std::string_view certPemFile, std::string_view certPemFilePwd ); public Q_SLOTS: /** * @brief Accept() is called when user agree with entered data. */ virtual void accept(); private Q_SLOTS: /** * @brief Test current connection */ void testConnection(); private: void reject(); void closeEvent(QCloseEvent *event); void restoreWindowSettings(); void saveWindowSettings() const; bool validateAndApply(); ConnectionAuthTab *_authTab = nullptr; ConnectionBasicTab *_basicTab = nullptr; ConnectionAdvancedTab *_advancedTab = nullptr; SshTunnelTab *_sshTab = nullptr; SSLTab *_sslTab = nullptr; #ifdef SSH_SUPPORT_ENABLED SshTunnelTab *_sshTab; #endif /** * @brief Edited connection */ ConnectionSettings *const _connection; }; } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionsDialog.cpp ================================================ #include "robomongo/gui/dialogs/ConnectionsDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/settings/SslSettings.h" #include "robomongo/core/settings/SshSettings.h" #include "robomongo/core/settings/CredentialSettings.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/dialogs/ConnectionDialog.h" #include "robomongo/gui/MainWindow.h" #include "robomongo/gui/widgets/workarea/WelcomeTab.h" #include "robomongo/utils/common.h" namespace Robomongo { /* ------------------------ ConnectionListWidgetItem ------------------------ */ /** * @brief Simple ListWidgetItem that has several convenience methods. */ class ConnectionListWidgetItem : public QTreeWidgetItem { public: /** * @brief Creates ConnectionListWidgetItem with specified ConnectionSettings */ ConnectionListWidgetItem(ConnectionSettings *connection) { setConnection(connection); } /** * @brief Returns attached ConnectionSettings. */ ConnectionSettings *connection() { return _connection; } /** * @brief Attach ConnectionSettings to this item */ void setConnection(ConnectionSettings *connection) { _connection = connection; if (_connection->isReplicaSet()) { setIcon(0, GuiRegistry::instance().replicaSetIcon()); setText(0, QtUtils::toQString(_connection->connectionName())); auto const repSetSize = _connection->replicaSetSettings()->members().size(); auto addrText = QString::number(repSetSize) + ((repSetSize > 1) ? " nodes" : " node"); if (!_connection->replicaSetSettings()->members().empty()) { addrText += QString::fromStdString(" (" + _connection->replicaSetSettings()->members().front() + ")"); } setText(1, addrText); } else { setIcon(0, GuiRegistry::instance().serverIcon()); setText(0, QtUtils::toQString(_connection->connectionName())); setText(1, QtUtils::toQString(_connection->getFullAddress())); } if (_connection->imported()) { setIcon(0, GuiRegistry::instance().serverImportedIcon()); } // Header "Attributes" (column[2]) setText(2, _connection->isReplicaSet() ? "Replica Set" : ""); if (_connection->sslSettings()->sslEnabled()) setText(2, text(2) + (text(2).isEmpty() ? "TLS" : ", TLS")); if (!_connection->isReplicaSet() && _connection->sshSettings()->enabled()) setText(2, text(2) + (text(2).isEmpty() ? "SSH" : ", SSH")); // Header "Auth. Database/User" (column[3]) if (_connection->hasEnabledPrimaryCredential()) { auto primaryCredential { _connection->primaryCredential() }; auto const authString = QString("%1 / %2").arg(QtUtils::toQString(primaryCredential->databaseName())) .arg(QtUtils::toQString(primaryCredential->userName())); setText(3, authString + " "); setIcon(3, GuiRegistry::instance().keyIcon()); } else { setIcon(3, QIcon()); setText(3, ""); } } private: ConnectionSettings *_connection; }; /* ------------------------ ConnectionsDialog ------------------------ */ /** * @brief Creates dialog */ ConnectionsDialog::ConnectionsDialog(SettingsManager *settingsManager, bool checkForImported, QWidget *parent) : QDialog(parent), _settingsManager(settingsManager), _checkForImported(checkForImported) { setWindowIcon(GuiRegistry::instance().connectIcon()); setWindowTitle("MongoDB Connections"); // Remove help button (?) setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); QAction *addAction = new QAction("&Add...", this); VERIFY(connect(addAction, SIGNAL(triggered()), this, SLOT(add()))); QAction *editAction = new QAction("&Edit...", this); VERIFY(connect(editAction, SIGNAL(triggered()), this, SLOT(edit()))); QAction *cloneAction = new QAction("&Clone...", this); VERIFY(connect(cloneAction, SIGNAL(triggered()), this, SLOT(clone()))); QAction *removeAction = new QAction("&Remove...", this); VERIFY(connect(removeAction, SIGNAL(triggered()), this, SLOT(remove()))); _listWidget = new ConnectionsTreeWidget; GuiRegistry::instance().setAlternatingColor(_listWidget); #if defined(Q_OS_MAC) _listWidget->setAttribute(Qt::WA_MacShowFocusRect, false); #endif _listWidget->setIndentation(5); QStringList colums; colums << "Name" << "Address" << "Attributes" << "Auth. Database / User"; _listWidget->setHeaderLabels(colums); #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) _listWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch); _listWidget->header()->setSectionResizeMode(1, QHeaderView::Stretch); _listWidget->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); _listWidget->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); #endif //_listWidget->setViewMode(QListView::ListMode); _listWidget->setContextMenuPolicy(Qt::ActionsContextMenu); _listWidget->addAction(addAction); _listWidget->addAction(editAction); _listWidget->addAction(cloneAction); _listWidget->addAction(removeAction); _listWidget->setSelectionMode(QAbstractItemView::SingleSelection); // single item can be draged or droped _listWidget->setDragEnabled(true); _listWidget->setDragDropMode(QAbstractItemView::InternalMove); _listWidget->setMinimumHeight(290); _listWidget->setMinimumWidth(630); VERIFY(connect(_listWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(accept()))); VERIFY(connect(_listWidget, SIGNAL(layoutChanged()), this, SLOT(listWidget_layoutChanged()))); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); buttonBox->button(QDialogButtonBox::Save)->setIcon(GuiRegistry::instance().serverIcon()); buttonBox->button(QDialogButtonBox::Save)->setText("C&onnect"); VERIFY(connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); QHBoxLayout *bottomLayout = new QHBoxLayout; // Information message is shown when connection // settings are imported from previous version of Robomongo int importedCount = _settingsManager->importedConnectionsCount(); if (_checkForImported && importedCount > 0) { QIcon importIcon = qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation); QPixmap importPixmap = importIcon.pixmap(20, 20); QLabel *importLabelIcon = new QLabel; importLabelIcon->setPixmap(importPixmap); QString importedRecords = importedCount > 1 ? "records" : "record"; QLabel *importLabelMessage = new QLabel(QString( "" "Connection settings have been imported (%1 %2)" "").arg(importedCount).arg(importedRecords)); bottomLayout->addWidget(importLabelIcon, 0, Qt::AlignLeft); bottomLayout->addWidget(importLabelMessage, 1, Qt::AlignLeft); } bottomLayout->addWidget(buttonBox, 0, Qt::AlignRight); QLabel *intro = new QLabel(QString( "Create, " "edit, " "remove, " "clone " "or reorder connections via drag'n'drop.").arg("#106CD6")); intro->setWordWrap(true); VERIFY(connect(intro, SIGNAL(linkActivated(QString)), this, SLOT(linkActivated(QString)))); QVBoxLayout *firstColumnLayout = new QVBoxLayout; firstColumnLayout->addWidget(intro); firstColumnLayout->addWidget(_listWidget, 1); firstColumnLayout->addLayout(bottomLayout); QHBoxLayout *mainLayout = new QHBoxLayout(this); mainLayout->addLayout(firstColumnLayout, 1); // Populate list with connections std::vector connectionSettings = _settingsManager->connections(); for (auto const& connSetting : connectionSettings) { ConnectionSettings *connectionModel { connSetting }; add(connectionModel); } // Highlight last item if (_listWidget->topLevelItemCount() > 0) _listWidget->setCurrentItem(_listWidget->topLevelItem(_listWidget->topLevelItemCount()-1)); _listWidget->setFocus(); resize(getSetting("ConnectionsDialog/size").toSize()); } /** * @brief This function is called when user clicks on "Connect" button. */ void ConnectionsDialog::accept() { auto currentItem = dynamic_cast(_listWidget->currentItem()); // Do nothing if no item selected if (!currentItem) return; _selectedConnection = currentItem->connection(); QDialog::accept(); } void ConnectionsDialog::reject() { QDialog::reject(); } ConnectionsDialog::~ConnectionsDialog() { saveSetting("ConnectionsDialog/size", size()); } void ConnectionsDialog::linkActivated(const QString &link) { if (link == "create") add(); else if (link == "edit") edit(); else if (link == "remove") remove(); else if (link == "clone") clone(); } /** * @brief Initiate 'add' action, usually when user clicked on Add button */ void ConnectionsDialog::add() { auto newConnSettings = std::unique_ptr(new ConnectionSettings(false)); ConnectionDialog editDialog(newConnSettings.get(), this); // Do nothing if not accepted if (editDialog.exec() != QDialog::Accepted) { return; } add(newConnSettings.get()); _settingsManager->addConnection(newConnSettings.release()); _listWidget->setFocus(); } /** * @brief Initiate 'edit' action, usually when user clicked on Edit button */ void ConnectionsDialog::edit() { auto currentItem = dynamic_cast(_listWidget->currentItem()); // Do nothing if no item selected if (!currentItem) return; auto connection = currentItem->connection(); boost::scoped_ptr clonedConnection(connection->clone()); ConnectionDialog editDialog(clonedConnection.get(), this); // Do nothing if not accepted if (editDialog.exec() != QDialog::Accepted) { // on linux focus is lost - we need to activate connections dialog activateWindow(); return; } connection->apply(editDialog.connection()); // on linux focus is lost - we need to activate connections dialog activateWindow(); int size = _connectionItems.size(); for (int i = 0; iconnection() == connection) { item->setConnection(connection); break; } } } /** * @brief Initiate 'remove' action, usually when user clicked on Remove button */ void ConnectionsDialog::remove() { auto currentItem = dynamic_cast(_listWidget->currentItem()); // Do nothing if no item selected if (!currentItem) return; ConnectionSettings *connSettings = currentItem->connection(); // Ask user QString const question { "Are you sure you want to delete \"%1\" connection?" }; int const answer = QMessageBox::question(this, "Connections", question.arg(QtUtils::toQString(connSettings->getReadableName())), QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton); if (answer != QMessageBox::Yes) return; /* Temporarily disabling Recent Connections feature _settingsManager->deleteRecentConnection(connSettings); // Remove from WelcomeTab for (auto widget : QApplication::topLevelWidgets()) { if (auto mainWin = dynamic_cast(widget)) mainWin->getWelcomeTab()->removeRecentConnectionItem(connSettings); } */ _settingsManager->removeConnection(connSettings); delete currentItem; } void ConnectionsDialog::clone() { auto currentItem = dynamic_cast(_listWidget->currentItem()); // Do nothing if no item selected if (!currentItem) return; // Clone connection ConnectionSettings *connection = currentItem->connection()->clone(); // This is a special clone which will actually be a new connection and must have unique UUID connection->setUuid(QUuid::createUuid().toString()); std::string newConnectionName = "Copy of " + connection->connectionName(); connection->setConnectionName(newConnectionName); connection->replicaSetSettings()->setCachedSetName(""); ConnectionDialog editDialog(connection, this); // Cleanup newly created connection and return, if not accepted. if (editDialog.exec() != QDialog::Accepted) { delete connection; return; } // Now connection will be owned by SettingsManager _settingsManager->addConnection(connection); add(connection); } /** * @brief Handles ListWidget layoutChanged() signal */ void ConnectionsDialog::listWidget_layoutChanged() { // Make childrens toplevel again. This is a bad, but quickiest item reordering // implementation. for (int i = 0; i < _listWidget->topLevelItemCount(); i++) { auto item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i); if (item->childCount() > 0) { auto childItem = (ConnectionListWidgetItem *) item->child(0); item->removeChild(childItem); _listWidget->insertTopLevelItem(++i, childItem); _listWidget->setCurrentItem(childItem); break; } } SettingsManager::ConnectionSettingsContainerType items; for (int i = 0; i < _listWidget->topLevelItemCount(); i++) { auto item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i); items.push_back(item->connection()); } _settingsManager->reorderConnections(items); } /** * @brief Add connection to the list widget */ void ConnectionsDialog::add(ConnectionSettings *connection) { auto item = new ConnectionListWidgetItem(connection); _listWidget->addTopLevelItem(item); _listWidget->setCurrentItem(item); _connectionItems.push_back(item); } void ConnectionsDialog::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_E && (event->modifiers() & Qt::ControlModifier)) { edit(); return; } if (event->key() == Qt::Key_W && (event->modifiers() & Qt::ControlModifier)) { close(); return; } // Shift + Return also accepts connection (this shortcut is handled // to support DEBUG level logging) if (event->key() == Qt::Key_Return && (event->modifiers() & Qt::ShiftModifier)) { accept(); return; } QDialog::keyPressEvent(event); } ConnectionsTreeWidget::ConnectionsTreeWidget() { setDragDropMode(QAbstractItemView::InternalMove); setSelectionMode(QAbstractItemView::SingleSelection); setDragEnabled(true); setAcceptDrops(true); } void ConnectionsTreeWidget::dropEvent(QDropEvent *event) { #ifdef __APPLE__ if(_dragDropCount > 0) return; #endif QTreeWidget::dropEvent(event); emit layoutChanged(); #ifdef __APPLE__ ++_dragDropCount; #endif } } ================================================ FILE: src/robomongo/gui/dialogs/ConnectionsDialog.h ================================================ #pragma once #include #include #include "robomongo/core/Core.h" namespace Robomongo { class ConnectionListWidgetItem; class SettingsManager; class ConnectionSettings; /** * @brief Dialog allows select/edit/add/delete connections */ class ConnectionsDialog : public QDialog { Q_OBJECT public: typedef std::vector ConnectionListItemContainerType; ConnectionsDialog(SettingsManager *manager, bool checkForImported, QWidget *parent = 0); ~ConnectionsDialog(); /** * @brief ConnectionSettings, that was selected after pressing on * "Connect" button */ ConnectionSettings *selectedConnection() const { return _selectedConnection; } public Q_SLOTS: /** * @brief This function is called when user clicks on "Connect" button. */ void accept() override; /** * @brief Called when "Cancel" button clicked. */ void reject() override; /** * @brief Add connection to the list widget */ void add(ConnectionSettings *connection); private Q_SLOTS: void linkActivated(const QString &link); /** * @brief Initiate 'add' action, usually when user clicked on Add button */ void add(); /** * @brief Initiate 'edit' action, usually when user clicked on Edit * button */ void edit(); /** * @brief Initiate 'remove' action, usually when user clicked on Remove * button */ void remove(); /** * @brief Initiate 'clone' action, usually when user clicked on Clone * button */ void clone(); /** * @brief Handles ListWidget layoutChanged() signal */ void listWidget_layoutChanged(); void keyPressEvent(QKeyEvent* event) override; private: /** * @brief ConnectionSettings, that was selected after pressing on * "Connect" button */ ConnectionSettings *_selectedConnection; /** * @brief Main list widget */ QTreeWidget *_listWidget; /** * @brief Settings manager */ SettingsManager *_settingsManager; /** * @brief Hash that helps to connect ConnectionSettings with * ConnectionListWidgetItem* */ ConnectionListItemContainerType _connectionItems; bool _checkForImported; }; class ConnectionsTreeWidget : public QTreeWidget { Q_OBJECT public: ConnectionsTreeWidget(); Q_SIGNALS: void layoutChanged(); protected: void dropEvent(QDropEvent *event); #ifdef __APPLE__ // macOS: Set one re-order limit per new connections window. // Workaround for Qt bug came in Qt 5.12.8. // https://github.com/Studio3T/robomongo/issues/1790 private: int _dragDropCount = 0; #endif }; } ================================================ FILE: src/robomongo/gui/dialogs/CopyCollectionDialog.cpp ================================================ #include "robomongo/gui/dialogs/CopyCollectionDialog.h" #include #include #include #include #include #include #include #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include "robomongo/gui/GuiRegistry.h" namespace Robomongo { const QSize CopyCollection::minimumSize = QSize(300, 150); CopyCollection::CopyCollection(const QString &serverName, const QString &database, const QString &collection, QWidget *parent) : QDialog(parent), _currentServerName(serverName), _currentDatabase(database) { QSet uniqueConnectionsNames; for (auto const& server : AppRegistry::instance().app()->getServers()) { if (server->isConnected()) { _servers.push_back(server.get()); uniqueConnectionsNames.insert(QtUtils::toQString(server->connectionRecord()->connectionName())); } } setWindowTitle("Copy Collection"); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?) setMinimumSize(minimumSize); Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), _currentServerName); QFrame *hline = new QFrame(); hline->setFrameShape(QFrame::HLine); hline->setFrameShadow(QFrame::Sunken); _buttonBox = new QDialogButtonBox(this); _buttonBox->setOrientation(Qt::Horizontal); _buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); _buttonBox->button(QDialogButtonBox::Save)->setText("Copy"); VERIFY(connect(_buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(_buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); QHBoxLayout *hlayout = new QHBoxLayout(); hlayout->addStretch(1); hlayout->addWidget(_buttonBox); QHBoxLayout *vlayout = new QHBoxLayout(); vlayout->addWidget(serverIndicator, 0, Qt::AlignLeft); vlayout->addWidget(new Indicator(GuiRegistry::instance().databaseIcon(), _currentDatabase), 0, Qt::AlignLeft); vlayout->addWidget(new Indicator(GuiRegistry::instance().collectionIcon(), collection), 0, Qt::AlignLeft); QVBoxLayout *serverlayout = new QVBoxLayout(); serverlayout->setContentsMargins(0, 3, 0, 0); _serverComboBox = new QComboBox(); QLabel *serverLabel = new QLabel("Select server:"); QLabel *description = new QLabel( QString("Copy %1 collection to database on this or another server. " "You need to be already connected to destination server, in order to see this server in the list below. " "This operation will not overwrite existing documents with the same _id.") .arg(collection)); description->setWordWrap(true); serverlayout->addWidget(description); serverlayout->addSpacing(14); serverlayout->addWidget(serverLabel); serverlayout->addWidget(_serverComboBox); QVBoxLayout *databaselayout = new QVBoxLayout(); databaselayout->setContentsMargins(0, 8, 0, 7); _databaseComboBox = new QComboBox(); QLabel *databaseLabel = new QLabel("Select database:"); databaselayout->addWidget(databaseLabel); databaselayout->addWidget(_databaseComboBox); VERIFY(connect(_serverComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateDatabaseComboBox(int)))); _serverComboBox->addItems(uniqueConnectionsNames.toList()); QVBoxLayout *layout = new QVBoxLayout(); layout->addLayout(vlayout); layout->addWidget(hline); layout->addLayout(serverlayout); layout->addLayout(databaselayout); layout->addLayout(hlayout); setLayout(layout); } void CopyCollection::updateDatabaseComboBox(int index) { _databaseComboBox->clear(); const QString &curentServerName = _serverComboBox->currentText(); MongoServer *server = nullptr; for (auto const& ser : _servers) { if (curentServerName == QtUtils::toQString(ser->connectionRecord()->connectionName())) { server = ser; break; } } if (!server) return; _databaseComboBox->addItems(server->getDatabasesNames()); if (_currentServerName == QtUtils::toQString(server->connectionRecord()->getFullAddress())) { _databaseComboBox->removeItem(_databaseComboBox->findText(_currentDatabase)); } } MongoDatabase *CopyCollection::selectedDatabase() { MongoDatabase *result = NULL; const QString &serverName = _serverComboBox->currentText(); const QString &dataBaseName = _databaseComboBox->currentText(); if (!serverName.isEmpty() && !dataBaseName.isEmpty()) { MongoServer *server = _servers[_serverComboBox->currentIndex()]; result = server->findDatabaseByName(QtUtils::toStdString(dataBaseName)); } return result; } void CopyCollection::accept() { if (!selectedDatabase()) return; QDialog::accept(); } } ================================================ FILE: src/robomongo/gui/dialogs/CopyCollectionDialog.h ================================================ #pragma once #include #include "robomongo/core/domain/App.h" QT_BEGIN_NAMESPACE class QDialogButtonBox; class QComboBox; QT_END_NAMESPACE namespace Robomongo { class MongoDatabase; class CopyCollection : public QDialog { Q_OBJECT public: static const QSize minimumSize; explicit CopyCollection(const QString &serverName, const QString &database, const QString &collection, QWidget *parent = 0); public Q_SLOTS: virtual void accept(); void updateDatabaseComboBox(int index); MongoDatabase *selectedDatabase(); private: std::vector _servers; const QString _currentServerName; const QString _currentDatabase; QComboBox *_serverComboBox; QComboBox *_databaseComboBox; QDialogButtonBox *_buttonBox; }; } ================================================ FILE: src/robomongo/gui/dialogs/CreateCollectionDialog.cpp ================================================ #include "robomongo/gui/dialogs/CreateCollectionDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/editors/FindFrame.h" #include "robomongo/gui/editors/JSLexer.h" #include "robomongo/gui/editors/PlainJavaScriptEditor.h" #include "robomongo/shell/bson/json.h" namespace Robomongo { enum { OPTIONS_TAB = 0, STORAGE_ENGINE_TAB = 1, VALIDATOR_TAB = 2, INDEX_OPTION_DEFAULTS_TAB = 3, }; const QString CreateCollectionDialog::STORAGE_ENGINE_TAB_HINT = "Option available for WiredTiger storage engine only and database version 3.0 and higher."; const QString CreateCollectionDialog::VALIDATOR_TAB_HINT = "Option available for database version 3.2 and higher."; const QString CreateCollectionDialog::INDEX_OPTION_DEFAULTS_TAB_HINT = "Option available for database version 3.2 and higher."; const QString CreateCollectionDialog::NO_PADDING_HINT = "Option available for MMAPv1 storage engine only and database version 3.0 and higher."; const QString CreateCollectionDialog::USE_POWEROFTWO_HINT = "Option available for MMAPv1 storage engine only and deprecated since database version 3.0"; const QString CreateCollectionDialog::AUTO_INDEXID_HINT = "Option deprecated since database version 3.2"; CreateCollectionDialog::CreateCollectionDialog(const QString &serverName, const float dbVersion, const std::string& storageEngine, const QString &database, const QString &collection, QWidget *parent) : QDialog(parent), _dbVersion(dbVersion), _storageEngine(storageEngine), _activeFrame(nullptr), _activeObj(&_storageEngineObj) { setWindowTitle(tr("Create Collection")); setMinimumSize(300,150); Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), serverName); QFrame *hline = new QFrame(); hline->setFrameShape(QFrame::HLine); hline->setFrameShadow(QFrame::Sunken); _inputEdit = new QLineEdit(); _inputLabel = new QLabel(tr("Collection Name:")); _inputEdit->setMaxLength(60); _buttonBox = new QDialogButtonBox(this); _buttonBox->setOrientation(Qt::Horizontal); _buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); _buttonBox->button(QDialogButtonBox::Save)->setText(tr("C&reate")); _buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); VERIFY(connect(_buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(_buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); VERIFY(connect(_inputEdit, SIGNAL(textChanged(const QString &)), this, SLOT(enableCreateButton(const QString &)))); _validateJsonButton = new QPushButton(tr("Validate JSON")); _validateJsonButton->setIcon(qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation)); _validateJsonButton->hide(); VERIFY(connect(_validateJsonButton, SIGNAL(clicked()), this, SLOT(onValidateButtonClicked()))); // Create advanced options tabs _advancedOptions = new QTabWidget(this); _advancedOptions->addTab(createOptionsTab(), tr("Options")); _advancedOptions->addTab(createStorageEngineTab(), tr("Storage Engine")); _advancedOptions->addTab(createValidatorTab(), tr("Validator")); _advancedOptions->addTab(createIndexOptionDefaultsTab(), tr("Index Option Defaults")); _advancedOptions->setTabsClosable(false); VERIFY(connect(_advancedOptions, SIGNAL(currentChanged(int)), this, SLOT(onTabChanged(int)))); // Create Advanced Button _advancedButton = new QPushButton(tr("Advanced")); _advancedButton->setCheckable(true); VERIFY(connect(_advancedButton, SIGNAL(toggled(bool)), this, SLOT(onAdvancedButtonToggled(bool)))); // Check mongodb version and storage engine type to enable/disable UI options disableUnsupportedOptions(); QHBoxLayout *buttomHlayout = new QHBoxLayout(); buttomHlayout->addWidget(_validateJsonButton); buttomHlayout->addStretch(1); buttomHlayout->addWidget(_buttonBox); buttomHlayout->addWidget(_advancedButton); QHBoxLayout *vlayout = new QHBoxLayout(); if (!serverName.isEmpty()) vlayout->addWidget(serverIndicator, 0, Qt::AlignLeft); if (!database.isEmpty()) vlayout->addWidget(createDatabaseIndicator(database), 0, Qt::AlignLeft); if (!collection.isEmpty()) vlayout->addWidget(createCollectionIndicator(collection), 0, Qt::AlignLeft); vlayout->addStretch(1); QVBoxLayout *namelayout = new QVBoxLayout(); namelayout->setContentsMargins(0, 7, 0, 7); namelayout->addWidget(_inputLabel); namelayout->addWidget(_inputEdit); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addLayout(vlayout); mainLayout->addWidget(hline); mainLayout->addLayout(namelayout); mainLayout->addWidget(_advancedOptions); mainLayout->addLayout(buttomHlayout); mainLayout->setSizeConstraint(QLayout::SetMaximumSize); setLayout(mainLayout); _advancedOptions->hide(); _inputEdit->setFocus(); } const mongo::BSONObj CreateCollectionDialog::getExtraOptions() const { return _extraOptionsObj; } QString CreateCollectionDialog::getCollectionName() const { return _inputEdit->text(); } bool CreateCollectionDialog::isCapped() const { return (_cappedCheckBox->checkState() == Qt::Checked); } long long CreateCollectionDialog::getSizeInputValue() const { return (_sizeInputEdit->text().toLongLong()); } int CreateCollectionDialog::getMaxDocNumberInputValue() const { return (_maxDocNumberInputEdit->text().toInt()); } bool CreateCollectionDialog::isCheckedAutoIndexid() const { return (_autoIndexCheckBox->checkState() == Qt::Checked); } bool CreateCollectionDialog::isCheckedUsePowerOfTwo() const { return (_usePowerOfTwoSizeCheckBox->checkState() == Qt::Checked); } bool CreateCollectionDialog::isCheckedNoPadding() const { return (_noPaddingCheckBox->checkState() == Qt::Checked); } void CreateCollectionDialog::accept() { if ( _inputEdit->text().isEmpty() || !validateOptionDependencies() || !validate(_storageEngineFrame, _storageEngineObj) || !validate(_validatorFrame, _validatorObj) || !validate(_indexOptionDefaultsFrame, _indexOptionDefaultsObj)) { return; } makeExtraOptionsObj(); saveWindowSettings(); QDialog::accept(); } void CreateCollectionDialog::reject() { saveWindowSettings(); QDialog::reject(); } void CreateCollectionDialog::closeEvent(QCloseEvent *event) { if (_advancedOptions->isVisible()) { saveWindowSettings(); } QWidget::closeEvent(event); } void CreateCollectionDialog::onFrameTextChanged() { _activeFrame->sciScintilla()->clearIndicatorRange(0, 0, _activeFrame->sciScintilla()->lines(), 40, 0); } void CreateCollectionDialog::onValidateButtonClicked() { validate(_activeFrame, *_activeObj, false); } void CreateCollectionDialog::enableCreateButton(const QString &text) { _buttonBox->button(QDialogButtonBox::Save)->setEnabled(!text.isEmpty()); } void CreateCollectionDialog::onCappedCheckBoxChanged(int newState) { _sizeInputEdit->setEnabled((Qt::Checked == static_cast(newState))); _maxDocNumberInputEdit->setEnabled((Qt::Checked == static_cast(newState))); } void CreateCollectionDialog::onTabChanged(int index) { if (OPTIONS_TAB == index) { _validateJsonButton->hide(); } else { _validateJsonButton->show(); if (STORAGE_ENGINE_TAB == index) { _activeFrame = _storageEngineFrame; _activeObj = &_storageEngineObj; } else if (VALIDATOR_TAB == index) { _activeFrame = _validatorFrame; _activeObj = &_validatorObj; } else if (INDEX_OPTION_DEFAULTS_TAB == index) { _activeFrame = _indexOptionDefaultsFrame; _activeObj = &_indexOptionDefaultsObj; } } }; void CreateCollectionDialog::onAdvancedButtonToggled(bool state) { _advancedOptions->setVisible(state); if (state) // resize, dialog is expanding { setMinimumSize(560, 440); _validateJsonButton->setHidden(_advancedOptions->currentIndex() == OPTIONS_TAB); QSettings settings("3T", "Robomongo"); if (settings.contains("CreateCollectionDialog/size")) { restoreWindowSettings(); } else { resize(560, 440); adjustSize(); } } else // resize, dialog is shrinking { saveWindowSettings(); // save expanded geometry first _validateJsonButton->hide(); setFixedSize(300, 150); adjustSize(); } } Indicator *CreateCollectionDialog::createDatabaseIndicator(const QString &database) { return new Indicator(GuiRegistry::instance().databaseIcon(), database); } Indicator *CreateCollectionDialog::createCollectionIndicator(const QString &collection) { return new Indicator(GuiRegistry::instance().collectionIcon(), collection); } QWidget* CreateCollectionDialog::createOptionsTab() { QWidget *optionsTab = new QWidget(this); _cappedCheckBox = new QCheckBox(tr("Create capped collection"), optionsTab); _sizeInputLabel = new QLabel(tr("Maximum size in bytes: ")); _sizeInputLabel->setContentsMargins(22, 0, 0, 0); _sizeInputEdit = new QLineEdit(); _sizeInputEdit->setEnabled(false); _maxDocNumberInputLabel = new QLabel(tr("Maximum number of documents: ")); _maxDocNumberInputLabel->setContentsMargins(22, 0, 0, 0); _maxDocNumberInputEdit = new QLineEdit(); _maxDocNumberInputEdit->setEnabled(false); _autoIndexCheckBox = new QCheckBox(tr("Auto index _id"), optionsTab); _autoIndexCheckBox->setChecked(true); _usePowerOfTwoSizeCheckBox = new QCheckBox(tr("Use power-of-2 sizes"), optionsTab); // Note: For mongodb 2.6 does not have storageEngine string due to the fact that it uses MMAPV1 only. if (MongoDatabase::StorageEngineType::MMAPV1 == _storageEngine || "" == _storageEngine) { _usePowerOfTwoSizeCheckBox->setChecked(true); } _noPaddingCheckBox = new QCheckBox(tr("No Padding"), optionsTab); VERIFY(connect(_cappedCheckBox, SIGNAL(stateChanged(int)), this, SLOT(onCappedCheckBoxChanged(int)))); QGridLayout *layout = new QGridLayout; layout->addWidget(_cappedCheckBox, 0, 0, 1, 2); layout->addWidget(_sizeInputLabel, 1, 0); layout->addWidget(_sizeInputEdit, 1, 1); layout->addWidget(_maxDocNumberInputLabel, 2, 0); layout->addWidget(_maxDocNumberInputEdit, 2, 1); layout->addWidget(_autoIndexCheckBox, 3, 0); layout->addWidget(_usePowerOfTwoSizeCheckBox, 4, 0); layout->addWidget(_noPaddingCheckBox, 5, 0); layout->setAlignment(Qt::AlignTop); optionsTab->setLayout(layout); return optionsTab; } QWidget* CreateCollectionDialog::createStorageEngineTab() { QWidget *storageEngineTab = new QWidget(this); _storageEngineFrameLabel = new QLabel(tr("Enter the configuration for the storage engine: ")); _storageEngineFrame = new JSONFrame(this); configureFrameText(_storageEngineFrame); _storageEngineFrame->sciScintilla()->setText("{\n \n}"); // clear modification state after setting the content _storageEngineFrame->sciScintilla()->setModified(false); VERIFY(connect(_storageEngineFrame->sciScintilla(), SIGNAL(textChanged()), this, SLOT(onFrameTextChanged()))); QGridLayout *layout = new QGridLayout; layout->addWidget(_storageEngineFrameLabel, 0, 0); layout->addWidget(_storageEngineFrame, 1, 0); layout->setAlignment(Qt::AlignTop); storageEngineTab->setLayout(layout); return storageEngineTab; } QWidget* CreateCollectionDialog::createValidatorTab() { QWidget *validatorEngineTab = new QWidget(this); _validatorLevelLabel = new QLabel(tr("Validation Level: ")); _validatorLevelComboBox = new QComboBox(); _validatorLevelComboBox->addItem(tr("off")); _validatorLevelComboBox->addItem(tr("strict")); _validatorLevelComboBox->addItem(tr("moderate")); _validatorLevelComboBox->setCurrentIndex(1); _validatorActionLabel = new QLabel(tr("Validation Action: ")); _validatorActionComboBox = new QComboBox(); _validatorActionComboBox->addItem(tr("error")); _validatorActionComboBox->addItem(tr("warn")); _validatorActionComboBox->setCurrentIndex(0); _validatorFrameLabel = new QLabel(tr("Enter the validator document for this collection: ")); _validatorFrame = new JSONFrame(this); configureFrameText(_validatorFrame); _validatorFrame->sciScintilla()->setText("{\n \n}"); // clear modification state after setting the content _validatorFrame->sciScintilla()->setModified(false); VERIFY(connect(_validatorFrame->sciScintilla(), SIGNAL(textChanged()), this, SLOT(onFrameTextChanged()))); QHBoxLayout *validationOptionslayout = new QHBoxLayout(); validationOptionslayout->addWidget(_validatorLevelLabel); validationOptionslayout->addWidget(_validatorLevelComboBox, Qt::AlignLeft); validationOptionslayout->addWidget(_validatorActionLabel); validationOptionslayout->addWidget(_validatorActionComboBox, Qt::AlignLeft); QVBoxLayout *vlayout = new QVBoxLayout(); vlayout->addWidget(_validatorFrameLabel); vlayout->addWidget(_validatorFrame); QVBoxLayout *layout = new QVBoxLayout(); layout->addLayout(validationOptionslayout); layout->addLayout(vlayout); validatorEngineTab->setLayout(layout); return validatorEngineTab; } QWidget* CreateCollectionDialog::createIndexOptionDefaultsTab() { QWidget *indexOptionDefaultsTab = new QWidget(this); _indexOptionDefaultsFrameLabel = new QLabel(tr("Enter a default configuration for indexes when creating a collection: ")); _indexOptionDefaultsFrame = new JSONFrame(this); configureFrameText(_indexOptionDefaultsFrame); _indexOptionDefaultsFrame->sciScintilla()->setText("{\n \n}"); // clear modification state after setting the content _indexOptionDefaultsFrame->sciScintilla()->setModified(false); VERIFY(connect(_indexOptionDefaultsFrame->sciScintilla(), SIGNAL(textChanged()), this, SLOT(onFrameTextChanged()))); QGridLayout *layout = new QGridLayout; layout->addWidget(_indexOptionDefaultsFrameLabel, 0, 0); layout->addWidget(_indexOptionDefaultsFrame, 1, 0); layout->setAlignment(Qt::AlignTop); indexOptionDefaultsTab->setLayout(layout); return indexOptionDefaultsTab; } void CreateCollectionDialog::saveWindowSettings() const { QSettings settings("3T", "Robomongo"); settings.setValue("CreateCollectionDialog/size", size()); } void CreateCollectionDialog::restoreWindowSettings() { QSettings settings("3T", "Robomongo"); resize(settings.value("CreateCollectionDialog/size").toSize()); } void CreateCollectionDialog::configureFrameText(JSONFrame* frame) { QsciLexerJavaScript *javaScriptLexer = new JSLexer(this); QFont font = GuiRegistry::instance().font(); javaScriptLexer->setFont(font); frame->sciScintilla()->setBraceMatching(QsciScintilla::StrictBraceMatch); frame->sciScintilla()->setFont(font); frame->sciScintilla()->setPaper(QColor(255, 0, 0, 127)); frame->sciScintilla()->setLexer(javaScriptLexer); frame->sciScintilla()->setWrapMode((QsciScintilla::WrapMode)QsciScintilla::SC_WRAP_WORD); frame->sciScintilla()->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); frame->sciScintilla()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); frame->sciScintilla()->setStyleSheet("QFrame { background-color: rgb(73, 76, 78); border: 1px solid #c7c5c4; border-radius: 4px; margin: 0px; padding: 0px;}"); } bool CreateCollectionDialog::validate(JSONFrame* frame, mongo::BSONObj& bsonObj, bool silentOnSuccess /* = true */) { try { bsonObj = mongo::Robomongo::fromjson(jsonText(frame).toStdString()); } catch (const mongo::Robomongo::ParseMsgAssertionException &ex) { // v0.9 QString message = QtUtils::toQString(ex.reason()); int offset = ex.offset(); int line = 0, pos = 0; frame->sciScintilla()->lineIndexFromPosition(offset, &line, &pos); frame->sciScintilla()->setCursorPosition(line, pos); int lineHeight = frame->sciScintilla()->lineLength(line); frame->sciScintilla()->fillIndicatorRange(line, pos, line, lineHeight, 0); message = QString(tr("Unable to parse JSON:
%1, at (%2, %3).")) .arg(message).arg(line + 1).arg(pos + 1); QMessageBox::critical(NULL, tr("Parsing error"), message); frame->setFocus(); activateWindow(); return false; } if (!silentOnSuccess) { QMessageBox::information(NULL, tr("Validation"), tr("JSON is valid!")); frame->setFocus(); activateWindow(); } return true; } void CreateCollectionDialog::makeExtraOptionsObj() { mongo::BSONObjBuilder builder; if (_autoIndexCheckBox->isEnabled()) builder.append("autoIndexId", isCheckedAutoIndexid()); if (_noPaddingCheckBox->isEnabled() && _usePowerOfTwoSizeCheckBox->isEnabled()) { builder.append("flags", (isCheckedNoPadding()*2 + isCheckedUsePowerOfTwo()*1)); } else if (_noPaddingCheckBox->isEnabled()) { builder.append("flags", (isCheckedNoPadding()*2)); } else if (_usePowerOfTwoSizeCheckBox->isEnabled()) { builder.append("flags", (isCheckedUsePowerOfTwo()*1)); } if (_advancedOptions->isTabEnabled(STORAGE_ENGINE_TAB) ) { validate(_storageEngineFrame, _storageEngineObj); if(!_storageEngineObj.isEmpty()) builder.append("storageEngine", _storageEngineObj); } if (_advancedOptions->isTabEnabled(VALIDATOR_TAB)) { validate(_validatorFrame, _validatorObj); if (!_validatorObj.isEmpty()) { builder.append("validator", _validatorObj); builder.append("validationLevel", _validatorLevelComboBox->currentText().toStdString()); builder.append("validationAction", _validatorActionComboBox->currentText().toStdString()); } } if (_advancedOptions->isTabEnabled(INDEX_OPTION_DEFAULTS_TAB)) { validate(_indexOptionDefaultsFrame, _indexOptionDefaultsObj); if (!_indexOptionDefaultsObj.isEmpty()) builder.append("indexOptionDefaults", _indexOptionDefaultsObj); } // Complete and get resulting BSONObj _extraOptionsObj = builder.obj(); } bool CreateCollectionDialog::validateOptionDependencies() const { bool result(false); // Validate capped options if (isCapped()) { if (!(getSizeInputValue() > 0)) { QMessageBox::critical(NULL, tr("Error"), tr("Maximum size is required for capped collections")); return false; } result = true; } else { result = true; } // Completed checking all dependendencies. return result; } void CreateCollectionDialog::disableUnsupportedOptions() const { // Handle WIRED_TIGER engine type if (MongoDatabase::StorageEngineType::WIRED_TIGER == _storageEngine) { disableOption(_usePowerOfTwoSizeCheckBox, USE_POWEROFTWO_HINT); disableOption(_noPaddingCheckBox, NO_PADDING_HINT); if (MongoDatabase::DBVersion::MONGODB_3_0 > _dbVersion) { disableTab(STORAGE_ENGINE_TAB, STORAGE_ENGINE_TAB_HINT); } } else { disableTab(STORAGE_ENGINE_TAB, STORAGE_ENGINE_TAB_HINT); } // Handle MMAPV1 engine type // Note: For mongodb 2.6 does not have storageEngine string due to the fact that it uses MMAPV1 only. if (MongoDatabase::StorageEngineType::MMAPV1 == _storageEngine || "" == _storageEngine) { if (MongoDatabase::DBVersion::MONGODB_3_0 < _dbVersion) { _usePowerOfTwoSizeCheckBox->hide(); } if (MongoDatabase::DBVersion::MONGODB_3_0 > _dbVersion) { disableOption(_noPaddingCheckBox, NO_PADDING_HINT); } } if (MongoDatabase::DBVersion::MONGODB_3_2 < _dbVersion) { disableOption(_autoIndexCheckBox, AUTO_INDEXID_HINT); } if (MongoDatabase::DBVersion::MONGODB_3_0 > _dbVersion) { disableTab(STORAGE_ENGINE_TAB, STORAGE_ENGINE_TAB_HINT); } if (MongoDatabase::DBVersion::MONGODB_3_2 > _dbVersion) { disableTab(VALIDATOR_TAB, VALIDATOR_TAB_HINT); disableTab(INDEX_OPTION_DEFAULTS_TAB, INDEX_OPTION_DEFAULTS_TAB_HINT); } } void CreateCollectionDialog::disableOption(QWidget* option, const QString& hint) const { option->setDisabled(true); option->setToolTip(hint); } void CreateCollectionDialog::disableTab(int index, const QString& hint) const { _advancedOptions->setTabEnabled(index, false); _advancedOptions->setTabToolTip(index, hint); } void CreateCollectionDialog::setCursorPosition(int line, int column) { _activeFrame->sciScintilla()->setCursorPosition(line, column); } QString CreateCollectionDialog::jsonText(JSONFrame* frame) const { return frame->sciScintilla()->text().trimmed(); } } ================================================ FILE: src/robomongo/gui/dialogs/CreateCollectionDialog.h ================================================ #pragma once #include #include QT_BEGIN_NAMESPACE class QLabel; class QDialogButtonBox; class QLineEdit; class QTabWidget; class QCheckBox; class QComboBox; QT_END_NAMESPACE class QsciScintilla; namespace Robomongo { class FindFrame; class Indicator; /** * @brief This Dialog allows to create collection with selective options. */ class CreateCollectionDialog : public QDialog { Q_OBJECT public: using JSONFrame = FindFrame; /** * @brief Construct dialog with parameters * @param serverName: Database name * @param dbVersion: Database version * @param storageEngine: Storage engine type * @param database: Name of the database * @param collection: Name of the collection */ explicit CreateCollectionDialog(const QString &serverName, const float dbVersion, const std::string& storageEngine, const QString &database = QString(), const QString &collection = QString(), QWidget *parent = 0); /** * @brief Used if dialog exec() method returns QDialog::Accepted * @return Extra options as BSONObj */ const mongo::BSONObj getExtraOptions() const; /** * @return Collection name */ QString getCollectionName() const; /** * @return true if capped option checked, false otherwise */ bool isCapped() const; /** * @return Size option input */ long long getSizeInputValue() const; /** * @return Max number of documents option input */ int getMaxDocNumberInputValue() const; /** * @return true if auto index_id option checked, false otherwise */ bool isCheckedAutoIndexid() const; /** * @return true if use power-of-2 sizes option checked, false otherwise */ bool isCheckedUsePowerOfTwo() const; /** * @return true if no padding option checked, false otherwise */ bool isCheckedNoPadding() const; public Q_SLOTS: /** * @brief Validate all input fields and make extraOptions object if all valid when create button clicked. */ void accept() override; /** * @brief Called when "Cancel" button clicked. */ void reject() override; protected: /** * @brief Reimplementing closeEvent in order to do implement functionalities before close this dialog. */ void closeEvent(QCloseEvent *event) override; private Q_SLOTS: /** * @brief Set indicator position when active JSON frame text changed */ void onFrameTextChanged(); /** * @brief Validate active JSON frame text when validate button clicked */ void onValidateButtonClicked(); /** * @brief Enable create button if input edit has text * @param text : string from _inputEdit widget */ void enableCreateButton(const QString &text); /** * @brief Enable/disable capped options when capped checkbox state changed * @param newState : New state of capped checkbox */ void onCappedCheckBoxChanged(int newState); /** * @brief Show/hide validate JSON button, update active JSON frame and object according to active tab * @param Index of active tab (i.e. index = 0 for the first tab) */ void onTabChanged(int index); /** * @brief Show/hide advanced options menu (_tabwidget) when advancedButton toggled. * @param state : true if dialog is expanding, false if shrinking. */ void onAdvancedButtonToggled(bool state); private: /** * @brief Tool tip strings for disabled options and tabs */ static const QString STORAGE_ENGINE_TAB_HINT; static const QString VALIDATOR_TAB_HINT; static const QString INDEX_OPTION_DEFAULTS_TAB_HINT; static const QString NO_PADDING_HINT; static const QString USE_POWEROFTWO_HINT; static const QString AUTO_INDEXID_HINT; /** * @brief Create database indicator widget * @param database : Name of the database * @return : Pointer to newly created Indicator */ Indicator* createDatabaseIndicator(const QString &database); /** * @brief Create collection indicator widget * @param database : Name of the collection * @return : Pointer to newly created Indicator */ Indicator* createCollectionIndicator(const QString &collection); /** * @brief Creators for the tabs * @return : Pointer to newly created tab */ QWidget* createOptionsTab(); QWidget* createStorageEngineTab(); QWidget* createValidatorTab(); QWidget* createIndexOptionDefaultsTab(); /** * @brief Save windows settings into system registry */ void saveWindowSettings() const; /** * @brief Restore windows settings from system registry */ void restoreWindowSettings(); /** * @brief Do initial configuration of given JSON frame. * @param frame : Pointer to newly created tab */ void configureFrameText(JSONFrame* frame); /** * @brief Build extraOptions BSON Object */ void makeExtraOptionsObj(); /** * @brief Validate dependencies and requirements between all inputs * @return : true if all options valid, false at least one dependency not met */ bool validateOptionDependencies() const; /** * @brief Disable options if they are not supported by database version or storage engine type */ void disableUnsupportedOptions() const; /** * @brief Disable widget if unsupported * @param option : Widget to disable * @param hint : Hint string to be shown */ void disableOption(QWidget* option, const QString& hint) const; /** * @brief Disable tab if unsupported * @param index : Tab index * @param hint : Hint string to be shown */ void disableTab(int index, const QString& hint) const; /** * @brief Set cursor position of the active JSON frame * @param line : Line number * @param column : Column number */ void setCursorPosition(int line, int column); /** * @return : String from given JSON frame * @param frame : Pointer to JSON frame */ QString jsonText(JSONFrame* frame) const; /** * @brief Do JSON validation/creation for given frame text * @param frame : Pointer to JSON frame * @param bsonObj : BSON object to be validated * @param silentOnSuccess : true for silent validation, false for message box on validation * @return true if text is valid JSON, false otherwise */ bool validate(JSONFrame* frame, mongo::BSONObj& bsonObj, bool silentOnSuccess = true); /** * @brief Main Window */ QLineEdit *_inputEdit; QLabel *_inputLabel; QTabWidget *_advancedOptions; QPushButton *_validateJsonButton; QDialogButtonBox *_buttonBox; QPushButton *_advancedButton; /** * @brief Options Tab */ QCheckBox *_cappedCheckBox; QLabel *_sizeInputLabel; QLineEdit *_sizeInputEdit; QLabel *_maxDocNumberInputLabel; QLineEdit *_maxDocNumberInputEdit; QCheckBox *_autoIndexCheckBox; QCheckBox *_usePowerOfTwoSizeCheckBox; QCheckBox *_noPaddingCheckBox; /** * @brief Storage Engine Tab */ JSONFrame *_storageEngineFrame; QLabel * _storageEngineFrameLabel; /** * @brief Validator Tab */ QLabel * _validatorLevelLabel; QComboBox * _validatorLevelComboBox; QLabel * _validatorActionLabel; QComboBox * _validatorActionComboBox; JSONFrame *_validatorFrame; QLabel * _validatorFrameLabel; /** * @brief Index Options Defaults Tab */ JSONFrame *_indexOptionDefaultsFrame; QLabel * _indexOptionDefaultsFrameLabel; /** * @brief Pointer to active tab's frame */ JSONFrame *_activeFrame; /** * @brief Pointer to active tab's BSON obj. */ mongo::BSONObj *_activeObj; /** * @brief Main BSON obj. with all extra options */ mongo::BSONObj _extraOptionsObj; /** * @brief Sub objects for each JSON tab to create extraOptions object finally. */ mongo::BSONObj _storageEngineObj; mongo::BSONObj _validatorObj; mongo::BSONObj _indexOptionDefaultsObj; /** * @brief Database version */ const float _dbVersion; /** * @brief Storage engine type */ const std::string _storageEngine; }; } ================================================ FILE: src/robomongo/gui/dialogs/CreateDatabaseDialog.cpp ================================================ #include "robomongo/gui/dialogs/CreateDatabaseDialog.h" #include #include #include #include #include #include #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { const QSize CreateDatabaseDialog::dialogSize = QSize(300, 150); CreateDatabaseDialog::CreateDatabaseDialog(const QString &serverName, const QString &database, const QString &collection, QWidget *parent) : QDialog(parent) { setWindowTitle("Create Database"); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?) //setFixedSize(dialogSize); setMinimumWidth(300); Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), serverName); QFrame *hline = new QFrame(); hline->setFrameShape(QFrame::HLine); hline->setFrameShadow(QFrame::Sunken); _inputEdit = new QLineEdit(); _inputLabel = new QLabel("Database Name:"); _inputEdit->setMaxLength(maxLenghtName); _buttonBox = new QDialogButtonBox(this); _buttonBox->setOrientation(Qt::Horizontal); _buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); _buttonBox->button(QDialogButtonBox::Save)->setText("C&reate"); VERIFY(connect(_buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(_buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); QHBoxLayout *hlayout = new QHBoxLayout(); hlayout->addStretch(1); hlayout->addWidget(_buttonBox); QHBoxLayout *vlayout = new QHBoxLayout(); if (!serverName.isEmpty()) vlayout->addWidget(serverIndicator, 0, Qt::AlignLeft); if (!database.isEmpty()) vlayout->addWidget(createDatabaseIndicator(database), 0, Qt::AlignLeft); if (!collection.isEmpty()) vlayout->addWidget(createCollectionIndicator(collection), 0, Qt::AlignLeft); QVBoxLayout *namelayout = new QVBoxLayout(); namelayout->setContentsMargins(0, 7, 0, 7); namelayout->addWidget(_inputLabel); namelayout->addWidget(_inputEdit); QVBoxLayout *layout = new QVBoxLayout(); layout->addLayout(vlayout); layout->addWidget(hline); layout->addLayout(namelayout); layout->addLayout(hlayout); setLayout(layout); _inputEdit->setFocus(); } QString CreateDatabaseDialog::databaseName() const { return _inputEdit->text(); } void CreateDatabaseDialog::setOkButtonText(const QString &text) { _buttonBox->button(QDialogButtonBox::Save)->setText(text); } void CreateDatabaseDialog::setInputLabelText(const QString &text) { _inputLabel->setText(text); } void CreateDatabaseDialog::setInputText(const QString &text) { _inputEdit->setText(text); _inputEdit->selectAll(); } void CreateDatabaseDialog::accept() { if (_inputEdit->text().isEmpty()) return; QDialog::accept(); } Indicator *CreateDatabaseDialog::createDatabaseIndicator(const QString &database) { return new Indicator(GuiRegistry::instance().databaseIcon(), database); } Indicator *CreateDatabaseDialog::createCollectionIndicator(const QString &collection) { return new Indicator(GuiRegistry::instance().collectionIcon(), collection); } } ================================================ FILE: src/robomongo/gui/dialogs/CreateDatabaseDialog.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QLabel; class QDialogButtonBox; class QLineEdit; QT_END_NAMESPACE namespace Robomongo { class Indicator; class CreateDatabaseDialog : public QDialog { Q_OBJECT public: explicit CreateDatabaseDialog(const QString &serverName, const QString &database = QString(), const QString &collection = QString(), QWidget *parent = 0); QString databaseName() const; void setOkButtonText(const QString &text); void setInputLabelText(const QString &text); void setInputText(const QString &text); enum { maxLenghtName = 60 }; const static QSize dialogSize; public Q_SLOTS: virtual void accept(); private: Indicator *createDatabaseIndicator(const QString &database); Indicator *createCollectionIndicator(const QString &collection); QLineEdit *_inputEdit; QLabel *_inputLabel; QDialogButtonBox *_buttonBox; }; } ================================================ FILE: src/robomongo/gui/dialogs/CreateUserDialog.cpp ================================================ #include "robomongo/gui/dialogs/CreateUserDialog.h" #include #include #include #include #include #include #include #include #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include "robomongo/gui/utils/ComboBoxUtils.h" #include "robomongo/core/domain/MongoUtils.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/GuiRegistry.h" namespace Robomongo { const char * rolesText[CreateUserDialog::RolesCount] = { "read", "readWrite", "dbAdmin", "userAdmin", "clusterAdmin", "readAnyDatabase", "readWriteAnyDatabase", "userAdminAnyDatabase", "dbAdminAnyDatabase" }; const QSize CreateUserDialog::minimumSize = QSize(400, 200); bool containsWord(const std::string& sentence, const std::string& word) { size_t pos = 0; while ((pos = sentence.substr(pos).find(word)) != std::string::npos) { if (pos + word.size() < sentence.size()) { char c = sentence[pos + word.size()]; bool isLastAlpha = isalpha(c); bool isFirstAlpha = false; if (pos) { isFirstAlpha = isalpha(sentence[pos - 1]); } if (!isFirstAlpha && !isLastAlpha) return true; } pos++; } return false; } CreateUserDialog::CreateUserDialog(const QStringList &databases, const QString &serverName, const QString &database, const MongoUser &user, QWidget *parent) : QDialog(parent), _user(user) { setWindowTitle("Add User"); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?) setMinimumSize(minimumSize); Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), serverName); Indicator *databaseIndicator = new Indicator(GuiRegistry::instance().databaseIcon(), database); QFrame *hline = new QFrame(); hline->setFrameShape(QFrame::HLine); hline->setFrameShadow(QFrame::Sunken); _userNameLabel = new QLabel("Name:"); _userNameEdit = new QLineEdit(); _userNameEdit->setText(QtUtils::toQString(user.name())); _userPassLabel = new QLabel("Password:"); _userPassEdit = new QLineEdit(); _userPassEdit->setEchoMode(QLineEdit::Password); _userSourceLabel = new QLabel("UserSource:"); _userSourceComboBox = new QComboBox(); _userSourceComboBox->addItems(QStringList() << databases); //setText(QtUtils::toQString(user.userSource())); _userSourceComboBox->setCurrentIndex(databases.indexOf(database)); utils::setCurrentText(_userSourceComboBox, QtUtils::toQString(user.userSource())); QGridLayout *gridRoles = new QGridLayout(); MongoUser::RolesVector userRoles = user.roles(); for (unsigned i = 0; isetChecked(it!= userRoles.end()); gridRoles->addWidget(_rolesArray[i], row, col); } QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); VERIFY(connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); QHBoxLayout *hlayout = new QHBoxLayout(); hlayout->addStretch(1); hlayout->addWidget(buttonBox); QHBoxLayout *vlayout = new QHBoxLayout(); vlayout->addWidget(serverIndicator, 0, Qt::AlignLeft); vlayout->addWidget(databaseIndicator, 0, Qt::AlignLeft); vlayout->addStretch(1); QGridLayout *namelayout = new QGridLayout(); namelayout->setContentsMargins(0, 7, 0, 7); namelayout->addWidget(_userNameLabel, 0, 0); namelayout->addWidget(_userNameEdit, 0, 1); namelayout->addWidget(_userPassLabel, 1, 0); namelayout->addWidget(_userPassEdit, 1, 1); namelayout->addWidget(_userSourceLabel, 2, 0); namelayout->addWidget(_userSourceComboBox, 2, 1); namelayout->addLayout(gridRoles, 3, 1); QVBoxLayout *layout = new QVBoxLayout(); layout->addLayout(vlayout); layout->addWidget(hline); layout->addLayout(namelayout); layout->addLayout(hlayout); setLayout(layout); _userNameEdit->setFocus(); } CreateUserDialog::CreateUserDialog(const QString &serverName, const QString &database, const MongoUser &user, QWidget *parent) : QDialog(parent), _user(user) { setWindowTitle("Add User"); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?) setMinimumSize(minimumSize); Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), serverName); Indicator *databaseIndicator = new Indicator(GuiRegistry::instance().databaseIcon(), database); QFrame *hline = new QFrame(); hline->setFrameShape(QFrame::HLine); hline->setFrameShadow(QFrame::Sunken); _userNameLabel = new QLabel("Name:"); _userNameEdit = new QLineEdit(); _userNameEdit->setText(QtUtils::toQString(user.name())); _userPassLabel= new QLabel("Password:"); _userPassEdit = new QLineEdit(); _userPassEdit->setEchoMode(QLineEdit::Password); _readOnlyCheckBox = new QCheckBox("Read Only"); _readOnlyCheckBox->setChecked(user.readOnly()); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); VERIFY(connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); QHBoxLayout *hlayout = new QHBoxLayout(); hlayout->addStretch(1); hlayout->addWidget(buttonBox); QHBoxLayout *vlayout = new QHBoxLayout(); vlayout->addWidget(serverIndicator, 0, Qt::AlignLeft); vlayout->addWidget(databaseIndicator, 0, Qt::AlignLeft); vlayout->addStretch(1); QGridLayout *namelayout = new QGridLayout(); namelayout->setContentsMargins(0, 7, 0, 7); namelayout->addWidget(_userNameLabel, 0, 0); namelayout->addWidget(_userNameEdit, 0, 1); namelayout->addWidget(_userPassLabel, 1, 0); namelayout->addWidget(_userPassEdit, 1, 1); namelayout->addWidget(_readOnlyCheckBox, 2, 1); QVBoxLayout *layout = new QVBoxLayout(); layout->addLayout(vlayout); layout->addWidget(hline); layout->addLayout(namelayout); layout->addLayout(hlayout); setLayout(layout); _userNameEdit->setFocus(); } void CreateUserDialog::setUserPasswordLabelText(const QString &text) { _userPassLabel->setText(text); } void CreateUserDialog::accept() { std::string username = QtUtils::toStdString(_userNameEdit->text()); std::string pass = QtUtils::toStdString(_userPassEdit->text()); if (_user.version() < MongoUser::minimumSupportedVersion) { if (username.empty() || pass.empty()) return; _user.setName(username); _user.setPassword(pass); _user.setReadOnly(_readOnlyCheckBox->isChecked()); } else { std::string userSource = QtUtils::toStdString(_userSourceComboBox->currentText()); if (username.empty()) return; if (userSource.empty() && pass.empty()) return; _user.setName(username); _user.setPassword(pass); _user.setUserSource(userSource); MongoUser::RolesVector roles; for (unsigned i = 0; i < RolesCount; ++i) { if (_rolesArray[i]->isChecked()) { roles.push_back(rolesText[i]); } } _user.setRoles(roles); } QDialog::accept(); } } ================================================ FILE: src/robomongo/gui/dialogs/CreateUserDialog.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QTextEdit; class QLabel; class QCheckBox; class QLineEdit; class QComboBox; QT_END_NAMESPACE #include "robomongo/core/domain/MongoUser.h" namespace Robomongo { class CreateUserDialog : public QDialog { Q_OBJECT public: static const QSize minimumSize; CreateUserDialog(const QStringList &databases, const QString &serverName, const QString &database, const MongoUser &user, QWidget *parent = 0); CreateUserDialog(const QString &serverName, const QString &database, const MongoUser &user, QWidget *parent = 0); MongoUser user() const { return _user; } void setUserPasswordLabelText(const QString &text); enum { RolesCount = 9}; public Q_SLOTS: virtual void accept(); private: MongoUser _user; QLabel *_userNameLabel; QLineEdit *_userNameEdit; QLabel *_userPassLabel; QLineEdit *_userPassEdit; QLabel *_userSourceLabel; QComboBox *_userSourceComboBox; QCheckBox *_readOnlyCheckBox; QCheckBox *_rolesArray[RolesCount]; }; } ================================================ FILE: src/robomongo/gui/dialogs/DocumentTextEditor.cpp ================================================ #include "robomongo/gui/dialogs/DocumentTextEditor.h" #include #include #include #include #include #include #include #include #include #include "robomongo/gui/editors/JSLexer.h" #include "robomongo/gui/editors/FindFrame.h" #include "robomongo/gui/editors/PlainJavaScriptEditor.h" #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/shell/bson/json.h" namespace Robomongo { const QSize DocumentTextEditor::minimumSize = QSize(800, 400); DocumentTextEditor::DocumentTextEditor(const CollectionInfo &info, const QString &json, bool readonly /* = false */, QWidget *parent) : QDialog(parent), _info(info), _readonly(readonly) { QRect screenGeometry = QApplication::desktop()->availableGeometry(); int horizontalMargin = (int)(screenGeometry.width() * 0.35); int verticalMargin = (int)(screenGeometry.height() * 0.20); QSize size(screenGeometry.width() - horizontalMargin, screenGeometry.height() - verticalMargin); QSettings settings("3T", "Robomongo"); if (settings.contains("DocumentTextEditor/size")) { restoreWindowSettings(); } else { resize(size); } setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); Indicator *collectionIndicator = new Indicator(GuiRegistry::instance().collectionIcon(), QtUtils::toQString(_info._ns.collectionName())); Indicator *databaseIndicator = new Indicator(GuiRegistry::instance().databaseIcon(), QtUtils::toQString(_info._ns.databaseName())); Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), QtUtils::toQString(detail::prepareServerAddress(_info._serverAddress))); QPushButton *validate = new QPushButton("Validate"); validate->setIcon(qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation)); VERIFY(connect(validate, SIGNAL(clicked()), this, SLOT(onValidateButtonClicked()))); _queryText = new FindFrame(this); _configureQueryText(); _queryText->sciScintilla()->setText(json); // clear modification state after setting the content _queryText->sciScintilla()->setModified(false); VERIFY(connect(_queryText->sciScintilla(), SIGNAL(textChanged()), this, SLOT(onQueryTextChanged()))); QHBoxLayout *hlayout = new QHBoxLayout(); hlayout->setContentsMargins(2, 0, 5, 1); hlayout->setSpacing(0); hlayout->addWidget(serverIndicator, 0, Qt::AlignLeft); hlayout->addWidget(databaseIndicator, 0, Qt::AlignLeft); hlayout->addWidget(collectionIndicator, 0, Qt::AlignLeft); hlayout->addStretch(1); QDialogButtonBox *buttonBox = new QDialogButtonBox (this); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); VERIFY(connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); QHBoxLayout *bottomlayout = new QHBoxLayout(); bottomlayout->addWidget(validate); bottomlayout->addStretch(1); bottomlayout->addWidget(buttonBox); QVBoxLayout *layout = new QVBoxLayout(); // show top bar only if we have info for it if (_info.isValid()) layout->addLayout(hlayout); layout->addWidget(_queryText); layout->addLayout(bottomlayout); setLayout(layout); if (_readonly) { validate->hide(); buttonBox->button(QDialogButtonBox::Save)->hide(); _queryText->sciScintilla()->setReadOnly(true); } } QString DocumentTextEditor::jsonText() const { return _queryText->sciScintilla()->text().trimmed(); } void DocumentTextEditor::setCursorPosition(int line, int column) { _queryText->sciScintilla()->setCursorPosition(line, column); } void DocumentTextEditor::accept() { if (!validate()) return; saveWindowSettings(); QDialog::accept(); } void DocumentTextEditor::reject() { if (_queryText->sciScintilla()->isModified()) { int ret = QMessageBox::warning(this, tr("Robo 3T"), tr("The document has been modified.\n" "Do you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save); if (ret == QMessageBox::Save) { this->accept(); } else if (ret == QMessageBox::Discard) { QDialog::reject(); } return; } saveWindowSettings(); QDialog::reject(); } bool DocumentTextEditor::validate(bool silentOnSuccess /* = true */) { QString text = jsonText(); int len = 0; try { std::string textString = QtUtils::toStdString(text); const char *json = textString.c_str(); int jsonLen = textString.length(); int offset = 0; _obj.clear(); while (offset != jsonLen) { mongo::BSONObj doc = mongo::Robomongo::fromjson(json+offset, &len); _obj.push_back(doc); offset += len; } } catch (const mongo::Robomongo::ParseMsgAssertionException &ex) { // v0.9 QString message = QtUtils::toQString(ex.reason()); int offset = ex.offset(); int line = 0, pos = 0; _queryText->sciScintilla()->lineIndexFromPosition(offset, &line, &pos); _queryText->sciScintilla()->setCursorPosition(line, pos); int lineHeight = _queryText->sciScintilla()->lineLength(line); _queryText->sciScintilla()->fillIndicatorRange(line, pos, line, lineHeight, 0); message = QString("Unable to parse JSON:
%1, at (%2, %3).") .arg(message).arg(line + 1).arg(pos + 1); QMessageBox::critical(NULL, "Parsing error", message); _queryText->setFocus(); activateWindow(); return false; } if (!silentOnSuccess) { QMessageBox::information(NULL, "Validation", "JSON is valid!"); _queryText->setFocus(); activateWindow(); } return true; } void DocumentTextEditor::onQueryTextChanged() { _queryText->sciScintilla()->clearIndicatorRange(0, 0, _queryText->sciScintilla()->lines(), 40, 0); } void DocumentTextEditor::onValidateButtonClicked() { validate(false); } void DocumentTextEditor::closeEvent(QCloseEvent *event) { saveWindowSettings(); QWidget::closeEvent(event); } /* ** Configure QsciScintilla query widget */ void DocumentTextEditor::_configureQueryText() { QsciLexerJavaScript *javaScriptLexer = new JSLexer(this); QFont font = GuiRegistry::instance().font(); javaScriptLexer->setFont(font); _queryText->sciScintilla()->setAppropriateBraceMatching(); _queryText->sciScintilla()->setFont(font); _queryText->sciScintilla()->setPaper(QColor(255, 0, 0, 127)); _queryText->sciScintilla()->setLexer(javaScriptLexer); _queryText->sciScintilla()->setWrapMode((QsciScintilla::WrapMode)QsciScintilla::SC_WRAP_WORD); _queryText->sciScintilla()->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); _queryText->sciScintilla()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); _queryText->sciScintilla()->setStyleSheet("QFrame { background-color: rgb(73, 76, 78); border: 1px solid #c7c5c4; border-radius: 4px; margin: 0px; padding: 0px;}"); } void DocumentTextEditor::saveWindowSettings() const { QSettings settings("3T", "Robomongo"); settings.setValue("DocumentTextEditor/size", size()); } void DocumentTextEditor::restoreWindowSettings() { QSettings settings("3T", "Robomongo"); resize(settings.value("DocumentTextEditor/size").toSize()); } } ================================================ FILE: src/robomongo/gui/dialogs/DocumentTextEditor.h ================================================ #pragma once #include #include #include "robomongo/core/domain/MongoQueryInfo.h" namespace Robomongo { class FindFrame; class DocumentTextEditor : public QDialog { Q_OBJECT public: typedef std::vector ReturnType; static const QSize minimumSize; explicit DocumentTextEditor(const CollectionInfo &info, const QString &json, bool readonly = false, QWidget *parent = 0); QString jsonText() const; /** * @brief Use returned BSONObj only if Dialog exec() method returns QDialog::Accepted */ ReturnType bsonObj() const { return _obj; } void setCursorPosition(int line, int column); public Q_SLOTS: void accept() override; void reject() override; bool validate(bool silentOnSuccess = true); private Q_SLOTS: void onQueryTextChanged(); void onValidateButtonClicked(); protected: /** * @brief Reimplementing closeEvent in order to do some pre-close actions. */ void closeEvent(QCloseEvent *event) override; private: void _configureQueryText(); /** * @brief Restore window settings from system registry */ void restoreWindowSettings(); /** * @brief Save window settings into system registry */ void saveWindowSettings() const; const CollectionInfo _info; FindFrame *_queryText; bool _readonly; ReturnType _obj; }; } ================================================ FILE: src/robomongo/gui/dialogs/EulaDialog.cpp ================================================ #include "robomongo/gui/dialogs/EulaDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { EulaDialog::EulaDialog(bool showFormPage, QWidget *parent) : QWizard(parent), _showFormPage(showFormPage) { setWindowTitle("EULA"); //// First page auto firstPage = new QWizardPage; auto agreeButton = new QRadioButton("I agree"); VERIFY(connect(agreeButton, SIGNAL(clicked()), this, SLOT(on_agreeButton_clicked()))); auto notAgreeButton = new QRadioButton("I don't agree"); notAgreeButton->setChecked(true); VERIFY(connect(notAgreeButton, SIGNAL(clicked()), this, SLOT(on_notAgreeButton_clicked()))); auto radioButtonsLay = new QHBoxLayout; radioButtonsLay->setAlignment(Qt::AlignHCenter); radioButtonsLay->setSpacing(30); radioButtonsLay->addWidget(agreeButton); radioButtonsLay->addWidget(notAgreeButton); auto textBrowser = new QTextBrowser; textBrowser->setOpenExternalLinks(true); textBrowser->setOpenLinks(true); QFile file(":gnu_gpl3_license.html"); if (file.open(QFile::ReadOnly | QFile::Text)) textBrowser->setText(file.readAll()); auto hline = new QFrame(); hline->setFrameShape(QFrame::HLine); hline->setFrameShadow(QFrame::Sunken); auto mainLayout1 = new QVBoxLayout(); mainLayout1->addWidget(new QLabel("

End-User License Agreement

")); mainLayout1->addWidget(new QLabel("")); mainLayout1->addWidget(textBrowser); mainLayout1->addWidget(new QLabel("")); mainLayout1->addLayout(radioButtonsLay, Qt::AlignCenter); mainLayout1->addWidget(new QLabel("")); mainLayout1->addWidget(hline); firstPage->setLayout(mainLayout1); //// Second page auto secondPage = new QWizardPage; auto nameLabel = new QLabel("First Name:"); _nameEdit = new QLineEdit; auto lastNameLabel = new QLabel("Last Name:"); _lastNameEdit = new QLineEdit; auto emailLabel = new QLabel("Email:"); _emailEdit = new QLineEdit; _phone = new QLineEdit; _company = new QLineEdit; auto buttomLabel = new QLabel("By submitting this form I agree to 3T Software Labs " "Privacy Policy."); buttomLabel->setOpenExternalLinks(true); auto bodyLabel = new QLabel("\nShare your email address with us and we'll keep you " "up-to-date with updates from us and new features as they come out."); bodyLabel->setWordWrap(true); auto mainLayout2 = new QGridLayout(); mainLayout2->addWidget(new QLabel, 0, 0, 1, 2); mainLayout2->addWidget(new QLabel("

Thank you for choosing Robo 3T!

"), 1, 0, 1, 2); mainLayout2->addWidget(bodyLabel, 2, 0 , 1, 2); mainLayout2->addWidget(new QLabel, 3, 0, 1, 2); mainLayout2->addWidget(nameLabel, 4, 0); mainLayout2->addWidget(_nameEdit, 4, 1); mainLayout2->addWidget(lastNameLabel, 5, 0); mainLayout2->addWidget(_lastNameEdit, 5, 1); mainLayout2->addWidget(emailLabel, 6, 0); mainLayout2->addWidget(_emailEdit, 6, 1); mainLayout2->addWidget(new QLabel("Phone: "), 7, 0); mainLayout2->addWidget(_phone, 7, 1); mainLayout2->addWidget(new QLabel("Company:"), 8, 0); mainLayout2->addWidget(_company, 8, 1); mainLayout2->addWidget(new QLabel, 9, 0, 1, 2); mainLayout2->addWidget(buttomLabel, 10, 0, 1, 2); secondPage->setLayout(mainLayout2); addPage(firstPage); if(_showFormPage) addPage(secondPage); //// Buttons setButtonText(QWizard::CustomButton1, tr("Back")); setButtonText(QWizard::CustomButton2, tr("Next")); setButtonText(QWizard::CustomButton3, tr("Finish")); VERIFY(connect(button(QWizard::CustomButton1), SIGNAL(clicked()), this, SLOT(on_back_clicked()))); VERIFY(connect(button(QWizard::CustomButton2), SIGNAL(clicked()), this, SLOT(on_next_clicked()))); VERIFY(connect(button(QWizard::CustomButton3), SIGNAL(clicked()), this, SLOT(on_finish_clicked()))); setButtonLayout(QList{ QWizard::Stretch, QWizard::CustomButton1, QWizard::CustomButton2, QWizard::CancelButton, QWizard::CustomButton3}); button(QWizard::CustomButton1)->setDisabled(true); button(QWizard::CustomButton2)->setDisabled(true); button(QWizard::CustomButton2)->setHidden(!_showFormPage); button(QWizard::CustomButton3)->setDisabled(true); setWizardStyle(QWizard::ModernStyle); QSettings const settings("3T", "Robomongo"); if (settings.contains("EulaDialog/size")) { restoreWindowSettings(); } else { auto const desktop = QApplication::desktop(); auto const& mainScreenSize = desktop->availableGeometry(desktop->primaryScreen()).size(); resize(mainScreenSize.width()*0.5, mainScreenSize.height()*0.6); } } void EulaDialog::accept() { saveWindowSettings(); if(_showFormPage) postUserData(); QDialog::accept(); } void EulaDialog::reject() { saveWindowSettings(); QDialog::reject(); } void EulaDialog::closeEvent(QCloseEvent *event) { saveWindowSettings(); QWidget::closeEvent(event); } void EulaDialog::on_agreeButton_clicked() { if(_showFormPage) button(QWizard::CustomButton2)->setEnabled(true); else button(QWizard::CustomButton3)->setEnabled(true); } void EulaDialog::on_notAgreeButton_clicked() { if (_showFormPage) button(QWizard::CustomButton2)->setEnabled(false); else button(QWizard::CustomButton3)->setEnabled(false); } void EulaDialog::on_next_clicked() { next(); button(QWizard::CustomButton1)->setEnabled(true); button(QWizard::CustomButton2)->setEnabled(false); button(QWizard::CustomButton3)->setEnabled(true); } void EulaDialog::on_back_clicked() { back(); button(QWizard::CustomButton1)->setEnabled(false); button(QWizard::CustomButton2)->setEnabled(true); button(QWizard::CustomButton3)->setEnabled(false); } void EulaDialog::on_finish_clicked() { accept(); } void EulaDialog::postUserData() const { if (_emailEdit->text().isEmpty() || AppRegistry::instance().settingsManager()->disableHttpsFeatures() ) return; // OS string #ifdef _WIN32 QString const OS = "win"; #elif __APPLE__ QString const OS = "osx"; #elif __linux__ QString const OS = "linux"; #else QString const OS = "unknown"; #endif // Timezone string QDateTime now = QDateTime::currentDateTime(); now.setOffsetFromUtc(now.offsetFromUtc()); QString dateAndTimezone = now.toString(Qt::ISODate); QString const date = QDateTime::currentDateTime().toString(Qt::ISODate); QString const timezone = "UTC" + dateAndTimezone.remove(date); // Build post data and send QJsonObject jsonStr { { "email", _emailEdit->text() }, { "firstName", _nameEdit->text() }, { "lastName", _lastNameEdit->text() }, { "phone", _phone->text() }, { "company", _company->text() }, { "os", OS }, { "timezone", timezone } }; QJsonDocument jsonDoc(jsonStr); QUrlQuery postData(jsonDoc.toJson()); postData = QUrlQuery("rd=" + postData.toString(QUrl::FullyEncoded).toUtf8()); QNetworkRequest request(QUrl("https://rm-form.3t.io/")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); auto networkManager = new QNetworkAccessManager; _reply = networkManager->post(request, postData.toString(QUrl::FullyEncoded).toUtf8()); debugLog("EulaDialog: Form posted"); } void EulaDialog::saveWindowSettings() const { QSettings settings("3T", "Robomongo"); settings.setValue("EulaDialog/size", size()); } void EulaDialog::restoreWindowSettings() { QSettings settings("3T", "Robomongo"); resize(settings.value("EulaDialog/size").toSize()); } } ================================================ FILE: src/robomongo/gui/dialogs/EulaDialog.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QLabel; class QLineEdit; class QCheckBox; class QPushButton; class QDialogButtonBox; class QComboBox; class QNetworkReply; QT_END_NAMESPACE namespace Robomongo { class EulaDialog : public QWizard { Q_OBJECT public: static const QSize minimumSize; explicit EulaDialog(bool showFormPage, QWidget *parent = nullptr); public Q_SLOTS: void accept() override; void reject() override; protected: void closeEvent(QCloseEvent *event) override; private Q_SLOTS: void on_agreeButton_clicked(); void on_notAgreeButton_clicked(); void on_next_clicked(); void on_back_clicked(); void on_finish_clicked(); private: // Send name, last name and email data of user to home server void postUserData() const; /** * @brief Restore window settings from system registry */ void restoreWindowSettings(); /** * @brief Save window settings into system registry */ void saveWindowSettings() const; QLineEdit* _nameEdit; QLineEdit* _lastNameEdit; QLineEdit* _emailEdit; QLineEdit* _phone; QLineEdit* _company; QByteArray _postData; mutable QNetworkReply* _reply; bool _showFormPage = true; }; } ================================================ FILE: src/robomongo/gui/dialogs/ExportDialog.cpp ================================================ #include "robomongo/gui/dialogs/ExportDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/domain/MongoCollection.h" #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/gui/widgets/explorer/ExplorerServerTreeItem.h" #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include "robomongo/gui/widgets/explorer/ExplorerWidget.h" #include "robomongo/gui/widgets/explorer/ExplorerTreeWidget.h" #include "robomongo/gui/widgets/explorer/ExplorerServerTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionIndexesDir.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerReplicaSetTreeItem.h" #include "robomongo/gui/utils/GuiConstants.h" #include "robomongo/gui/GuiRegistry.h" namespace Robomongo { namespace { const QString defaultDir = "D:\\exports\\"; // Default location auto const AUTO_MODE_SIZE = QSize(500, 450); auto const MANUAL_MODE_SIZE = QSize(500, 400); auto const SHOW_DETAILS = "Show details"; auto const HIDE_DETAILS = "Hide details"; // This structure represents the arguments as in "mongoexport.exe --help" // See http://docs.mongodb.org/manual/reference/program/mongoexport/ for more information struct MongoExportArgs { static QString db(const QString& dbName) { return ("--db" + dbName); } static QString collection(const QString& collection) { return ("--collection" + collection); } // i.e. absFilePath: "/exports/coll1.json" static QString out(const QString& absFilePath) { return ("--out " + absFilePath); } }; } ExportDialog::ExportDialog(QString const& dbName, QString const& collName, QWidget *parent) : QDialog(parent), _mode(AUTO), _mongoExportArgs(), _activeProcess(nullptr) { setWindowTitle("Export Collection"); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?) setMinimumSize(AUTO_MODE_SIZE); //setFixedHeight(AUTO_MODE_SIZE.height()); _activeProcess = new QProcess(this); //_activeProcess->setProcessChannelMode(QProcess::MergedChannels); VERIFY(connect(_activeProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(on_exportFinished(int, QProcess::ExitStatus)))); VERIFY(connect(_activeProcess, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(on_processErrorOccurred(QProcess::ProcessError)))); // todo: move to a global location // Enable copyable text for QMessageBox qApp->setStyleSheet("QMessageBox { messagebox-text-interaction-flags: 5; }"); const QString serverName = "localhost:20017"; // todo: remove Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), serverName); // Horizontal line QFrame *horline = new QFrame(); horline->setFrameShape(QFrame::HLine); horline->setFrameShadow(QFrame::Sunken); // Widgets related to Input auto dbNameLabel = new QLabel("Database Name:"); auto dbNameLineEdit = new QLineEdit; auto dbNameLay = new QHBoxLayout; dbNameLay->addWidget(dbNameLabel); dbNameLay->addWidget(dbNameLineEdit); auto collNameLabel = new QLabel("Collection Name:"); auto collNameLineEdit = new QLineEdit; auto collNameLay = new QHBoxLayout; collNameLay->addWidget(collNameLabel); collNameLay->addWidget(collNameLineEdit); auto selectedCollLay = new QGridLayout; selectedCollLay->setAlignment(Qt::AlignTop); selectedCollLay->setColumnStretch(2, 1); auto serverIcon = new QLabel(""); auto dbIcon = new QLabel(""); auto collIcon = new QLabel(""); selectedCollLay->addWidget(serverIcon, 1, 0); selectedCollLay->addWidget(new QLabel("Server: "), 1, 1); selectedCollLay->addWidget(new QLabel("local_(2)"), 1, 2); selectedCollLay->addWidget(dbIcon, 2, 0); selectedCollLay->addWidget(new QLabel("Database: "), 2, 1); selectedCollLay->addWidget(new QLabel(dbName), 2, 2); selectedCollLay->addWidget(collIcon, 3, 0); selectedCollLay->addWidget(new QLabel("Collection: "), 3, 1); selectedCollLay->addWidget(new QLabel(collName), 3, 2); // Widgets related to Output _formatComboBox = new QComboBox; _formatComboBox->addItem("JSON"); _formatComboBox->addItem("CSV"); VERIFY(connect(_formatComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(on_formatComboBox_change(int)))); _fieldsLabel = new QLabel("Fields:"); _fields = new QLineEdit; // todo: use textedit // Initially hidden _fieldsLabel->setHidden(true); _fields->setHidden(true); _query = new QLineEdit("{}"); // todo: JSON frame can be used _outputFileName = new QLineEdit; _outputDir = new QLineEdit; _browseButton = new QPushButton("..."); _browseButton->setMaximumWidth(50); VERIFY(connect(_browseButton, SIGNAL(clicked()), this, SLOT(on_browseButton_clicked()))); // Export summary widgets _exportOutput = new QTextEdit; QFontMetrics font(_exportOutput->font()); _exportOutput->setFixedHeight((4+1.5) * (font.lineSpacing())); // 4-line text edit _exportOutput->setReadOnly(true); _mongoExportOutput = new QTextEdit; _mongoExportOutput->setFixedHeight(4 * (font.lineSpacing() + 8)); // 4-line text edit _mongoExportOutput->setReadOnly(true); _mongoExportOutput->setHidden(true); // Attempt to fix issue for Windows High DPI button height is slightly taller than other widgets #ifdef Q_OS_WIN _browseButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); #endif auto outputsInnerLay = new QGridLayout; outputsInnerLay->addWidget(new QLabel("Format:"), 0, 0); outputsInnerLay->addWidget(_formatComboBox, 0, 1, 1, 2); outputsInnerLay->addWidget(_fieldsLabel, 1, 0, Qt::AlignTop); outputsInnerLay->addWidget(_fields, 1, 1, 1, 2); outputsInnerLay->addWidget(new QLabel("Query:"), 2, 0); outputsInnerLay->addWidget(_query, 2, 1, 1, 2); outputsInnerLay->addWidget(new QLabel("File Name:"), 3, 0); outputsInnerLay->addWidget(_outputFileName, 3, 1, 1, 2); outputsInnerLay->addWidget(new QLabel("Directory:"), 4, 0); outputsInnerLay->addWidget(_outputDir, 4, 1); outputsInnerLay->addWidget(_browseButton, 4, 2); //outputsInnerLay->addWidget(new QLabel("Result:"), 5, 0, Qt::AlignTop); //outputsInnerLay->addWidget(_exportOutput, 6, 0, 1, 3, Qt::AlignTop); auto manualLayout = new QGridLayout; auto cmdLabel = new QLabel("Command:"); cmdLabel->setFixedHeight(cmdLabel->sizeHint().height()); _manualExportCmd = new QTextEdit; QFontMetrics font1(_manualExportCmd->font()); _manualExportCmd->setFixedHeight(2 * (font1.lineSpacing()+8)); // 2-line text edit manualLayout->addWidget(cmdLabel, 0, 0, Qt::AlignTop); manualLayout->addWidget(_manualExportCmd, 1, 0, Qt::AlignTop); // Button box and Manual Mode button _modeButton = new QPushButton("Manual Mode"); VERIFY(connect(_modeButton, SIGNAL(clicked()), this, SLOT(on_modeButton_clicked()))); _buttonBox = new QDialogButtonBox(this); _buttonBox->setOrientation(Qt::Horizontal); _buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); _buttonBox->button(QDialogButtonBox::Save)->setText("E&xport"); _buttonBox->button(QDialogButtonBox::Save)->setMaximumWidth(70); _buttonBox->button(QDialogButtonBox::Cancel)->setMaximumWidth(70); VERIFY(connect(_buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(_buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); // Sub layouts auto serverIndicatorlayout = new QHBoxLayout(); if (!serverName.isEmpty()) { serverIndicatorlayout->addWidget(serverIndicator, 0, Qt::AlignLeft); } // Input layout _inputsGroupBox = new QGroupBox("Selected Collection"); _inputsGroupBox->setLayout(selectedCollLay); _inputsGroupBox->setStyleSheet("QGroupBox::title { left: 0px }"); _inputsGroupBox->setFixedHeight(_inputsGroupBox->sizeHint().height()); // Outputs _autoOutputsGroup = new QGroupBox("Output Properties"); _autoOutputsGroup->setLayout(outputsInnerLay); _autoOutputsGroup->setStyleSheet("QGroupBox::title { left: 0px }"); _autoOutputsGroup->setFixedHeight(_autoOutputsGroup->sizeHint().height()); // Manual Groupbox _manualGroupBox = new QGroupBox("Manual Export"); _manualGroupBox->setLayout(manualLayout); _manualGroupBox->setStyleSheet("QGroupBox::title { left: 0px }"); _manualGroupBox->setHidden(true); // Export Summary auto exportSummaryGroup = new QGroupBox("Export Summary"); exportSummaryGroup->setStyleSheet("QGroupBox::title { left: 0px }"); _viewOutputLink = new QLabel(SHOW_DETAILS); VERIFY(connect(_viewOutputLink, SIGNAL(linkActivated(QString)), this, SLOT(on_viewOutputLink(QString)))); _viewOutputLink->setFixedHeight(_viewOutputLink->sizeHint().height()); auto tempLayout = new QVBoxLayout(); //tempLayout->setSizeConstraint(QLayout::SetFixedSize); tempLayout->addWidget(_exportOutput, Qt::AlignTop); tempLayout->addWidget(_viewOutputLink, Qt::AlignLeft | Qt::AlignTop); //tempLayout->addWidget(_mongoExportOutput); exportSummaryGroup->setLayout(tempLayout); exportSummaryGroup->setFixedHeight(exportSummaryGroup->sizeHint().height()); // Buttonbox layout auto hButtonBoxlayout = new QHBoxLayout(); hButtonBoxlayout->addStretch(1); hButtonBoxlayout->addWidget(_buttonBox); hButtonBoxlayout->addWidget(_modeButton); // Main Layout auto layout = new QVBoxLayout(); layout->addWidget(_inputsGroupBox, Qt::AlignTop); //layout->addWidget(horline, Qt::AlignTop); layout->addWidget(_autoOutputsGroup, Qt::AlignTop); layout->addWidget(_manualGroupBox); layout->addWidget(exportSummaryGroup, Qt::AlignTop); layout->addLayout(hButtonBoxlayout); setLayout(layout); // todo: move to a function // Help user filling inputs automatically auto date = QDateTime::currentDateTime().toString("dd.MM.yyyy"); auto time = QDateTime::currentDateTime().toString("hh.mm.ss"); auto timeStamp = date + "_" + time; auto format = _formatComboBox->currentIndex() == 0 ? "json" : "csv"; _outputFileName->setText(dbName + "." + collName + "_" + timeStamp + "." + format); _outputDir->setText(defaultDir); _manualExportCmd->setText("mongoexport --db " + dbName + " --collection " + collName + " --out " + _outputDir->text() + _outputFileName->text()); // todo: setExportArgs() // First set db and coll _mongoExportArgs = " --db " + dbName + " --collection " + collName; _outputFileName->setFocus(); } void ExportDialog::setOkButtonText(const QString &text) { _buttonBox->button(QDialogButtonBox::Save)->setText(text); } // todo: remove void ExportDialog::setInputLabelText(const QString &text) { //_inputLabel->setText(text); } void ExportDialog::accept() { QString mongoExport = "D:\\mongo_export\\bin\\mongoexport.exe"; bool disable = false; enableDisableWidgets(disable); if (AUTO == _mode) { _exportOutput->clear(); _exportOutput->setText("Exporting..."); // If CSV append output format and fields if (_formatComboBox->currentIndex() == 1) { if (_fields->text().isEmpty()) { QMessageBox::critical(this, "Error", "\"Fields\" option is required in CSV mode."); return; } _mongoExportArgs.append(" --type=csv"); _mongoExportArgs.append(" --fields " + _fields->text().replace(" ", "")); } if (!_query->text().isEmpty() && _query->text() != "{}") { _mongoExportArgs.append(" --query " + _query->text()); } // Append file path and name auto absFilePath = _outputDir->text() + _outputFileName->text(); _mongoExportArgs.append(" --out " + absFilePath); // Start mongoexport non-blocking _activeProcess->start(mongoExport + _mongoExportArgs); } else if (MANUAL == _mode) { _exportOutput->clear(); _exportOutput->setText("Exporting..."); // todo: check if _activeProcess->state() is QProcess::NotRunning // Start mongoexport non-blocking _activeProcess->start("D:\\mongo_export\\bin\\" + _manualExportCmd->toPlainText()); } } void ExportDialog::ui_itemExpanded(QTreeWidgetItem *item) { auto categoryItem = dynamic_cast(item); if (categoryItem) { categoryItem->expand(); return; } ExplorerServerTreeItem *serverItem = dynamic_cast(item); if (serverItem) { serverItem->expand(); return; } auto dirItem = dynamic_cast(item); if (dirItem) { dirItem->expand(); } } void ExportDialog::ui_itemDoubleClicked(QTreeWidgetItem *item, int column) { // todo } void ExportDialog::on_browseButton_clicked() { // Select output directory QString origDir = QFileDialog::getExistingDirectory(this, tr("Select Directory"), defaultDir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); auto dir = QDir::toNativeSeparators(origDir); QApplication::activeModalWidget()->raise(); QApplication::activeModalWidget()->activateWindow(); if (dir.isNull()) return; _outputDir->setText(dir + "\\"); } void ExportDialog::on_formatComboBox_change(int index) { bool const isCsv = static_cast(index); _fieldsLabel->setVisible(isCsv); _fields->setVisible(isCsv); // todo: divide ui_itemClicked() //ui_itemClicked(_treeWidget->currentItem()); } void ExportDialog::on_modeButton_clicked() { _exportOutput->clear(); _mode = (AUTO == _mode ? MANUAL : AUTO); _modeButton->setText(AUTO == _mode ? "Manual Mode" : "Auto Mode"); _autoOutputsGroup->setVisible(AUTO == _mode); _manualGroupBox->setVisible(MANUAL == _mode); setMinimumSize(AUTO == _mode ? AUTO_MODE_SIZE : MANUAL_MODE_SIZE); _inputsGroupBox->setTitle(AUTO == _mode ? "Selected Collection" : "Selected Server"); adjustSize(); } void ExportDialog::on_exportFinished(int exitCode, QProcess::ExitStatus exitStatus) { bool enable = true; enableDisableWidgets(enable); // Extract absolute file path QString absFilePath; if (AUTO == _mode) { absFilePath = _outputDir->text() + _outputFileName->text(); } else if (MANUAL == _mode) { // extract absolute file path string QStringList strlist1 = _manualExportCmd->toPlainText().split("--out"); if (strlist1.size() > 1) { QString str1 = strlist1[1]; QStringList strlist2 = str1.split("--"); if (strlist2.size() > 1) { absFilePath = strlist2[0]; } else { absFilePath = str1; } } } absFilePath.replace(" ", ""); // todo: handle paths with white spaces // todo: also check process exit code // Check exported file exists and mongoexport output does not contain error QFileInfo const file(absFilePath); _mongoExportOutputStr = _activeProcess->readAllStandardError(); // Extract mongoexport command output if (file.exists() && file.isFile() && _mongoExportOutputStr.contains("exported")) { QStringList splitA = _mongoExportOutputStr.split("exported"); QStringList splitB = splitA[1].split("records"); _exportResult = "Export Successful: \n" "Exported file: " + absFilePath + "\n" "Number of records exported:" + splitB[0]; _exportOutput->setText(_exportResult); } else { _exportOutput->setText("Export Failed.\n"); _exportOutput->append("Output:\n" + _mongoExportOutputStr); } _exportOutput->moveCursor(QTextCursor::Start); } void ExportDialog::on_processErrorOccurred(QProcess::ProcessError error) { bool enable = true; enableDisableWidgets(enable); if (QProcess::FailedToStart == error) { _exportOutput->setText("Error: \"mongoexport\" process failed to start. Either the " "invoked program is missing, or you may have insufficient permissions to invoke the program."); } else if (QProcess::Crashed == error) { _exportOutput->setText("Error: \"mongoexport\" process crashed some time after starting" " successfully.."); } else { _exportOutput->setText("Error: \"mongoexport\" process failed. Error code: " + QString::number(error)); } _exportOutput->moveCursor(QTextCursor::Start); } void ExportDialog::on_viewOutputLink(QString) { QMessageBox::information(this, "Details", _mongoExportOutputStr); } Indicator *ExportDialog::createDatabaseIndicator(const QString &database) { return new Indicator(GuiRegistry::instance().databaseIcon(), database); } Indicator *ExportDialog::createCollectionIndicator(const QString &collection) { return new Indicator(GuiRegistry::instance().collectionIcon(), collection); } void ExportDialog::enableDisableWidgets(bool enable) const { // Auto mode widgets //_treeWidget->setEnabled(enable); _formatComboBox->setEnabled(enable); _fieldsLabel->setEnabled(enable); _fields->setEnabled(enable); _query->setEnabled(enable); _outputFileName->setEnabled(enable); _outputDir->setEnabled(enable); _browseButton->setEnabled(enable); _buttonBox->button(QDialogButtonBox::Save)->setEnabled(enable); _modeButton->setEnabled(enable); // Manual mode widgets //_treeWidget->setEnabled(enable); _manualExportCmd->setEnabled(enable); _buttonBox->button(QDialogButtonBox::Save)->setEnabled(enable); _modeButton->setEnabled(enable); } } ================================================ FILE: src/robomongo/gui/dialogs/ExportDialog.h ================================================ #pragma once #include #include QT_BEGIN_NAMESPACE class QLabel; class QDialogButtonBox; class QLineEdit; class QTreeWidgetItem; class QTreeWidget; class QComboBox; class QPushButton; class QGroupBox; class QTextEdit; class QProcess; QT_END_NAMESPACE namespace Robomongo { class Indicator; /** * @brief This class is not finished, it is still under development. * Currently export is disabled in the GUI. If enabled in GUI, export * will work with current code. However, the code is unfinished; * it needs to finished, reviewed and tested. * Currently, there is support for simultaneous export which can be done * on multiple export dialogs without blocking each other or main window. * So user can still work while multiple export dialogs are doing export. * This achieved by the assumption that there will be usable export executable for * Robomongo to use. Currently, this location is hard coded as * "D:\\mongo_export\\bin\\mongoexport.exe". One of the next actions will be * to design the location of mongoexport which should be cross-platform, * generic and not hard coded. */ class ExportDialog : public QDialog { Q_OBJECT enum MODE { AUTO = 0, MANUAL = 1 }; public: explicit ExportDialog(QString const& dbName, QString const& collName, QWidget *parent = 0); void setOkButtonText(const QString &text); void setInputLabelText(const QString &text); void setInputText(const QString &text); enum { maxLenghtName = 60 }; public Q_SLOTS: virtual void accept(); private Q_SLOTS: void ui_itemExpanded(QTreeWidgetItem *item); void ui_itemDoubleClicked(QTreeWidgetItem *item, int column); void on_browseButton_clicked(); void on_formatComboBox_change(int index); void on_modeButton_clicked(); void on_exportFinished(int exitCode, QProcess::ExitStatus exitStatus); void on_processErrorOccurred(QProcess::ProcessError); void on_viewOutputLink(QString); private: // todo: remove if unused Indicator *createDatabaseIndicator(const QString &database); Indicator *createCollectionIndicator(const QString &collection); // Enable/Disable widgets during/after export operation // @param enable: true to enable, false to disable widgets void enableDisableWidgets(bool enable) const; // Auto Mode QGroupBox* _inputsGroupBox; QComboBox* _formatComboBox; QLabel* _fieldsLabel; QLineEdit* _fields; QLineEdit* _query; QLineEdit* _outputFileName; QLineEdit* _outputDir; QPushButton* _browseButton; QGroupBox* _autoOutputsGroup; QTextEdit* _exportOutput; QLabel* _viewOutputLink; // QTextEdit* _mongoExportOutput; // Manual Mode QTextEdit* _manualExportCmd; QPushButton* _modeButton; QGroupBox* _manualGroupBox; // Common QDialogButtonBox* _buttonBox; MODE _mode; QString _dbName; QString _collName; QString _mongoExportArgs; QString _exportResult; QString _mongoExportOutputStr; QProcess* _activeProcess; // pointer to running/finished process }; } ================================================ FILE: src/robomongo/gui/dialogs/FunctionTextEditor.cpp ================================================ #include "robomongo/gui/dialogs/FunctionTextEditor.h" #include #include #include #include #include #include #include #include #include #include "robomongo/gui/editors/JSLexer.h" #include "robomongo/gui/editors/FindFrame.h" #include "robomongo/gui/editors/PlainJavaScriptEditor.h" #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { FunctionTextEditor::FunctionTextEditor(const QString &server, const QString &database, const MongoFunction &function, QWidget *parent) : QDialog(parent), _function(function) { setMinimumWidth(700); setMinimumHeight(550); Indicator *databaseIndicator = new Indicator(GuiRegistry::instance().databaseIcon(), database); Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), server); _nameEdit = new QLineEdit(QtUtils::toQString(function.name())); _queryText = new FindFrame(this); _configureQueryText(); _queryText->sciScintilla()->setText(QtUtils::toQString(_function.code())); QFrame *hline = new QFrame(this); hline->setFrameShape(QFrame::HLine); hline->setFrameShadow(QFrame::Sunken); QHBoxLayout *hlayout = new QHBoxLayout(); hlayout->setContentsMargins(2, 0, 5, 1); hlayout->setSpacing(0); hlayout->addWidget(serverIndicator, 0, Qt::AlignLeft); hlayout->addWidget(databaseIndicator, 0, Qt::AlignLeft); hlayout->addStretch(1); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); VERIFY(connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); QHBoxLayout *bottomlayout = new QHBoxLayout(); bottomlayout->addStretch(1); bottomlayout->addWidget(buttonBox); QVBoxLayout *layout = new QVBoxLayout(); layout->addLayout(hlayout); layout->addWidget(hline); layout->addWidget(new QLabel("Name:")); layout->addWidget(_nameEdit); layout->addWidget(new QLabel("Code:")); layout->addWidget(_queryText); layout->addLayout(bottomlayout); setLayout(layout); _nameEdit->setFocus(); } void FunctionTextEditor::setCursorPosition(int line, int column) { _queryText->sciScintilla()->setCursorPosition(line, column); } void FunctionTextEditor::setCode(const QString &code) { _queryText->sciScintilla()->setText(code); } void FunctionTextEditor::accept() { if (_nameEdit->text().isEmpty() && _queryText->sciScintilla()->text().isEmpty()) return; _function.setName(QtUtils::toStdString(_nameEdit->text())); _function.setCode(QtUtils::toStdString(_queryText->sciScintilla()->text())); BaseClass::accept(); } /* ** Configure QsciScintilla query widget */ void FunctionTextEditor::_configureQueryText() { const QFont &textFont = GuiRegistry::instance().font(); QsciLexerJavaScript *javaScriptLexer = new JSLexer(this); javaScriptLexer->setFont(textFont); _queryText->sciScintilla()->setAppropriateBraceMatching(); _queryText->sciScintilla()->setFont(textFont); _queryText->sciScintilla()->setPaper(QColor(255, 0, 0, 127)); _queryText->sciScintilla()->setLexer(javaScriptLexer); _queryText->sciScintilla()->setWrapMode((QsciScintilla::WrapMode)QsciScintilla::SC_WRAP_WORD); _queryText->sciScintilla()->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); _queryText->sciScintilla()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); _queryText->sciScintilla()->setStyleSheet("QFrame { background-color: rgb(73, 76, 78); border: 1px solid #c7c5c4; border-radius: 4px; margin: 0px; padding: 0px;}"); } } ================================================ FILE: src/robomongo/gui/dialogs/FunctionTextEditor.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QLineEdit; class QLabel; QT_END_NAMESPACE #include "robomongo/core/domain/MongoFunction.h" namespace Robomongo { class FindFrame; class FunctionTextEditor : public QDialog { Q_OBJECT public: typedef QDialog BaseClass; explicit FunctionTextEditor(const QString &server, const QString &database, const MongoFunction &code, QWidget *parent = 0); MongoFunction function() const { return _function; } void setCursorPosition(int line, int column); void setCode(const QString &code); public Q_SLOTS: virtual void accept(); private: void _configureQueryText(); QLineEdit *_nameEdit; FindFrame *_queryText; MongoFunction _function; }; } ================================================ FILE: src/robomongo/gui/dialogs/PreferencesDialog.cpp ================================================ #include "robomongo/gui/dialogs/PreferencesDialog.h" #include #include #include #include #include #include #include #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/AppStyle.h" #include "robomongo/gui/utils/ComboBoxUtils.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/settings/SettingsManager.h" namespace Robomongo { PreferencesDialog::PreferencesDialog(QWidget *parent) : BaseClass(parent) { setWindowIcon(GuiRegistry::instance().mainWindowIcon()); setWindowTitle("Preferences " PROJECT_NAME_TITLE); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setFixedSize(height, width); QVBoxLayout *layout = new QVBoxLayout(this); QHBoxLayout *defLayout = new QHBoxLayout(this); QLabel *defDisplayModeLabel = new QLabel("Default display mode:"); defLayout->addWidget(defDisplayModeLabel); _defDisplayModeComboBox = new QComboBox(); QStringList modes; for (int i = Text; i <= Custom; ++i) { modes.append(convertViewModeToString(static_cast(i))); } _defDisplayModeComboBox->addItems(modes); defLayout->addWidget(_defDisplayModeComboBox); layout->addLayout(defLayout); QHBoxLayout *timeZoneLayout = new QHBoxLayout(this); QLabel *timeZoneLabel = new QLabel("Display Dates in:"); timeZoneLayout->addWidget(timeZoneLabel); _timeZoneComboBox = new QComboBox(); QStringList times; for (int i = Utc; i <= LocalTime; ++i) { times.append(convertTimesToString(static_cast(i))); } _timeZoneComboBox->addItems(times); timeZoneLayout->addWidget(_timeZoneComboBox); layout->addLayout(timeZoneLayout); QHBoxLayout *uuidEncodingLayout = new QHBoxLayout(this); QLabel *uuidEncodingLabel = new QLabel("Legacy UUID Encoding:"); uuidEncodingLayout->addWidget(uuidEncodingLabel); _uuidEncodingComboBox = new QComboBox(); QStringList uuids; for (int i = DefaultEncoding; i <= PythonLegacy; ++i) { uuids.append(convertUUIDEncodingToString(static_cast(i))); } _uuidEncodingComboBox->addItems(uuids); uuidEncodingLayout->addWidget(_uuidEncodingComboBox); layout->addLayout(uuidEncodingLayout); _loadMongoRcJsCheckBox = new QCheckBox("Load .mongorc.js"); layout->addWidget(_loadMongoRcJsCheckBox); _disabelConnectionShortcutsCheckBox = new QCheckBox("Disable connection shortcuts"); layout->addWidget(_disabelConnectionShortcutsCheckBox); QHBoxLayout *stylesLayout = new QHBoxLayout(this); QLabel *stylesLabel = new QLabel("Styles:"); stylesLayout->addWidget(stylesLabel); _stylesComboBox = new QComboBox(); _stylesComboBox->addItems(AppStyleUtils::getSupportedStyles()); stylesLayout->addWidget(_stylesComboBox); layout->addLayout(stylesLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); VERIFY(connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); layout->addWidget(buttonBox); setLayout(layout); syncWithSettings(); } void PreferencesDialog::syncWithSettings() { utils::setCurrentText(_defDisplayModeComboBox, convertViewModeToString(Robomongo::AppRegistry::instance().settingsManager()->viewMode())); utils::setCurrentText(_timeZoneComboBox, convertTimesToString(Robomongo::AppRegistry::instance().settingsManager()->timeZone())); utils::setCurrentText(_uuidEncodingComboBox, convertUUIDEncodingToString(Robomongo::AppRegistry::instance().settingsManager()->uuidEncoding())); _loadMongoRcJsCheckBox->setChecked(AppRegistry::instance().settingsManager()->loadMongoRcJs()); _disabelConnectionShortcutsCheckBox->setChecked(AppRegistry::instance().settingsManager()->disableConnectionShortcuts()); utils::setCurrentText(_stylesComboBox, Robomongo::AppRegistry::instance().settingsManager()->currentStyle()); } void PreferencesDialog::accept() { ViewMode mode = convertStringToViewMode(QtUtils::toStdString(_defDisplayModeComboBox->currentText()).c_str()); Robomongo::AppRegistry::instance().settingsManager()->setViewMode(mode); SupportedTimes time = convertStringToTimes(QtUtils::toStdString(_timeZoneComboBox->currentText()).c_str()); Robomongo::AppRegistry::instance().settingsManager()->setTimeZone(time); UUIDEncoding uuidC = convertStringToUUIDEncoding(QtUtils::toStdString(_uuidEncodingComboBox->currentText()).c_str()); Robomongo::AppRegistry::instance().settingsManager()->setUuidEncoding(uuidC); AppRegistry::instance().settingsManager()->setLoadMongoRcJs(_loadMongoRcJsCheckBox->isChecked()); AppRegistry::instance().settingsManager()->setDisableConnectionShortcuts(_disabelConnectionShortcutsCheckBox->isChecked()); Robomongo::AppRegistry::instance().settingsManager()->setCurrentStyle(_stylesComboBox->currentText()); AppStyleUtils::applyStyle(_stylesComboBox->currentText()); Robomongo::AppRegistry::instance().settingsManager()->save(); return BaseClass::accept(); } } ================================================ FILE: src/robomongo/gui/dialogs/PreferencesDialog.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QComboBox; class QCheckBox; QT_END_NAMESPACE namespace Robomongo { class PreferencesDialog : public QDialog { Q_OBJECT public: typedef QDialog BaseClass; explicit PreferencesDialog(QWidget *parent); enum { height = 640, width = 480}; public Q_SLOTS: virtual void accept(); private: void syncWithSettings(); private: QComboBox *_defDisplayModeComboBox; QComboBox *_timeZoneComboBox; QComboBox *_uuidEncodingComboBox; QCheckBox *_loadMongoRcJsCheckBox; QCheckBox *_disabelConnectionShortcutsCheckBox; QComboBox *_stylesComboBox; }; } ================================================ FILE: src/robomongo/gui/dialogs/SSHTunnelTab.cpp ================================================ #include "robomongo/gui/dialogs/SSHTunnelTab.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/utils/ComboBoxUtils.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/SshSettings.h" #include "robomongo/gui/utils/GuiConstants.h" namespace { const QString askPasswordText = "Ask for password each time"; const QString askPassphraseText = "Ask for passphrase each time"; bool isFileExists(const QString &path) { QFileInfo fileInfo(path); return fileInfo.exists() && fileInfo.isFile(); } } namespace Robomongo { SshTunnelTab::SshTunnelTab(ConnectionSettings *settings) : _settings(settings) { SshSettings *info = settings->sshSettings(); _useSsh = new QCheckBox("Use SSH tunnel"); _useSsh->setStyleSheet("margin-bottom: 7px"); _useSsh->setChecked(info->enabled()); _askForPassword = new QCheckBox(askPasswordText); _askForPassword->setChecked(info->askPassword()); VERIFY(connect(_askForPassword, SIGNAL(stateChanged(int)), this, SLOT(askForPasswordStateChanged(int)))); _sshHostName = new QLineEdit(QtUtils::toQString(info->host())); _userName = new QLineEdit(QtUtils::toQString(info->userName())); _sshPort = new QLineEdit(QString::number(info->port())); _sshPort->setFixedWidth(40); QRegExp rx("\\d+"); //(0-65554) _sshPort->setValidator(new QRegExpValidator(rx, this)); _security = new QComboBox(); _security->addItems(QStringList() << "Password" << "Private Key"); VERIFY(connect(_security, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(securityChange(const QString&)))); _passwordBox = new QLineEdit(QtUtils::toQString(info->userPassword())); _passwordBox->setEchoMode(QLineEdit::Password); _passwordEchoModeButton = new QPushButton; _passwordEchoModeButton->setIcon(GuiRegistry::instance().hideIcon()); VERIFY(connect(_passwordEchoModeButton, SIGNAL(clicked()), this, SLOT(togglePasswordEchoMode()))); _privateKeyBox = new QLineEdit(QtUtils::toQString(info->privateKeyFile())); _privateKeyBox->setPlaceholderText( "DSA, RSA, and on Windows/macOS ECDSA, Ed25519 keys are supported." " PPK keys must be converted to OPENSSH format." ); _passphraseBox = new QLineEdit(QtUtils::toQString(info->passphrase())); _passphraseBox->setEchoMode(QLineEdit::Password); _passphraseEchoModeButton = new QPushButton; _passphraseEchoModeButton->setIcon(GuiRegistry::instance().hideIcon()); VERIFY(connect(_passphraseEchoModeButton, SIGNAL(clicked()), this, SLOT(togglePassphraseEchoMode()))); _passwordLabel = new QLabel("User Password:"); _sshPrivateKeyLabel = new QLabel("Private key:"); _sshPassphraseLabel = new QLabel("Passphrase:"); _sshAddressLabel = new QLabel("SSH Address:"); _sshUserNameLabel = new QLabel("SSH User Name:"); _sshAuthMethodLabel = new QLabel("SSH Auth Method:"); /* // Commented because of this: // https://github.com/paralect/robomongo/issues/391 #ifdef Q_OS_WIN QRegExp pathx("([a-zA-Z]:)?([\\\\/][a-zA-Z0-9_.-]+)+[\\\\/]?"); #else QRegExp pathx("^\\/?([\\d\\w\\.]+)(/([\\d\\w\\.]+))*\\/?$"); #endif // Q_OS_WIN _publicKeyBox->setValidator(new QRegExpValidator(pathx, this)); _privateKeyBox->setValidator(new QRegExpValidator(pathx, this)); */ QHBoxLayout *hostAndPasswordLayout = new QHBoxLayout; hostAndPasswordLayout->addWidget(_sshHostName); hostAndPasswordLayout->addWidget(new QLabel(":")); hostAndPasswordLayout->addWidget(_sshPort); QGridLayout *connectionLayout = new QGridLayout; connectionLayout->setAlignment(Qt::AlignTop); connectionLayout->setColumnStretch(1, 1); connectionLayout->setColumnMinimumWidth(0, _passwordLabel->sizeHint().width() + 5); connectionLayout->addWidget(_sshAddressLabel , 1, 0); connectionLayout->addLayout(hostAndPasswordLayout, 1, 1, 1, 2); connectionLayout->addWidget(_sshUserNameLabel, 2, 0); connectionLayout->addWidget(_userName, 2, 1, 1, 2); connectionLayout->addWidget(_sshAuthMethodLabel, 4, 0); connectionLayout->addWidget(_security, 4, 1, 1, 2); connectionLayout->addWidget(_passwordLabel, 5, 0); connectionLayout->addWidget(_passwordBox, 5, 1); connectionLayout->addWidget(_passwordEchoModeButton, 5, 2); _selectPrivateFileButton = new QPushButton("..."); _selectPrivateFileButton->setMaximumWidth(50); connectionLayout->addWidget(_sshPrivateKeyLabel, 7, 0); connectionLayout->addWidget(_privateKeyBox, 7, 1); connectionLayout->addWidget(_selectPrivateFileButton, 7, 2); connectionLayout->addWidget(_sshPassphraseLabel, 8, 0); connectionLayout->addWidget(_passphraseBox, 8, 1); connectionLayout->addWidget(_passphraseEchoModeButton, 8, 2); connectionLayout->addWidget(_askForPassword, 9, 1, 1, 2); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(_useSsh); mainLayout->addLayout(connectionLayout); setLayout(mainLayout); if (info->authMethod() == "publickey") { utils::setCurrentText(_security, "Private Key"); } else { utils::setCurrentText(_security, "Password"); } securityChange(_security->currentText()); VERIFY(connect(_selectPrivateFileButton, SIGNAL(clicked()), this, SLOT(setPrivateFile()))); sshSupportStateChange(_useSsh->checkState()); VERIFY(connect(_useSsh, SIGNAL(stateChanged(int)), this, SLOT(sshSupportStateChange(int)))); _sshHostName->setFocus(); #ifdef Q_OS_MAC _passwordEchoModeButton->setMaximumWidth(_selectPrivateFileButton->width()); _passphraseEchoModeButton->setMaximumWidth(_selectPrivateFileButton->width()); #else _passwordEchoModeButton->setMinimumWidth(_selectPrivateFileButton->width()); _passphraseEchoModeButton->setMinimumWidth(_selectPrivateFileButton->width()); #endif // Attempt to fix the issue for Windows High DPI button height is slightly taller than other widgets #ifdef Q_OS_WIN _passwordEchoModeButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); _passphraseEchoModeButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); _selectPrivateFileButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); #endif setDisabled(_settings->isReplicaSet()); toggleSshCheckboxToolTip(_settings->isReplicaSet()); } void SshTunnelTab::toggleSshCheckboxToolTip(bool isReplicaSet) { _useSsh->setToolTip(!isReplicaSet ? "" : "SSH is currently not supported for Replica Set connections"); } void SshTunnelTab::setPasswordFieldsEnabled(bool enabled) { _sshPassphraseLabel->setEnabled(enabled); _passphraseBox->setEnabled(enabled); _passwordBox->setEnabled(enabled); _passwordLabel->setEnabled(enabled); _passphraseEchoModeButton->setEnabled(enabled); _passwordEchoModeButton->setEnabled(enabled); } void SshTunnelTab::askForPasswordStateChanged(int state) { bool checked = state == Qt::Checked; if (checked) { _passphraseBox->setText(""); _passwordBox->setText(""); } setPasswordFieldsEnabled(!checked && _useSsh->isChecked()); } void SshTunnelTab::sshSupportStateChange(int state) { bool checked = state == Qt::Checked; _sshHostName->setEnabled(checked); _userName->setEnabled(checked); _sshPort->setEnabled(checked); _security->setEnabled(checked); _sshPrivateKeyLabel->setEnabled(checked); _privateKeyBox->setEnabled(checked); _selectPrivateFileButton->setEnabled(checked); _sshAddressLabel->setEnabled(checked); _sshUserNameLabel->setEnabled(checked); _sshAuthMethodLabel->setEnabled(checked); _askForPassword->setEnabled(checked); askForPasswordStateChanged(_askForPassword->checkState()); if (checked) _sshHostName->setFocus(); } void SshTunnelTab::securityChange(const QString& method) { bool isKey = method == "Private Key"; _sshPrivateKeyLabel->setVisible(isKey); _privateKeyBox->setVisible(isKey); _selectPrivateFileButton->setVisible(isKey); _sshPassphraseLabel->setVisible(isKey); _passphraseBox->setVisible(isKey); _passphraseEchoModeButton->setVisible(isKey); _passwordBox->setVisible(!isKey); _passwordLabel->setVisible(!isKey); _passwordEchoModeButton->setVisible(!isKey); _askForPassword->setText(isKey ? askPassphraseText : askPasswordText); } void SshTunnelTab::setPrivateFile() { // Default location QString sshDir = QString("%1/.ssh").arg(QDir::homePath()); QString filepath = QFileDialog::getOpenFileName(this, "Select private key file", sshDir, QObject::tr("Private key files (*)")); // Some strange behaviour at least on Mac happens when you // close QFileDialog. Focus switched to a different modal // dialog, not the one that was active before openning QFileDialog. // http://stackoverflow.com/questions/17998811/window-modal-qfiledialog-pushing-parent-to-background-after-exec QApplication::activeModalWidget()->raise(); QApplication::activeModalWidget()->activateWindow(); if (filepath.isNull()) return; _privateKeyBox->setText(filepath); } // SSHInfo::SupportedAuthenticationMetods SshTunnelTab::selectedAuthMethod() // { // if (_security->currentText() == "Private Key") // return SSHInfo::PUBLICKEY; // // return SSHInfo::PASSWORD; // } bool SshTunnelTab::accept() { bool const sshEnabled = this->isEnabled() && _useSsh->isChecked(); QString authMethod = _security->currentText() == "Private Key" ? "publickey" : "password"; // Check for existence of the private key file name // and try to expand "~" character when needed QString privateKey = _privateKeyBox->text(); if (sshEnabled && authMethod == "publickey" && !isFileExists(privateKey)) { bool failed = true; // Try to expand "~" if available if (privateKey.startsWith ("~/")) { privateKey.replace (0, 1, QDir::homePath()); if (isFileExists(privateKey)) { failed = false; } } if (failed) { QString message = QString("Private key file \"%1\" doesn't exist").arg(privateKey); QMessageBox::information(this, "Settings are incomplete", message); return false; } } SshSettings *info = _settings->sshSettings(); info->setHost(QtUtils::toStdString(_sshHostName->text())); info->setPort(_sshPort->text().toInt()); info->setUserName(QtUtils::toStdString(_userName->text())); info->setUserPassword(QtUtils::toStdString(_passwordBox->text())); info->setAskPassword(_askForPassword->isChecked()); info->setPrivateKeyFile(QtUtils::toStdString(privateKey)); info->setPassphrase(QtUtils::toStdString(_passphraseBox->text())); info->setAuthMethod(QtUtils::toStdString(authMethod)); info->setEnabled(sshEnabled); return true; } void SshTunnelTab::togglePasswordEchoMode() { bool isPassword = _passwordBox->echoMode() == QLineEdit::Password; _passwordBox->setEchoMode(isPassword ? QLineEdit::Normal: QLineEdit::Password); _passwordEchoModeButton->setIcon(isPassword ? GuiRegistry::instance().showIcon() : GuiRegistry::instance().hideIcon()); } void SshTunnelTab::togglePassphraseEchoMode() { bool isPassword = _passphraseBox->echoMode() == QLineEdit::Password; _passphraseBox->setEchoMode(isPassword ? QLineEdit::Normal: QLineEdit::Password); _passphraseEchoModeButton->setIcon(isPassword ? GuiRegistry::instance().showIcon() : GuiRegistry::instance().hideIcon()); } } ================================================ FILE: src/robomongo/gui/dialogs/SSHTunnelTab.h ================================================ #pragma once #include #include "robomongo/core/settings/ConnectionSettings.h" QT_BEGIN_NAMESPACE class QLabel; class QLineEdit; class QCheckBox; class QPushButton; class QComboBox; class QFrame; QT_END_NAMESPACE namespace Robomongo { class ConnectionSettings; class SshTunnelTab : public QWidget { Q_OBJECT public: SshTunnelTab(ConnectionSettings *settings); bool accept(); void toggleSshCheckboxToolTip(bool isReplicaSet); private Q_SLOTS: void sshSupportStateChange(int checked); void askForPasswordStateChanged(int checked); void securityChange(const QString& val); void setPrivateFile(); void togglePasswordEchoMode(); void togglePassphraseEchoMode(); private: void setPasswordFieldsEnabled(bool enabled); private: QCheckBox *_useSsh; QCheckBox *_askForPassword; QLineEdit *_sshHostName; QLineEdit *_userName; QLineEdit *_sshPort; QComboBox *_security; QLabel *_sshPrivateKeyLabel; QLabel *_sshPassphraseLabel; QLabel *_sshAddressLabel; QLabel *_sshUserNameLabel; QLabel *_sshAuthMethodLabel; QPushButton *_selectPrivateFileButton; QLineEdit *_passwordBox; QLabel *_passwordLabel; QPushButton *_passwordEchoModeButton; QLineEdit *_privateKeyBox; QLineEdit *_passphraseBox; QPushButton *_passphraseEchoModeButton; ConnectionSettings *const _settings; }; } ================================================ FILE: src/robomongo/gui/dialogs/SSLTab.cpp ================================================ #include "robomongo/gui/dialogs/SSLTab.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/SshSettings.h" #include "robomongo/core/settings/SslSettings.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/utils/ComboBoxUtils.h" #include "robomongo/gui/utils/GuiConstants.h" namespace { // Helper function: Check file existence, return true if file exists, false otherwise bool fileExists(const QString &path) { QFileInfo fileInfo(path); return (fileInfo.exists() && fileInfo.isFile()); } // Helper hint strings QString const CA_FILE_HINT = " mongo --tlsCAFile : Certificate Authority file for TLS"; QString const PEM_FILE_HINT = " mongo --tlsCertificateKeyFile : PEM certificate/key file for TLS"; QString const PEM_PASS_HINT = " mongo --tlsCertificateKeyFilePassword : Password for key in PEM file for TLS"; QString const ALLOW_INVALID_HOSTNAME_HINT = " mongo --tlsAllowInvalidHostnames : Allow connections " "to servers with non-matching hostnames"; QString const ALLOW_INVALID_CERTIFICATES_HINT = " mongo --tlsAllowInvalidCertificates : Allow connections " "to servers with invalid certificates"; QString const CRL_FILE_HINT = " mongo --tlsCRLFile : Certificate Revocation List file for TLS"; } namespace Robomongo { SSLTab::SSLTab(ConnectionSettings *connSettings) : _connSettings(connSettings) { const SslSettings* const sslSettings = _connSettings->sslSettings(); // Use TLS section _useSslCheckBox = new QCheckBox("Use TLS protocol"); _useSslCheckBox->setStyleSheet("margin-bottom: 7px"); VERIFY(connect(_useSslCheckBox, SIGNAL(stateChanged(int)), this, SLOT(useSslCheckBoxStateChange(int)))); // Auth. Method section _authMethodLabel = new QLabel("Authentication Method: "); _authMethodComboBox = new QComboBox; _authMethodComboBox->addItem("Self-signed Certificate"); _authMethodComboBox->addItem("Use CA Certificate"); _authMethodComboBox->setItemData(0, ALLOW_INVALID_CERTIFICATES_HINT, Qt::ToolTipRole); _authMethodComboBox->setItemData(1, CA_FILE_HINT, Qt::ToolTipRole); _selfSignedInfoStr = new QLabel("In general, avoid using self-signed certificates unless the network is trusted. " "If self-signed certificate is used, the communications channel will be encrypted however there will be " "no validation of server identity."); _selfSignedInfoStr->setWordWrap(true); _selfSignedInfoStr->setToolTip(ALLOW_INVALID_CERTIFICATES_HINT); _caFileLabel = new QLabel("CA Certificate:"); _caFileLabel->setToolTip(CA_FILE_HINT); _caFilePathLineEdit = new QLineEdit; _caFileBrowseButton = new QPushButton("..."); _caFileBrowseButton->setMaximumWidth(50); VERIFY(connect(_caFileBrowseButton, SIGNAL(clicked()), this, SLOT(on_caFileBrowseButton_clicked()))); VERIFY(connect(_authMethodComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(on_authModeComboBox_change(int)))); // PEM file section _usePemFileCheckBox = new QCheckBox("Use PEM Cert./Key: "); _usePemFileCheckBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); _pemFileInfoStr = new QLabel("Enable this option to connect to a MongoDB that requires CA-signed client certificates/key file."); _pemFileInfoStr->setWordWrap(true); #ifdef Q_OS_WIN _pemFileInfoStr->setContentsMargins(0,2,0,0); // Top alignment adjustment required only for Windows #endif _pemFileLabel = new QLabel("PEM Certificate/Key: "); _pemFileLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); _pemFileLabel->setToolTip(PEM_FILE_HINT); _pemFilePathLineEdit = new QLineEdit; _pemFileBrowseButton = new QPushButton("..."); _pemFileBrowseButton->setMaximumWidth(50); VERIFY(connect(_usePemFileCheckBox, SIGNAL(toggled(bool)), this, SLOT(on_usePemFileCheckBox_toggle(bool)))); VERIFY(connect(_pemFileBrowseButton, SIGNAL(clicked()), this, SLOT(on_pemKeyFileBrowseButton_clicked()))); // PEM Passphrase section _pemPassLabel = new QLabel("Passphrase: "); _pemPassLabel->setToolTip(PEM_PASS_HINT); _pemPassLineEdit = new QLineEdit; _pemPassShowButton = new QPushButton; // Fix for MAC OSX: PEM pass show button was created bigger, making it same size as other pushbuttons _pemPassShowButton->setMaximumWidth(_pemFileBrowseButton->width()); VERIFY(connect(_pemPassShowButton, SIGNAL(clicked()), this, SLOT(togglePassphraseShowMode()))); togglePassphraseShowMode(); _askPemPassCheckBox = new QCheckBox("Ask for passphrase each time"); _askPemPassCheckBox->setChecked(sslSettings->askPassphrase()); VERIFY(connect(_askPemPassCheckBox, SIGNAL(toggled(bool)), this, SLOT(on_askPemPassCheckBox_toggle(bool)))); // Advanced options _useAdvancedOptionsCheckBox = new QCheckBox("Advanced Options"); _useAdvancedOptionsCheckBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); VERIFY(connect(_useAdvancedOptionsCheckBox, SIGNAL(toggled(bool)), this, SLOT(on_useAdvancedOptionsCheckBox_toggle(bool)))); _crlFileLabel = new QLabel("CRL (Revocation List): "); _crlFileLabel->setToolTip(CRL_FILE_HINT); _crlFilePathLineEdit = new QLineEdit; _crlFileBrowseButton = new QPushButton("..."); _crlFileBrowseButton->setMaximumWidth(50); VERIFY(connect(_crlFileBrowseButton, SIGNAL(clicked()), this, SLOT(on_crlFileBrowseButton_clicked()))); _allowInvalidHostnamesLabel = new QLabel("Invalid Hostnames: "); _allowInvalidHostnamesLabel->setToolTip(ALLOW_INVALID_HOSTNAME_HINT); _allowInvalidHostnamesComboBox = new QComboBox; _allowInvalidHostnamesComboBox->addItem("Not Allowed"); _allowInvalidHostnamesComboBox->addItem("Allowed"); _allowInvalidHostnamesComboBox->setCurrentIndex(sslSettings->allowInvalidHostnames()); // Layouts // Auth. method section QGridLayout* gridLayout = new QGridLayout; gridLayout->addWidget(_authMethodLabel, 0 ,0); gridLayout->addWidget(_authMethodComboBox, 0 ,1, 1, 2); gridLayout->addWidget(_selfSignedInfoStr, 1, 1, 1, 2); gridLayout->addWidget(_caFileLabel, 2, 0); gridLayout->addWidget(_caFilePathLineEdit, 2, 1); gridLayout->addWidget(_caFileBrowseButton, 2, 2); #ifdef _WIN32 gridLayout->addWidget(new QLabel(""), 3, 0); #endif // PEM File Section gridLayout->addWidget(_usePemFileCheckBox, 4, 0, Qt::AlignTop); gridLayout->addWidget(_pemFileInfoStr, 4, 1, 1, 2); gridLayout->addWidget(_pemFileLabel, 5, 0); gridLayout->addWidget(_pemFilePathLineEdit, 5, 1); gridLayout->addWidget(_pemFileBrowseButton, 5, 2); gridLayout->addWidget(_pemPassLabel, 6, 0); gridLayout->addWidget(_pemPassLineEdit, 6, 1); gridLayout->addWidget(_pemPassShowButton, 6, 2); gridLayout->addWidget(_askPemPassCheckBox, 7, 1, 1, 2); #ifdef _WIN32 gridLayout->addWidget(new QLabel(""), 8, 0); #endif // Advanced section gridLayout->addWidget(_useAdvancedOptionsCheckBox, 9, 0, Qt::AlignTop); gridLayout->addWidget(_crlFileLabel, 10, 0); gridLayout->addWidget(_crlFilePathLineEdit, 10, 1); gridLayout->addWidget(_crlFileBrowseButton, 10, 2); gridLayout->addWidget(_allowInvalidHostnamesLabel, 11, 0); gridLayout->addWidget(_allowInvalidHostnamesComboBox, 11, 1, Qt::AlignLeft); auto mainLayout = new QVBoxLayout; mainLayout->setAlignment(Qt::AlignTop); mainLayout->addWidget(_useSslCheckBox); mainLayout->addLayout(gridLayout); setLayout(mainLayout); // Load SSL settings to update UI states _useSslCheckBox->setChecked(sslSettings->sslEnabled()); _authMethodComboBox->setCurrentIndex(!sslSettings->allowInvalidCertificates()); _caFilePathLineEdit->setText(QString::fromStdString(sslSettings->caFile())); _usePemFileCheckBox->setChecked(sslSettings->usePemFile()); _pemFilePathLineEdit->setText(QString::fromStdString(sslSettings->pemKeyFile())); _askPemPassCheckBox->setChecked(sslSettings->askPassphrase()); // Load passphrase only if askPassphrase is false if (!sslSettings->askPassphrase()) { _pemPassLineEdit->setText(QString::fromStdString(sslSettings->pemPassPhrase())); } _useAdvancedOptionsCheckBox->setChecked(sslSettings->useAdvancedOptions()); _allowInvalidHostnamesComboBox->setCurrentIndex(sslSettings->allowInvalidHostnames()); _crlFilePathLineEdit->setText(QString::fromStdString(sslSettings->crlFile())); // Update UI inter-connected (signal-slot) widget states on_authModeComboBox_change(_authMethodComboBox->currentIndex()); on_usePemFileCheckBox_toggle(_usePemFileCheckBox->isChecked()); on_askPemPassCheckBox_toggle(_askPemPassCheckBox->isChecked()); on_useAdvancedOptionsCheckBox_toggle(_useAdvancedOptionsCheckBox->isChecked()); // Enable/disable all SSL tab widgets useSslCheckBoxStateChange(_useSslCheckBox->checkState()); // Attempt to fix issue for Windows High DPI button height is slightly taller than other widgets #ifdef Q_OS_WIN _caFileBrowseButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); _pemFileBrowseButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); _pemPassShowButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); _crlFileBrowseButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT); #endif } bool SSLTab::accept() { saveSslSettings(); return validate(); } bool SSLTab::sslEnabled() const { return _useSslCheckBox->isChecked(); } void SSLTab::clearTab() { _authMethodComboBox->setCurrentIndex(1); _caFilePathLineEdit->clear(); _usePemFileCheckBox->setChecked(false); _pemFilePathLineEdit->clear(); _pemPassLineEdit->clear(); _useAdvancedOptionsCheckBox->setChecked(false); _allowInvalidHostnamesComboBox->setCurrentIndex(0); } void SSLTab::setSslOptions( int index, bool allowInvalidHostnames, std::string_view caFile, std::string_view certPemFile, std::string_view certPemFilePwd ) { _useSslCheckBox->setChecked(true); _authMethodComboBox->setCurrentIndex(index); _caFilePathLineEdit->setText(QString::fromStdString(std::string(caFile))); if(!certPemFile.empty()) _usePemFileCheckBox->setChecked(true); _pemFilePathLineEdit->setText(QString::fromStdString(std::string(certPemFile))); _pemPassLineEdit->setText(QString::fromStdString(std::string(certPemFilePwd))); if (allowInvalidHostnames) _useAdvancedOptionsCheckBox->setChecked(true); _allowInvalidHostnamesComboBox->setCurrentIndex(allowInvalidHostnames); } void SSLTab::useSslCheckBoxStateChange(int state) { bool isChecked = static_cast(state); _authMethodLabel->setDisabled(!isChecked); _authMethodComboBox->setDisabled(!isChecked); _selfSignedInfoStr->setDisabled(!isChecked); _caFileLabel->setDisabled(!isChecked); _caFilePathLineEdit->setDisabled(!isChecked); _caFileBrowseButton->setDisabled(!isChecked); _usePemFileCheckBox->setDisabled(!isChecked); _pemFileInfoStr->setDisabled(!isChecked); _pemFileLabel->setDisabled(!isChecked); _pemFilePathLineEdit->setDisabled(!isChecked); _pemFileBrowseButton->setDisabled(!isChecked); _pemPassLabel->setDisabled(!isChecked); _pemPassLineEdit->setDisabled(!isChecked); _pemPassShowButton->setDisabled(!isChecked); _askPemPassCheckBox->setDisabled(!isChecked); _useAdvancedOptionsCheckBox->setDisabled(!isChecked); _crlFileLabel->setDisabled(!isChecked); _crlFilePathLineEdit->setDisabled(!isChecked); _crlFileBrowseButton->setDisabled(!isChecked); _allowInvalidHostnamesLabel->setDisabled(!isChecked); _allowInvalidHostnamesComboBox->setDisabled(!isChecked); if (isChecked) // Update some widgets only if SSL enabled { on_usePemFileCheckBox_toggle(_usePemFileCheckBox->isChecked()); on_askPemPassCheckBox_toggle(_askPemPassCheckBox->isChecked()); } } void SSLTab::on_authModeComboBox_change(int index) { bool const isCaSigned = static_cast(index); _selfSignedInfoStr->setVisible(!isCaSigned); _caFileLabel->setVisible(isCaSigned); _caFilePathLineEdit->setVisible(isCaSigned); _caFileBrowseButton->setVisible(isCaSigned); } void SSLTab::on_usePemFileCheckBox_toggle(bool isChecked) { _pemFileInfoStr->setVisible(!isChecked); _pemFileLabel->setVisible(isChecked); _pemFilePathLineEdit->setVisible(isChecked); _pemFileBrowseButton->setVisible(isChecked); _pemPassLabel->setVisible(isChecked); _pemPassLineEdit->setVisible(isChecked); _pemPassShowButton->setVisible(isChecked); _askPemPassCheckBox->setVisible(isChecked); } void SSLTab::on_useAdvancedOptionsCheckBox_toggle(bool isChecked) { _crlFileLabel->setVisible(isChecked); _crlFilePathLineEdit->setVisible(isChecked); _crlFileBrowseButton->setVisible(isChecked); _allowInvalidHostnamesLabel->setVisible(isChecked); _allowInvalidHostnamesComboBox->setVisible(isChecked); } void SSLTab::on_caFileBrowseButton_clicked() { QString const& fileName = openFileBrowseDialog(_caFilePathLineEdit->text()); if (!fileName.isEmpty()) { _caFilePathLineEdit->setText(fileName); } } void SSLTab::on_pemKeyFileBrowseButton_clicked() { QString const& fileName = openFileBrowseDialog(_pemFilePathLineEdit->text()); if (!fileName.isEmpty()) { _pemFilePathLineEdit->setText(fileName); } } void SSLTab::on_crlFileBrowseButton_clicked() { QString const& fileName = openFileBrowseDialog(_crlFilePathLineEdit->text()); if (!fileName.isEmpty()) { _crlFilePathLineEdit->setText(fileName); } } void SSLTab::togglePassphraseShowMode() { bool isPassword = _pemPassLineEdit->echoMode() == QLineEdit::Password; _pemPassLineEdit->setEchoMode(isPassword ? QLineEdit::Normal : QLineEdit::Password); _pemPassShowButton->setIcon(isPassword ? GuiRegistry::instance().showIcon() : GuiRegistry::instance().hideIcon()); } void SSLTab::on_askPemPassCheckBox_toggle(bool checked) { //if (_usePemFileCheckBox->isChecked()) //{ _pemPassLabel->setDisabled(checked); _pemPassLineEdit->setDisabled(checked); _pemPassShowButton->setDisabled(checked); if (checked) { _pemPassLineEdit->setText(""); // clear passphrase on UI } //} } bool SSLTab::validate() { // Validate existence of files auto const& resultAndFileName = checkExistenseOfFiles(); if (!resultAndFileName.first) { QString const& nonExistingFile = resultAndFileName.second; QMessageBox errorBox; errorBox.critical(this, "Error", ("Error: " + nonExistingFile + " file does not exist")); errorBox.adjustSize(); return false; } return true; } std::pair SSLTab::checkExistenseOfFiles() const { if (_caFilePathLineEdit->isEnabled() && _caFilePathLineEdit->isVisible()) { if (!fileExists(_caFilePathLineEdit->text())) { return {false, "CA-signed certificate"}; } } if (_pemFilePathLineEdit->isVisible() && _pemFilePathLineEdit->isEnabled()) { if (!fileExists(_pemFilePathLineEdit->text())) { return {false, "PEM Certificate/Key"}; } } if (!_crlFilePathLineEdit->text().isEmpty()) { if (!fileExists(_crlFilePathLineEdit->text())) { return {false, "CRL (Revocation List)"}; } } return {true, ""}; } void SSLTab::saveSslSettings() const { SslSettings* sslSettings = _connSettings->sslSettings(); sslSettings->enableSSL(_useSslCheckBox->isChecked()); sslSettings->setAllowInvalidCertificates(!static_cast(_authMethodComboBox->currentIndex())); sslSettings->setCaFile(QtUtils::toStdString(_caFilePathLineEdit->text())); sslSettings->setUsePemFile(_usePemFileCheckBox->isChecked()); sslSettings->setPemKeyFile(QtUtils::toStdString(_pemFilePathLineEdit->text())); sslSettings->setAskPassphrase(_askPemPassCheckBox->isChecked()); // save passphrase only if _askPemPassCheckBox is not checked; otherwise don't save and clear saved passphrase if (!_askPemPassCheckBox->isChecked()) { sslSettings->setPemPassPhrase(QtUtils::toStdString(_pemPassLineEdit->text())); } else { sslSettings->setPemPassPhrase(""); } sslSettings->setUseAdvancedOptions(_useAdvancedOptionsCheckBox->isChecked()); sslSettings->setCrlFile(QtUtils::toStdString(_crlFilePathLineEdit->text())); sslSettings->setAllowInvalidHostnames(static_cast(_allowInvalidHostnamesComboBox->currentIndex())); } QString SSLTab::openFileBrowseDialog(const QString& initialPath) { QString filePath = initialPath; // If user has previously selected a file, initialize file dialog with that file's // path and name; otherwise, use user's home directory. if (filePath.isEmpty()) { filePath = QDir::homePath(); } QString fileName = QFileDialog::getOpenFileName(this, tr("Choose File"), filePath); // Some strange behaviour at least on Mac happens when you close QFileDialog. Focus switched to a different modal // dialog, not the one that was active before openning QFileDialog. // http://stackoverflow.com/questions/17998811/window-modal-qfiledialog-pushing-parent-to-background-after-exec QApplication::activeModalWidget()->raise(); QApplication::activeModalWidget()->activateWindow(); return QDir::toNativeSeparators(fileName); } } ================================================ FILE: src/robomongo/gui/dialogs/SSLTab.h ================================================ #pragma once #include #include "robomongo/core/settings/ConnectionSettings.h" QT_BEGIN_NAMESPACE class QLabel; class QLineEdit; class QCheckBox; class QPushButton; class QRadioButton; class QComboBox; QT_END_NAMESPACE namespace Robomongo { class ConnectionSettings; class SSLTab : public QWidget { Q_OBJECT public: /** * @brief Construct SSL tab of ConnectionDialog * @param settings: Pointer to active connection's ConnectionsSettings member */ SSLTab(ConnectionSettings *settings); /** * @brief Save dialog input into connection's ssl settings * @return true on success, false otherwise */ bool accept(); /** * @return true if use SSL checkbox is checked, false otherwise */ bool sslEnabled() const; void clearTab(); void setSslOptions( int index, bool allowInvalidHostnames, std::string_view caFile, std::string_view certPemFile, std::string_view certPemFilePwd ); private Q_SLOTS : /** * @brief Disable/enable widgets according to SSL check box state */ void useSslCheckBoxStateChange(int checked); /** * @brief Disable/enable widgets according to state of authentication method combo box */ void on_authModeComboBox_change(int index); /** * @brief Disable/enable widgets according to state of use PEM file combo box */ void on_usePemFileCheckBox_toggle(bool checked); /** * @brief Disable/enable widgets according to state of advanced options combo box */ void on_useAdvancedOptionsCheckBox_toggle(bool checked); /** * @brief File browsers for SSL related cert/key files */ void on_caFileBrowseButton_clicked(); void on_pemKeyFileBrowseButton_clicked(); void on_crlFileBrowseButton_clicked(); /** * @brief Show/hide client cert's passphrase on Show/Hide button pressed */ void togglePassphraseShowMode(); /** * @brief Enable/disable/clean PEM passphrase widgets section */ void on_askPemPassCheckBox_toggle(bool checked); private: /** * @brief Do validation according to user input in UI * @return true on success, false otherwise */ bool validate(); /** * @brief Check existence of files: CA cert, Client Cert and CRL file * @return true if all files exist, false any of them does not exist * @return QString Lable of file which does not exist, empty string if all files exist */ std::pair checkExistenseOfFiles() const; /** * @brief Save dialog input into connection's ssl settings */ void saveSslSettings() const; /** * @brief * @param initialPath Previously selected file path * @return Selected file absolute path and file name */ QString openFileBrowseDialog(const QString& initialPath); /** * @brief Main checkbox to disable/enable all other SSL tab widgets */ QCheckBox *_useSslCheckBox; /** * @brief Auth. Method widgets */ QLabel *_authMethodLabel; QComboBox *_authMethodComboBox; QLabel *_selfSignedInfoStr; QLabel *_caFileLabel; QLineEdit *_caFilePathLineEdit; QPushButton *_caFileBrowseButton; /** * @brief PEM file widgets */ QCheckBox* _usePemFileCheckBox; QLabel* _pemFileInfoStr; QLabel* _pemFileLabel; QLineEdit *_pemFilePathLineEdit; QPushButton *_pemFileBrowseButton; QLabel* _pemPassLabel; QLineEdit* _pemPassLineEdit; QPushButton* _pemPassShowButton; QCheckBox* _askPemPassCheckBox; /** * @brief Advanced options widgets */ QCheckBox* _useAdvancedOptionsCheckBox; QLabel *_crlFileLabel; QLineEdit *_crlFilePathLineEdit; QPushButton *_crlFileBrowseButton; QLabel *_allowInvalidHostnamesLabel; QComboBox *_allowInvalidHostnamesComboBox; /** * @brief Pointer to active connection's settings */ ConnectionSettings *const _connSettings; }; } /* end of Robomongo namespace */ ================================================ FILE: src/robomongo/gui/editors/FindFrame.cpp ================================================ #include "robomongo/gui/editors/FindFrame.h" #include #include #include #include #include #include #include #include #include #include "robomongo/gui/editors/PlainJavaScriptEditor.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/KeyboardManager.h" #include "robomongo/gui/widgets/workarea/ScriptWidget.h" namespace Robomongo { FindFrame::FindFrame(QWidget *parent) : BaseClass(parent), _parent(parent), _scin(new RoboScintilla()), _findPanel(new QFrame(this)), _close(new QToolButton(this)), _findLine(new QLineEdit(this)), _next(new QPushButton("Next", this)), _prev(new QPushButton("Previous", this)), _caseSensitive(new QCheckBox("Match case", this)), _commentSign("// "), _commentSignLength(3) { _close->setIcon(QIcon(":/robomongo/icons/close_2_16x16.png")); _close->setToolButtonStyle(Qt::ToolButtonIconOnly); _close->setIconSize(QSize(16, 16)); _close->hide(); // We do not need close button because ESC works _findLine->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute); QHBoxLayout *layout = new QHBoxLayout(); layout->setContentsMargins(2, 0, 6, 0); layout->setSpacing(7); layout->addWidget(_findLine); layout->addWidget(_next); layout->addWidget(_prev); layout->addWidget(_caseSensitive); _findPanel->setFixedHeight(HeightFindPanel); _findPanel->setLayout(layout); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); mainLayout->addWidget(_scin, 1); mainLayout->addWidget(_findPanel, 0, Qt::AlignBottom); setLayout(mainLayout); _findPanel->hide(); VERIFY(connect(_close, SIGNAL(clicked()), _findPanel, SLOT(hide()))); VERIFY(connect(_next, SIGNAL(clicked()), this, SLOT(goToNextElement()))); VERIFY(connect(_prev, SIGNAL(clicked()), this, SLOT(goToPrevElement()))); } void FindFrame::wheelEvent(QWheelEvent *e) { return BaseClass::wheelEvent(e); } void FindFrame::keyPressEvent(QKeyEvent *keyEvent) { bool isFocusScin = _scin->isActiveWindow(); bool isShowFind = _findPanel->isVisible(); if (Qt::Key_Escape == keyEvent->key() && isFocusScin && isShowFind) { // Hide & Show of Scintilla widget solves problem of UI blinking _scin->hide(); _findPanel->hide(); _scin->setFocus(); _scin->show(); return keyEvent->accept(); } else if (Qt::Key_Return == keyEvent->key() && (keyEvent->modifiers() & Qt::ShiftModifier) && isFocusScin && isShowFind) { goToPrevElement(); } else if (Qt::Key_Return == keyEvent->key() && isFocusScin && isShowFind) { goToNextElement(); } else if (((keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() == Qt::Key_F) && isFocusScin) { _findPanel->show(); _findLine->setFocus(); _findLine->selectAll(); return keyEvent->accept(); } else if (KeyboardManager::isToggleCommentsShortcut(keyEvent)) { toggleComments(); return keyEvent->accept(); } else { return BaseClass::keyPressEvent(keyEvent); } } void FindFrame::goToNextElement() { findElement(true); } void FindFrame::goToPrevElement() { findElement(false); } void FindFrame::findElement(bool forward) { const QString &text = _findLine->text(); if (!text.isEmpty()) { bool re = false; bool wo = false; bool looped = true; int index = 0; int line = 0; _scin->getCursorPosition(&line, &index); if (!forward) index -= _scin->selectedText().length(); _scin->setCursorPosition(line, 0); bool isFounded = _scin->findFirst(text, re, _caseSensitive->checkState() == Qt::Checked, wo, looped, forward, line, index); if (isFounded) { _scin->ensureCursorVisible(); } else { QMessageBox::warning(this, tr("Search"), tr("The specified text was not found.")); } } } void FindFrame::toggleComments() { int lineFrom, indexFrom, lineTo, indexTo; QString line; ScriptWidget *container; bool commentOut, is_textAndCursorNotificationsDisabled; _scin->getSelection(&lineFrom, &indexFrom, &lineTo, &indexTo); if (-1 == indexTo) { // There is no selection. Get cursor position _scin->getCursorPosition(&lineFrom, &indexFrom); lineTo = lineFrom; } // Define what action should be done for each selected line line = _scin->text(lineFrom); if (line.startsWith(_commentSign)) { // Remove comment sign commentOut = false; } else { // Add comment sign commentOut = true; } // To prevent displaying of autocomplete menu container = dynamic_cast(_parent); if (NULL != container) { is_textAndCursorNotificationsDisabled = container->getDisableTextAndCursorNotifications(); container->setDisableTextAndCursorNotifications(true); } for (int lineIndex = lineFrom; lineIndex <= lineTo; ++lineIndex) { setLineComment(lineIndex, commentOut); } /** * Changing cursor position cancels the selection, so restoring original position of the cursor is done * only if there was no selection at the beginning of the operation */ if (-1 == indexTo) { // No selection, set original cursor position if (commentOut) { _scin->setCursorPosition(lineFrom, indexFrom + _commentSignLength); } else { _scin->setCursorPosition(lineFrom, indexFrom - _commentSignLength); } } else { // Restore original selection if (commentOut) { _scin->setSelection(lineFrom, indexFrom + _commentSignLength, lineTo, indexTo + _commentSignLength); } else { _scin->setSelection(lineFrom, indexFrom - _commentSignLength, lineTo, indexTo - _commentSignLength); } } if (NULL != container) { container->setDisableTextAndCursorNotifications(is_textAndCursorNotificationsDisabled); } } void FindFrame::setLineComment(const int lineIndex, const bool commentOut) { QString line; line = _scin->text(lineIndex); if (commentOut) { // Add comment sign _scin->insertAt(_commentSign, lineIndex, 0); } else if (line.startsWith(_commentSign)) { // Remove comment sign _scin->setSelection(lineIndex, 0, lineIndex, _commentSignLength); _scin->removeSelectedText(); } } FindFrame::~FindFrame() { delete _scin; } } ================================================ FILE: src/robomongo/gui/editors/FindFrame.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QTextEdit; class QPushButton; class QToolButton; class QCheckBox; class QLineEdit; QT_END_NAMESPACE class QsciScintilla; namespace Robomongo { class RoboScintilla; class FindFrame : public QFrame { Q_OBJECT public: enum { HeightFindPanel = 38 }; typedef QFrame BaseClass; explicit FindFrame(QWidget *parent); RoboScintilla *const sciScintilla() const { return _scin; } void toggleComments(); virtual ~FindFrame(); protected: virtual void wheelEvent(QWheelEvent *e); virtual void keyPressEvent(QKeyEvent *e); private Q_SLOTS: void goToNextElement(); void goToPrevElement(); private: void findElement(bool forward); void setLineComment(const int lineIndex, const bool commentOut); RoboScintilla *const _scin; QFrame *const _findPanel; QLineEdit *const _findLine; QToolButton *const _close; QPushButton *const _next; QPushButton *const _prev; QCheckBox *const _caseSensitive; const char *_commentSign; const int _commentSignLength; QWidget *_parent; }; } ================================================ FILE: src/robomongo/gui/editors/JSLexer.cpp ================================================ #include "robomongo/gui/editors/JSLexer.h" #include namespace Robomongo { JSLexer::JSLexer(QObject *parent) : QsciLexerJavaScript(parent) { } QColor JSLexer::defaultPaper(int style) const { return QColor(73, 76, 78); //return QColor(48, 10, 36); // Ubuntu-style background } QColor JSLexer::defaultColor(int style) const { switch (style) { case Default: return QColor("#FFFFFF"); case Comment: case CommentLine: return QColor("#999999"); case CommentDoc: case CommentLineDoc: return QColor("#999999"); case Number: //return QColor("#DBF76C"); return QColor("#FFA09E"); case Keyword: //return QColor("#FDE15D"); return QColor("#BEE5FF"); case DoubleQuotedString: case SingleQuotedString: case RawString: //return QColor("#5ED363"); return QColor("#C6F079"); case PreProcessor: return QColor("#00FF00"); case Operator: case UnclosedString: //return QColor("#FF7729"); //return QColor("#AFBED4"); return QColor("#FFD14D"); case Regex: return QColor("#FFFFFF"); case CommentDocKeyword: return QColor("#FFFFFF"); case CommentDocKeywordError: return QColor("#FFFFFF"); case InactiveDefault: case InactiveUUID: case InactiveCommentLineDoc: case InactiveKeywordSet2: case InactiveCommentDocKeyword: case InactiveCommentDocKeywordError: return QColor("#FFFFFF"); case InactiveComment: case InactiveCommentLine: case InactiveNumber: return QColor("#FFFFFF"); case InactiveCommentDoc: return QColor("#FFFFFF"); case InactiveKeyword: return QColor("#FFFFFF"); case InactiveDoubleQuotedString: case InactiveSingleQuotedString: case InactiveRawString: return QColor("#FFFFFF"); case InactivePreProcessor: return QColor("#FFFFFF"); case InactiveOperator: case InactiveIdentifier: case InactiveGlobalClass: return QColor("#FFFFFF"); case InactiveUnclosedString: return QColor("#FFFFFF"); case InactiveVerbatimString: return QColor("#FFFFFF"); case InactiveRegex: return QColor("#FFFFFF"); } return QColor("#FFFFFF"); // return QsciLexer::defaultColor(style); } const char *JSLexer::keywords(int set) const { if (set == 1) return "abstract boolean break byte case catch char class const continue " "debugger default delete do double else enum export extends final " "finally float for function goto if implements import in instanceof " "int interface long native new package private protected public " "return short static super switch synchronized this throw throws " "transient try typeof var void volatile while with " "ISODate ObjectId Mongo Date NumberInt Number NumberLong Timestamp _id null false true " "UUID LUUID PYUUID CSUUID JUUID NUUID "; return 0; } } ================================================ FILE: src/robomongo/gui/editors/JSLexer.h ================================================ #pragma once #include #include #include namespace Robomongo { class JSLexer : public QsciLexerJavaScript { Q_OBJECT // This Q_OBJECT macro produce the following error for VC, but works on GCC: // unresolved external symbol "public: static struct QMetaObject const... // Q_OBJECT public: JSLexer(QObject *parent = 0); QColor defaultPaper(int style) const; QColor defaultColor(int style) const; const char *keywords(int set) const; }; } ================================================ FILE: src/robomongo/gui/editors/PlainJavaScriptEditor.cpp ================================================ #include "robomongo/gui/editors/PlainJavaScriptEditor.h" #include #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/utils/QtUtils.h" namespace { /** * @brief Returns the number of digits in an 32-bit integer * http://stackoverflow.com/questions/1489830/efficient-way-to-determine-number-of-digits-in-an-integer */ int getNumberOfDigits(int x) { if (x < 0) return getNumberOfDigits(-x) + 1; if (x >= 10000) { if (x >= 10000000) { if (x >= 100000000) { if (x >= 1000000000) return 10; return 9; } return 8; } if (x >= 100000) { if (x >= 1000000) return 7; return 6; } return 5; } if (x >= 100) { if (x >= 1000) return 4; return 3; } if (x >= 10) return 2; return 1; } } namespace Robomongo { const QColor RoboScintilla::marginsBackgroundColor = QColor(73, 76, 78); const QColor RoboScintilla::caretForegroundColor = QColor("#FFFFFF"); const QColor RoboScintilla::matchedBraceForegroundColor = QColor("#FF8861"); RoboScintilla::RoboScintilla(QWidget *parent) : QsciScintilla(parent), _ignoreEnterKey(false), _ignoreTabKey(false), _lineNumberDigitWidth(0), _lineNumberMarginWidth(0) { setAutoIndent(true); setIndentationsUseTabs(false); setIndentationWidth(indentationWidth); setUtf8(true); setMarginWidth(1, 0); setCaretForegroundColor(caretForegroundColor); setMatchedBraceForegroundColor(matchedBraceForegroundColor); //1AB0A6 setMatchedBraceBackgroundColor(marginsBackgroundColor); setContentsMargins(0, 0, 0, 0); setViewportMargins(3, 3, 3, 3); QFont ourFont = GuiRegistry::instance().font(); setMarginsFont(ourFont); setMarginLineNumbers(0, true); setMarginsBackgroundColor(QColor(53, 56, 58)); setMarginsForegroundColor(QColor(173, 176, 178)); SendScintilla(QsciScintilla::SCI_STYLESETFONT, 1, ourFont.family().data()); SendScintilla(QsciScintilla::SCI_SETHSCROLLBAR, 0); setWrapMode((QsciScintilla::WrapMode)QsciScintilla::SC_WRAP_NONE); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // Cache width of one digit #ifdef Q_OS_WIN _lineNumberDigitWidth = rowNumberWidth; #else _lineNumberDigitWidth = textWidth(STYLE_LINENUMBER, "0"); #endif updateLineNumbersMarginWidth(); setLineNumbers(AppRegistry::instance().settingsManager()->lineNumbers()); setUtf8(true); VERIFY(connect(this, SIGNAL(linesChanged()), this, SLOT(updateLineNumbersMarginWidth()))); } int RoboScintilla::lineNumberMarginWidth() const { return marginWidth(0); } int RoboScintilla::textWidth(int style, const QString &text) { const char *byteArray = (text.toUtf8()).constData(); return SendScintilla(SCI_TEXTWIDTH, style, byteArray); } void RoboScintilla::wheelEvent(QWheelEvent *e) { if (this->isActiveWindow()) { QsciScintilla::wheelEvent(e); } else { qApp->sendEvent(parentWidget(), e); e->accept(); } } void RoboScintilla::setLineNumbers(bool displayNumbers) { if (displayNumbers) { setMarginWidth(0, _lineNumberMarginWidth); } else { setMarginWidth(0, 0); } } void RoboScintilla::toggleLineNumbers() { setLineNumbers(!lineNumberMarginWidth()); } void RoboScintilla::keyPressEvent(QKeyEvent *keyEvent) { if (_ignoreEnterKey) { if (keyEvent->key() == Qt::Key_Return) { keyEvent->ignore(); _ignoreEnterKey = false; return; } } if (_ignoreTabKey) { if (keyEvent->key() == Qt::Key_Tab) { keyEvent->ignore(); _ignoreTabKey = false; return; } } if (keyEvent->key() == Qt::Key_F11) { keyEvent->ignore(); toggleLineNumbers(); return; } if (((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->key() == Qt::Key_F4 || keyEvent->key() == Qt::Key_W || keyEvent->key() == Qt::Key_T || keyEvent->key() == Qt::Key_Space || keyEvent->key() == Qt::Key_F || keyEvent->key() == Qt::Key_Slash)) || keyEvent->key() == Qt::Key_Escape /*|| keyEvent->key() == Qt::Key_Return*/ || ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::AltModifier) && keyEvent->key() == Qt::Key_Left) || ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::AltModifier) && keyEvent->key() == Qt::Key_Right) || ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::ShiftModifier) && keyEvent->key() == Qt::Key_C) ) { keyEvent->ignore(); } else { BaseClass::keyPressEvent(keyEvent); } } void RoboScintilla::updateLineNumbersMarginWidth() { int numberOfDigits = getNumberOfDigits(lines()); _lineNumberMarginWidth = numberOfDigits * _lineNumberDigitWidth + rowNumberWidth; // If line numbers margin already displayed, update its width if (lineNumberMarginWidth()) { setMarginWidth(0, _lineNumberMarginWidth); } } void RoboScintilla::setAppropriateBraceMatching() { #ifdef Q_OS_MAC // On Mac OS when brace matching is enabled, text // will blink when you move cursor to some brace or // when inside braces. This behaviour is not fully fixed // in QScintilla 2.9.1 and 2.8.4 setBraceMatching(QsciScintilla::NoBraceMatch); #else setBraceMatching(QsciScintilla::StrictBraceMatch); #endif } } ================================================ FILE: src/robomongo/gui/editors/PlainJavaScriptEditor.h ================================================ #pragma once #include namespace Robomongo { class RoboScintilla : public QsciScintilla { Q_OBJECT public: typedef QsciScintilla BaseClass; enum { rowNumberWidth = 6, indentationWidth = 4 }; static const QColor marginsBackgroundColor; static const QColor caretForegroundColor; static const QColor matchedBraceForegroundColor; RoboScintilla(QWidget *parent = NULL); void setIgnoreEnterKey(bool ignore) { _ignoreEnterKey = ignore; } void setIgnoreTabKey(bool ignore) { _ignoreTabKey = ignore; } int lineNumberMarginWidth() const; int textWidth(int style, const QString &text); void setAppropriateBraceMatching(); protected: void wheelEvent(QWheelEvent *e); void keyPressEvent(QKeyEvent *e); private Q_SLOTS: void updateLineNumbersMarginWidth(); private: void setLineNumbers(bool displayNumbers); void toggleLineNumbers(); bool _ignoreEnterKey; bool _ignoreTabKey; int _lineNumberMarginWidth; int _lineNumberDigitWidth; }; } ================================================ FILE: src/robomongo/gui/resources/gui.qrc ================================================ icons/collection_16x16.png icons/server_16x16.gif icons/left_16x16.png icons/right_16x16.png icons/bson_binary_16x16.png icons/bson_bool_16x16.png icons/bson_datetime_16x16.png icons/bson_double_16x16.png icons/bson_decimal128_16x16.png icons/bson_integer_16x16.png icons/bson_null_16x16.png icons/bson_string_16x16.png icons/bson_unsupported_16x16.png icons/bson_array_16x16.png icons/bson_object_16x16.png icons/database_16x16.png icons/loading.gif icons/loading_ticks_40x40.gif scripts/esprima.js scripts/uuidhelpers.js icons/maximize.gif icons/undock.png icons/dock.png icons/maximize.png icons/minimize.png icons/text_16x16.png icons/tree_16x16.png icons/rotate_16x16.png icons/close_16x16.png icons/close_hover_16x16.png icons/close_2_16x16.png icons/close_2_Mac_16x16.png icons/close_hover_16x16_original.png icons/text2_16x16.png icons/mongodb_16x16.png icons/mongodb_icon_for_MAC.png icons/connect_24x24.png icons/server_16x16.png icons/server_primary_16x16.png icons/server_secondary_16x16.png icons/server_imported_16x16.png icons/replica_set_16x16.png icons/replica_set_offline_16x16.png icons/execute_24x24.png icons/main_window_icon_old.png icons/logo-256x256.png icons/logo-20x20.png icons/handsup-20x20.png icons/text_highlighted_16x16.png icons/tree_highlighted_16x16.png icons/maximize_highlighted_16x16.png icons/table_16x16.png icons/table_highlighted_16x16.png icons/no_mark_24x24.png icons/no_mark_24x24@2x.png icons/yes_mark_24x24.png icons/yes_mark_24x24@2x.png icons/skip_mark_24x24.png icons/skip_mark_24x24@2x.png icons/question_mark_24x24.png icons/question_mark_24x24@2x.png icons/time_16x16.png icons/key_16x16.png icons/progress_bar.gif icons/custom_16x16.gif icons/custom_16x16.png icons/custom_highlighted_16x16.png icons/user_16x16.png icons/stop_24x24.png icons/function_16x16.png icons/index_16x16.png icons/hide_64x64.png icons/show_64x64.png icons/export_64x64.png icons/import_64x64.png icons/plus_sign.png icons/minus_sign.png icons/delete.png icons/delete_red_color.png icons/delete_mouse_hovered.png icons/welcome_tab_icon.png ================================================ FILE: src/robomongo/gui/resources/scripts/esprima.js ================================================ /* Copyright (c) jQuery Foundation, Inc. and Contributors, 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. 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 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. */ /* Robomongo - esprima.js version 2.7.3 added to support ES6 features. https://cdn.rawgit.com/jquery/esprima/2.7.3/esprima.js https://github.com/jquery/esprima/blob/master/ChangeLog http://esprima.org/ */ (function (root, factory) { 'use strict'; // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, // Rhino, and plain browser loading. /* istanbul ignore next */ if (typeof define === 'function' && define.amd) { define(['exports'], factory); } else if (typeof exports !== 'undefined') { factory(exports); } else { factory((root.esprima = {})); } }(this, function (exports) { 'use strict'; var Token, TokenName, FnExprTokens, Syntax, PlaceHolders, Messages, Regex, source, strict, index, lineNumber, lineStart, hasLineTerminator, lastIndex, lastLineNumber, lastLineStart, startIndex, startLineNumber, startLineStart, scanning, length, lookahead, state, extra, isBindingElement, isAssignmentTarget, firstCoverInitializedNameError; Token = { BooleanLiteral: 1, EOF: 2, Identifier: 3, Keyword: 4, NullLiteral: 5, NumericLiteral: 6, Punctuator: 7, StringLiteral: 8, RegularExpression: 9, Template: 10 }; TokenName = {}; TokenName[Token.BooleanLiteral] = 'Boolean'; TokenName[Token.EOF] = ''; TokenName[Token.Identifier] = 'Identifier'; TokenName[Token.Keyword] = 'Keyword'; TokenName[Token.NullLiteral] = 'Null'; TokenName[Token.NumericLiteral] = 'Numeric'; TokenName[Token.Punctuator] = 'Punctuator'; TokenName[Token.StringLiteral] = 'String'; TokenName[Token.RegularExpression] = 'RegularExpression'; TokenName[Token.Template] = 'Template'; // A function following one of those tokens is an expression. FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new', 'return', 'case', 'delete', 'throw', 'void', // assignment operators '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=', '&=', '|=', '^=', ',', // binary/unary operators '+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&', '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=', '<=', '<', '>', '!=', '!==']; Syntax = { AssignmentExpression: 'AssignmentExpression', AssignmentPattern: 'AssignmentPattern', ArrayExpression: 'ArrayExpression', ArrayPattern: 'ArrayPattern', ArrowFunctionExpression: 'ArrowFunctionExpression', BlockStatement: 'BlockStatement', BinaryExpression: 'BinaryExpression', BreakStatement: 'BreakStatement', CallExpression: 'CallExpression', CatchClause: 'CatchClause', ClassBody: 'ClassBody', ClassDeclaration: 'ClassDeclaration', ClassExpression: 'ClassExpression', ConditionalExpression: 'ConditionalExpression', ContinueStatement: 'ContinueStatement', DoWhileStatement: 'DoWhileStatement', DebuggerStatement: 'DebuggerStatement', EmptyStatement: 'EmptyStatement', ExportAllDeclaration: 'ExportAllDeclaration', ExportDefaultDeclaration: 'ExportDefaultDeclaration', ExportNamedDeclaration: 'ExportNamedDeclaration', ExportSpecifier: 'ExportSpecifier', ExpressionStatement: 'ExpressionStatement', ForStatement: 'ForStatement', ForOfStatement: 'ForOfStatement', ForInStatement: 'ForInStatement', FunctionDeclaration: 'FunctionDeclaration', FunctionExpression: 'FunctionExpression', Identifier: 'Identifier', IfStatement: 'IfStatement', ImportDeclaration: 'ImportDeclaration', ImportDefaultSpecifier: 'ImportDefaultSpecifier', ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', ImportSpecifier: 'ImportSpecifier', Literal: 'Literal', LabeledStatement: 'LabeledStatement', LogicalExpression: 'LogicalExpression', MemberExpression: 'MemberExpression', MetaProperty: 'MetaProperty', MethodDefinition: 'MethodDefinition', NewExpression: 'NewExpression', ObjectExpression: 'ObjectExpression', ObjectPattern: 'ObjectPattern', Program: 'Program', Property: 'Property', RestElement: 'RestElement', ReturnStatement: 'ReturnStatement', SequenceExpression: 'SequenceExpression', SpreadElement: 'SpreadElement', Super: 'Super', SwitchCase: 'SwitchCase', SwitchStatement: 'SwitchStatement', TaggedTemplateExpression: 'TaggedTemplateExpression', TemplateElement: 'TemplateElement', TemplateLiteral: 'TemplateLiteral', ThisExpression: 'ThisExpression', ThrowStatement: 'ThrowStatement', TryStatement: 'TryStatement', UnaryExpression: 'UnaryExpression', UpdateExpression: 'UpdateExpression', VariableDeclaration: 'VariableDeclaration', VariableDeclarator: 'VariableDeclarator', WhileStatement: 'WhileStatement', WithStatement: 'WithStatement', YieldExpression: 'YieldExpression' }; PlaceHolders = { ArrowParameterPlaceHolder: 'ArrowParameterPlaceHolder' }; // Error messages should be identical to V8. Messages = { UnexpectedToken: 'Unexpected token %0', UnexpectedNumber: 'Unexpected number', UnexpectedString: 'Unexpected string', UnexpectedIdentifier: 'Unexpected identifier', UnexpectedReserved: 'Unexpected reserved word', UnexpectedTemplate: 'Unexpected quasi %0', UnexpectedEOS: 'Unexpected end of input', NewlineAfterThrow: 'Illegal newline after throw', InvalidRegExp: 'Invalid regular expression', UnterminatedRegExp: 'Invalid regular expression: missing /', InvalidLHSInAssignment: 'Invalid left-hand side in assignment', InvalidLHSInForIn: 'Invalid left-hand side in for-in', InvalidLHSInForLoop: 'Invalid left-hand side in for-loop', MultipleDefaultsInSwitch: 'More than one default clause in switch statement', NoCatchOrFinally: 'Missing catch or finally after try', UnknownLabel: 'Undefined label \'%0\'', Redeclaration: '%0 \'%1\' has already been declared', IllegalContinue: 'Illegal continue statement', IllegalBreak: 'Illegal break statement', IllegalReturn: 'Illegal return statement', StrictModeWith: 'Strict mode code may not include a with statement', StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', StrictVarName: 'Variable name may not be eval or arguments in strict mode', StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', StrictParamDupe: 'Strict mode function may not have duplicate parameter names', StrictFunctionName: 'Function name may not be eval or arguments in strict mode', StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', StrictDelete: 'Delete of an unqualified identifier in strict mode.', StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', StrictReservedWord: 'Use of future reserved word in strict mode', TemplateOctalLiteral: 'Octal literals are not allowed in template strings.', ParameterAfterRestParameter: 'Rest parameter must be last formal parameter', DefaultRestParameter: 'Unexpected token =', ObjectPatternAsRestParameter: 'Unexpected token {', DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals', ConstructorSpecialMethod: 'Class constructor may not be an accessor', DuplicateConstructor: 'A class may only have one constructor', StaticPrototype: 'Classes may not have static property named prototype', MissingFromClause: 'Unexpected token', NoAsAfterImportNamespace: 'Unexpected token', InvalidModuleSpecifier: 'Unexpected token', IllegalImportDeclaration: 'Unexpected token', IllegalExportDeclaration: 'Unexpected token', DuplicateBinding: 'Duplicate binding %0' }; // See also tools/generate-unicode-regex.js. Regex = { // ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierStart: NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B2\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDE00-\uDE11\uDE13-\uDE2B\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDE00-\uDE2F\uDE44\uDE80-\uDEAA]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|\uD809[\uDC00-\uDC6E]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]/, // ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierPart: NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B2\u08E4-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA69D\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2D\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDD0-\uDDDA\uDE00-\uDE11\uDE13-\uDE37\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF01-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|\uD809[\uDC00-\uDC6E]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/ }; // Ensure the condition is true, otherwise throw an error. // This is only to have a better contract semantic, i.e. another safety net // to catch a logic error. The condition shall be fulfilled in normal case. // Do NOT use this to enforce a certain condition on any user input. function assert(condition, message) { /* istanbul ignore if */ if (!condition) { throw new Error('ASSERT: ' + message); } } function isDecimalDigit(ch) { return (ch >= 0x30 && ch <= 0x39); // 0..9 } function isHexDigit(ch) { return '0123456789abcdefABCDEF'.indexOf(ch) >= 0; } function isOctalDigit(ch) { return '01234567'.indexOf(ch) >= 0; } function octalToDecimal(ch) { // \0 is not octal escape sequence var octal = (ch !== '0'), code = '01234567'.indexOf(ch); if (index < length && isOctalDigit(source[index])) { octal = true; code = code * 8 + '01234567'.indexOf(source[index++]); // 3 digits are only allowed when string starts // with 0, 1, 2, 3 if ('0123'.indexOf(ch) >= 0 && index < length && isOctalDigit(source[index])) { code = code * 8 + '01234567'.indexOf(source[index++]); } } return { code: code, octal: octal }; } // ECMA-262 11.2 White Space function isWhiteSpace(ch) { return (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) || (ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(ch) >= 0); } // ECMA-262 11.3 Line Terminators function isLineTerminator(ch) { return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029); } // ECMA-262 11.6 Identifier Names and Identifiers function fromCodePoint(cp) { return (cp < 0x10000) ? String.fromCharCode(cp) : String.fromCharCode(0xD800 + ((cp - 0x10000) >> 10)) + String.fromCharCode(0xDC00 + ((cp - 0x10000) & 1023)); } function isIdentifierStart(ch) { return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore) (ch >= 0x41 && ch <= 0x5A) || // A..Z (ch >= 0x61 && ch <= 0x7A) || // a..z (ch === 0x5C) || // \ (backslash) ((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch))); } function isIdentifierPart(ch) { return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore) (ch >= 0x41 && ch <= 0x5A) || // A..Z (ch >= 0x61 && ch <= 0x7A) || // a..z (ch >= 0x30 && ch <= 0x39) || // 0..9 (ch === 0x5C) || // \ (backslash) ((ch >= 0x80) && Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch))); } // ECMA-262 11.6.2.2 Future Reserved Words function isFutureReservedWord(id) { switch (id) { case 'enum': case 'export': case 'import': case 'super': return true; default: return false; } } function isStrictModeReservedWord(id) { switch (id) { case 'implements': case 'interface': case 'package': case 'private': case 'protected': case 'public': case 'static': case 'yield': case 'let': return true; default: return false; } } function isRestrictedWord(id) { return id === 'eval' || id === 'arguments'; } // ECMA-262 11.6.2.1 Keywords function isKeyword(id) { switch (id.length) { case 2: return (id === 'if') || (id === 'in') || (id === 'do'); case 3: return (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try') || (id === 'let'); case 4: return (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with') || (id === 'enum'); case 5: return (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw') || (id === 'const') || (id === 'yield') || (id === 'class') || (id === 'super'); case 6: return (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch') || (id === 'export') || (id === 'import'); case 7: return (id === 'default') || (id === 'finally') || (id === 'extends'); case 8: return (id === 'function') || (id === 'continue') || (id === 'debugger'); case 10: return (id === 'instanceof'); default: return false; } } // ECMA-262 11.4 Comments function addComment(type, value, start, end, loc) { var comment; assert(typeof start === 'number', 'Comment must have valid position'); state.lastCommentStart = start; comment = { type: type, value: value }; if (extra.range) { comment.range = [start, end]; } if (extra.loc) { comment.loc = loc; } extra.comments.push(comment); if (extra.attachComment) { extra.leadingComments.push(comment); extra.trailingComments.push(comment); } if (extra.tokenize) { comment.type = comment.type + 'Comment'; if (extra.delegate) { comment = extra.delegate(comment); } extra.tokens.push(comment); } } function skipSingleLineComment(offset) { var start, loc, ch, comment; start = index - offset; loc = { start: { line: lineNumber, column: index - lineStart - offset } }; while (index < length) { ch = source.charCodeAt(index); ++index; if (isLineTerminator(ch)) { hasLineTerminator = true; if (extra.comments) { comment = source.slice(start + offset, index - 1); loc.end = { line: lineNumber, column: index - lineStart - 1 }; addComment('Line', comment, start, index - 1, loc); } if (ch === 13 && source.charCodeAt(index) === 10) { ++index; } ++lineNumber; lineStart = index; return; } } if (extra.comments) { comment = source.slice(start + offset, index); loc.end = { line: lineNumber, column: index - lineStart }; addComment('Line', comment, start, index, loc); } } function skipMultiLineComment() { var start, loc, ch, comment; if (extra.comments) { start = index - 2; loc = { start: { line: lineNumber, column: index - lineStart - 2 } }; } while (index < length) { ch = source.charCodeAt(index); if (isLineTerminator(ch)) { if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) { ++index; } hasLineTerminator = true; ++lineNumber; ++index; lineStart = index; } else if (ch === 0x2A) { // Block comment ends with '*/'. if (source.charCodeAt(index + 1) === 0x2F) { ++index; ++index; if (extra.comments) { comment = source.slice(start + 2, index - 2); loc.end = { line: lineNumber, column: index - lineStart }; addComment('Block', comment, start, index, loc); } return; } ++index; } else { ++index; } } // Ran off the end of the file - the whole thing is a comment if (extra.comments) { loc.end = { line: lineNumber, column: index - lineStart }; comment = source.slice(start + 2, index); addComment('Block', comment, start, index, loc); } tolerateUnexpectedToken(); } function skipComment() { var ch, start; hasLineTerminator = false; start = (index === 0); while (index < length) { ch = source.charCodeAt(index); if (isWhiteSpace(ch)) { ++index; } else if (isLineTerminator(ch)) { hasLineTerminator = true; ++index; if (ch === 0x0D && source.charCodeAt(index) === 0x0A) { ++index; } ++lineNumber; lineStart = index; start = true; } else if (ch === 0x2F) { // U+002F is '/' ch = source.charCodeAt(index + 1); if (ch === 0x2F) { ++index; ++index; skipSingleLineComment(2); start = true; } else if (ch === 0x2A) { // U+002A is '*' ++index; ++index; skipMultiLineComment(); } else { break; } } else if (start && ch === 0x2D) { // U+002D is '-' // U+003E is '>' if ((source.charCodeAt(index + 1) === 0x2D) && (source.charCodeAt(index + 2) === 0x3E)) { // '-->' is a single-line comment index += 3; skipSingleLineComment(3); } else { break; } } else if (ch === 0x3C) { // U+003C is '<' if (source.slice(index + 1, index + 4) === '!--') { ++index; // `<` ++index; // `!` ++index; // `-` ++index; // `-` skipSingleLineComment(4); } else { break; } } else { break; } } } function scanHexEscape(prefix) { var i, len, ch, code = 0; len = (prefix === 'u') ? 4 : 2; for (i = 0; i < len; ++i) { if (index < length && isHexDigit(source[index])) { ch = source[index++]; code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); } else { return ''; } } return String.fromCharCode(code); } function scanUnicodeCodePointEscape() { var ch, code; ch = source[index]; code = 0; // At least, one hex digit is required. if (ch === '}') { throwUnexpectedToken(); } while (index < length) { ch = source[index++]; if (!isHexDigit(ch)) { break; } code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); } if (code > 0x10FFFF || ch !== '}') { throwUnexpectedToken(); } return fromCodePoint(code); } function codePointAt(i) { var cp, first, second; cp = source.charCodeAt(i); if (cp >= 0xD800 && cp <= 0xDBFF) { second = source.charCodeAt(i + 1); if (second >= 0xDC00 && second <= 0xDFFF) { first = cp; cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; } } return cp; } function getComplexIdentifier() { var cp, ch, id; cp = codePointAt(index); id = fromCodePoint(cp); index += id.length; // '\u' (U+005C, U+0075) denotes an escaped character. if (cp === 0x5C) { if (source.charCodeAt(index) !== 0x75) { throwUnexpectedToken(); } ++index; if (source[index] === '{') { ++index; ch = scanUnicodeCodePointEscape(); } else { ch = scanHexEscape('u'); cp = ch.charCodeAt(0); if (!ch || ch === '\\' || !isIdentifierStart(cp)) { throwUnexpectedToken(); } } id = ch; } while (index < length) { cp = codePointAt(index); if (!isIdentifierPart(cp)) { break; } ch = fromCodePoint(cp); id += ch; index += ch.length; // '\u' (U+005C, U+0075) denotes an escaped character. if (cp === 0x5C) { id = id.substr(0, id.length - 1); if (source.charCodeAt(index) !== 0x75) { throwUnexpectedToken(); } ++index; if (source[index] === '{') { ++index; ch = scanUnicodeCodePointEscape(); } else { ch = scanHexEscape('u'); cp = ch.charCodeAt(0); if (!ch || ch === '\\' || !isIdentifierPart(cp)) { throwUnexpectedToken(); } } id += ch; } } return id; } function getIdentifier() { var start, ch; start = index++; while (index < length) { ch = source.charCodeAt(index); if (ch === 0x5C) { // Blackslash (U+005C) marks Unicode escape sequence. index = start; return getComplexIdentifier(); } else if (ch >= 0xD800 && ch < 0xDFFF) { // Need to handle surrogate pairs. index = start; return getComplexIdentifier(); } if (isIdentifierPart(ch)) { ++index; } else { break; } } return source.slice(start, index); } function scanIdentifier() { var start, id, type; start = index; // Backslash (U+005C) starts an escaped character. id = (source.charCodeAt(index) === 0x5C) ? getComplexIdentifier() : getIdentifier(); // There is no keyword or literal with only one character. // Thus, it must be an identifier. if (id.length === 1) { type = Token.Identifier; } else if (isKeyword(id)) { type = Token.Keyword; } else if (id === 'null') { type = Token.NullLiteral; } else if (id === 'true' || id === 'false') { type = Token.BooleanLiteral; } else { type = Token.Identifier; } return { type: type, value: id, lineNumber: lineNumber, lineStart: lineStart, start: start, end: index }; } // ECMA-262 11.7 Punctuators function scanPunctuator() { var token, str; token = { type: Token.Punctuator, value: '', lineNumber: lineNumber, lineStart: lineStart, start: index, end: index }; // Check for most common single-character punctuators. str = source[index]; switch (str) { case '(': if (extra.tokenize) { extra.openParenToken = extra.tokenValues.length; } ++index; break; case '{': if (extra.tokenize) { extra.openCurlyToken = extra.tokenValues.length; } state.curlyStack.push('{'); ++index; break; case '.': ++index; if (source[index] === '.' && source[index + 1] === '.') { // Spread operator: ... index += 2; str = '...'; } break; case '}': ++index; state.curlyStack.pop(); break; case ')': case ';': case ',': case '[': case ']': case ':': case '?': case '~': ++index; break; default: // 4-character punctuator. str = source.substr(index, 4); if (str === '>>>=') { index += 4; } else { // 3-character punctuators. str = str.substr(0, 3); if (str === '===' || str === '!==' || str === '>>>' || str === '<<=' || str === '>>=') { index += 3; } else { // 2-character punctuators. str = str.substr(0, 2); if (str === '&&' || str === '||' || str === '==' || str === '!=' || str === '+=' || str === '-=' || str === '*=' || str === '/=' || str === '++' || str === '--' || str === '<<' || str === '>>' || str === '&=' || str === '|=' || str === '^=' || str === '%=' || str === '<=' || str === '>=' || str === '=>') { index += 2; } else { // 1-character punctuators. str = source[index]; if ('<>=!+-*%&|^/'.indexOf(str) >= 0) { ++index; } } } } } if (index === token.start) { throwUnexpectedToken(); } token.end = index; token.value = str; return token; } // ECMA-262 11.8.3 Numeric Literals function scanHexLiteral(start) { var number = ''; while (index < length) { if (!isHexDigit(source[index])) { break; } number += source[index++]; } if (number.length === 0) { throwUnexpectedToken(); } if (isIdentifierStart(source.charCodeAt(index))) { throwUnexpectedToken(); } return { type: Token.NumericLiteral, value: parseInt('0x' + number, 16), lineNumber: lineNumber, lineStart: lineStart, start: start, end: index }; } function scanBinaryLiteral(start) { var ch, number; number = ''; while (index < length) { ch = source[index]; if (ch !== '0' && ch !== '1') { break; } number += source[index++]; } if (number.length === 0) { // only 0b or 0B throwUnexpectedToken(); } if (index < length) { ch = source.charCodeAt(index); /* istanbul ignore else */ if (isIdentifierStart(ch) || isDecimalDigit(ch)) { throwUnexpectedToken(); } } return { type: Token.NumericLiteral, value: parseInt(number, 2), lineNumber: lineNumber, lineStart: lineStart, start: start, end: index }; } function scanOctalLiteral(prefix, start) { var number, octal; if (isOctalDigit(prefix)) { octal = true; number = '0' + source[index++]; } else { octal = false; ++index; number = ''; } while (index < length) { if (!isOctalDigit(source[index])) { break; } number += source[index++]; } if (!octal && number.length === 0) { // only 0o or 0O throwUnexpectedToken(); } if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) { throwUnexpectedToken(); } return { type: Token.NumericLiteral, value: parseInt(number, 8), octal: octal, lineNumber: lineNumber, lineStart: lineStart, start: start, end: index }; } function isImplicitOctalLiteral() { var i, ch; // Implicit octal, unless there is a non-octal digit. // (Annex B.1.1 on Numeric Literals) for (i = index + 1; i < length; ++i) { ch = source[i]; if (ch === '8' || ch === '9') { return false; } if (!isOctalDigit(ch)) { return true; } } return true; } function scanNumericLiteral() { var number, start, ch; ch = source[index]; assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), 'Numeric literal must start with a decimal digit or a decimal point'); start = index; number = ''; if (ch !== '.') { number = source[index++]; ch = source[index]; // Hex number starts with '0x'. // Octal number starts with '0'. // Octal number in ES6 starts with '0o'. // Binary number in ES6 starts with '0b'. if (number === '0') { if (ch === 'x' || ch === 'X') { ++index; return scanHexLiteral(start); } if (ch === 'b' || ch === 'B') { ++index; return scanBinaryLiteral(start); } if (ch === 'o' || ch === 'O') { return scanOctalLiteral(ch, start); } if (isOctalDigit(ch)) { if (isImplicitOctalLiteral()) { return scanOctalLiteral(ch, start); } } } while (isDecimalDigit(source.charCodeAt(index))) { number += source[index++]; } ch = source[index]; } if (ch === '.') { number += source[index++]; while (isDecimalDigit(source.charCodeAt(index))) { number += source[index++]; } ch = source[index]; } if (ch === 'e' || ch === 'E') { number += source[index++]; ch = source[index]; if (ch === '+' || ch === '-') { number += source[index++]; } if (isDecimalDigit(source.charCodeAt(index))) { while (isDecimalDigit(source.charCodeAt(index))) { number += source[index++]; } } else { throwUnexpectedToken(); } } if (isIdentifierStart(source.charCodeAt(index))) { throwUnexpectedToken(); } return { type: Token.NumericLiteral, value: parseFloat(number), lineNumber: lineNumber, lineStart: lineStart, start: start, end: index }; } // ECMA-262 11.8.4 String Literals function scanStringLiteral() { var str = '', quote, start, ch, unescaped, octToDec, octal = false; quote = source[index]; assert((quote === '\'' || quote === '"'), 'String literal must starts with a quote'); start = index; ++index; while (index < length) { ch = source[index++]; if (ch === quote) { quote = ''; break; } else if (ch === '\\') { ch = source[index++]; if (!ch || !isLineTerminator(ch.charCodeAt(0))) { switch (ch) { case 'u': case 'x': if (source[index] === '{') { ++index; str += scanUnicodeCodePointEscape(); } else { unescaped = scanHexEscape(ch); if (!unescaped) { throw throwUnexpectedToken(); } str += unescaped; } break; case 'n': str += '\n'; break; case 'r': str += '\r'; break; case 't': str += '\t'; break; case 'b': str += '\b'; break; case 'f': str += '\f'; break; case 'v': str += '\x0B'; break; case '8': case '9': str += ch; tolerateUnexpectedToken(); break; default: if (isOctalDigit(ch)) { octToDec = octalToDecimal(ch); octal = octToDec.octal || octal; str += String.fromCharCode(octToDec.code); } else { str += ch; } break; } } else { ++lineNumber; if (ch === '\r' && source[index] === '\n') { ++index; } lineStart = index; } } else if (isLineTerminator(ch.charCodeAt(0))) { break; } else { str += ch; } } if (quote !== '') { index = start; throwUnexpectedToken(); } return { type: Token.StringLiteral, value: str, octal: octal, lineNumber: startLineNumber, lineStart: startLineStart, start: start, end: index }; } // ECMA-262 11.8.6 Template Literal Lexical Components function scanTemplate() { var cooked = '', ch, start, rawOffset, terminated, head, tail, restore, unescaped; terminated = false; tail = false; start = index; head = (source[index] === '`'); rawOffset = 2; ++index; while (index < length) { ch = source[index++]; if (ch === '`') { rawOffset = 1; tail = true; terminated = true; break; } else if (ch === '$') { if (source[index] === '{') { state.curlyStack.push('${'); ++index; terminated = true; break; } cooked += ch; } else if (ch === '\\') { ch = source[index++]; if (!isLineTerminator(ch.charCodeAt(0))) { switch (ch) { case 'n': cooked += '\n'; break; case 'r': cooked += '\r'; break; case 't': cooked += '\t'; break; case 'u': case 'x': if (source[index] === '{') { ++index; cooked += scanUnicodeCodePointEscape(); } else { restore = index; unescaped = scanHexEscape(ch); if (unescaped) { cooked += unescaped; } else { index = restore; cooked += ch; } } break; case 'b': cooked += '\b'; break; case 'f': cooked += '\f'; break; case 'v': cooked += '\v'; break; default: if (ch === '0') { if (isDecimalDigit(source.charCodeAt(index))) { // Illegal: \01 \02 and so on throwError(Messages.TemplateOctalLiteral); } cooked += '\0'; } else if (isOctalDigit(ch)) { // Illegal: \1 \2 throwError(Messages.TemplateOctalLiteral); } else { cooked += ch; } break; } } else { ++lineNumber; if (ch === '\r' && source[index] === '\n') { ++index; } lineStart = index; } } else if (isLineTerminator(ch.charCodeAt(0))) { ++lineNumber; if (ch === '\r' && source[index] === '\n') { ++index; } lineStart = index; cooked += '\n'; } else { cooked += ch; } } if (!terminated) { throwUnexpectedToken(); } if (!head) { state.curlyStack.pop(); } return { type: Token.Template, value: { cooked: cooked, raw: source.slice(start + 1, index - rawOffset) }, head: head, tail: tail, lineNumber: lineNumber, lineStart: lineStart, start: start, end: index }; } // ECMA-262 11.8.5 Regular Expression Literals function testRegExp(pattern, flags) { // The BMP character to use as a replacement for astral symbols when // translating an ES6 "u"-flagged pattern to an ES5-compatible // approximation. // Note: replacing with '\uFFFF' enables false positives in unlikely // scenarios. For example, `[\u{1044f}-\u{10440}]` is an invalid // pattern that would not be detected by this substitution. var astralSubstitute = '\uFFFF', tmp = pattern; if (flags.indexOf('u') >= 0) { tmp = tmp // Replace every Unicode escape sequence with the equivalent // BMP character or a constant ASCII code point in the case of // astral symbols. (See the above note on `astralSubstitute` // for more information.) .replace(/\\u\{([0-9a-fA-F]+)\}|\\u([a-fA-F0-9]{4})/g, function ($0, $1, $2) { var codePoint = parseInt($1 || $2, 16); if (codePoint > 0x10FFFF) { throwUnexpectedToken(null, Messages.InvalidRegExp); } if (codePoint <= 0xFFFF) { return String.fromCharCode(codePoint); } return astralSubstitute; }) // Replace each paired surrogate with a single ASCII symbol to // avoid throwing on regular expressions that are only valid in // combination with the "u" flag. .replace( /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, astralSubstitute ); } // First, detect invalid regular expressions. try { RegExp(tmp); } catch (e) { throwUnexpectedToken(null, Messages.InvalidRegExp); } // Return a regular expression object for this pattern-flag pair, or // `null` in case the current environment doesn't support the flags it // uses. try { return new RegExp(pattern, flags); } catch (exception) { /* istanbul ignore next */ return null; } } function scanRegExpBody() { var ch, str, classMarker, terminated, body; ch = source[index]; assert(ch === '/', 'Regular expression literal must start with a slash'); str = source[index++]; classMarker = false; terminated = false; while (index < length) { ch = source[index++]; str += ch; if (ch === '\\') { ch = source[index++]; // ECMA-262 7.8.5 if (isLineTerminator(ch.charCodeAt(0))) { throwUnexpectedToken(null, Messages.UnterminatedRegExp); } str += ch; } else if (isLineTerminator(ch.charCodeAt(0))) { throwUnexpectedToken(null, Messages.UnterminatedRegExp); } else if (classMarker) { if (ch === ']') { classMarker = false; } } else { if (ch === '/') { terminated = true; break; } else if (ch === '[') { classMarker = true; } } } if (!terminated) { throwUnexpectedToken(null, Messages.UnterminatedRegExp); } // Exclude leading and trailing slash. body = str.substr(1, str.length - 2); return { value: body, literal: str }; } function scanRegExpFlags() { var ch, str, flags, restore; str = ''; flags = ''; while (index < length) { ch = source[index]; if (!isIdentifierPart(ch.charCodeAt(0))) { break; } ++index; if (ch === '\\' && index < length) { ch = source[index]; if (ch === 'u') { ++index; restore = index; ch = scanHexEscape('u'); if (ch) { flags += ch; for (str += '\\u'; restore < index; ++restore) { str += source[restore]; } } else { index = restore; flags += 'u'; str += '\\u'; } tolerateUnexpectedToken(); } else { str += '\\'; tolerateUnexpectedToken(); } } else { flags += ch; str += ch; } } return { value: flags, literal: str }; } function scanRegExp() { var start, body, flags, value; scanning = true; lookahead = null; skipComment(); start = index; body = scanRegExpBody(); flags = scanRegExpFlags(); value = testRegExp(body.value, flags.value); scanning = false; if (extra.tokenize) { return { type: Token.RegularExpression, value: value, regex: { pattern: body.value, flags: flags.value }, lineNumber: lineNumber, lineStart: lineStart, start: start, end: index }; } return { literal: body.literal + flags.literal, value: value, regex: { pattern: body.value, flags: flags.value }, start: start, end: index }; } function collectRegex() { var pos, loc, regex, token; skipComment(); pos = index; loc = { start: { line: lineNumber, column: index - lineStart } }; regex = scanRegExp(); loc.end = { line: lineNumber, column: index - lineStart }; /* istanbul ignore next */ if (!extra.tokenize) { // Pop the previous token, which is likely '/' or '/=' if (extra.tokens.length > 0) { token = extra.tokens[extra.tokens.length - 1]; if (token.range[0] === pos && token.type === 'Punctuator') { if (token.value === '/' || token.value === '/=') { extra.tokens.pop(); } } } extra.tokens.push({ type: 'RegularExpression', value: regex.literal, regex: regex.regex, range: [pos, index], loc: loc }); } return regex; } function isIdentifierName(token) { return token.type === Token.Identifier || token.type === Token.Keyword || token.type === Token.BooleanLiteral || token.type === Token.NullLiteral; } // Using the following algorithm: // https://github.com/mozilla/sweet.js/wiki/design function advanceSlash() { var regex, previous, check; function testKeyword(value) { return value && (value.length > 1) && (value[0] >= 'a') && (value[0] <= 'z'); } previous = extra.tokenValues[extra.tokenValues.length - 1]; regex = (previous !== null); switch (previous) { case 'this': case ']': regex = false; break; case ')': check = extra.tokenValues[extra.openParenToken - 1]; regex = (check === 'if' || check === 'while' || check === 'for' || check === 'with'); break; case '}': // Dividing a function by anything makes little sense, // but we have to check for that. regex = false; if (testKeyword(extra.tokenValues[extra.openCurlyToken - 3])) { // Anonymous function, e.g. function(){} /42 check = extra.tokenValues[extra.openCurlyToken - 4]; regex = check ? (FnExprTokens.indexOf(check) < 0) : false; } else if (testKeyword(extra.tokenValues[extra.openCurlyToken - 4])) { // Named function, e.g. function f(){} /42/ check = extra.tokenValues[extra.openCurlyToken - 5]; regex = check ? (FnExprTokens.indexOf(check) < 0) : true; } } return regex ? collectRegex() : scanPunctuator(); } function advance() { var cp, token; if (index >= length) { return { type: Token.EOF, lineNumber: lineNumber, lineStart: lineStart, start: index, end: index }; } cp = source.charCodeAt(index); if (isIdentifierStart(cp)) { token = scanIdentifier(); if (strict && isStrictModeReservedWord(token.value)) { token.type = Token.Keyword; } return token; } // Very common: ( and ) and ; if (cp === 0x28 || cp === 0x29 || cp === 0x3B) { return scanPunctuator(); } // String literal starts with single quote (U+0027) or double quote (U+0022). if (cp === 0x27 || cp === 0x22) { return scanStringLiteral(); } // Dot (.) U+002E can also start a floating-point number, hence the need // to check the next character. if (cp === 0x2E) { if (isDecimalDigit(source.charCodeAt(index + 1))) { return scanNumericLiteral(); } return scanPunctuator(); } if (isDecimalDigit(cp)) { return scanNumericLiteral(); } // Slash (/) U+002F can also start a regex. if (extra.tokenize && cp === 0x2F) { return advanceSlash(); } // Template literals start with ` (U+0060) for template head // or } (U+007D) for template middle or template tail. if (cp === 0x60 || (cp === 0x7D && state.curlyStack[state.curlyStack.length - 1] === '${')) { return scanTemplate(); } // Possible identifier start in a surrogate pair. if (cp >= 0xD800 && cp < 0xDFFF) { cp = codePointAt(index); if (isIdentifierStart(cp)) { return scanIdentifier(); } } return scanPunctuator(); } function collectToken() { var loc, token, value, entry; loc = { start: { line: lineNumber, column: index - lineStart } }; token = advance(); loc.end = { line: lineNumber, column: index - lineStart }; if (token.type !== Token.EOF) { value = source.slice(token.start, token.end); entry = { type: TokenName[token.type], value: value, range: [token.start, token.end], loc: loc }; if (token.regex) { entry.regex = { pattern: token.regex.pattern, flags: token.regex.flags }; } if (extra.tokenValues) { extra.tokenValues.push((entry.type === 'Punctuator' || entry.type === 'Keyword') ? entry.value : null); } if (extra.tokenize) { if (!extra.range) { delete entry.range; } if (!extra.loc) { delete entry.loc; } if (extra.delegate) { entry = extra.delegate(entry); } } extra.tokens.push(entry); } return token; } function lex() { var token; scanning = true; lastIndex = index; lastLineNumber = lineNumber; lastLineStart = lineStart; skipComment(); token = lookahead; startIndex = index; startLineNumber = lineNumber; startLineStart = lineStart; lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance(); scanning = false; return token; } function peek() { scanning = true; skipComment(); lastIndex = index; lastLineNumber = lineNumber; lastLineStart = lineStart; startIndex = index; startLineNumber = lineNumber; startLineStart = lineStart; lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance(); scanning = false; } function Position() { this.line = startLineNumber; this.column = startIndex - startLineStart; } function SourceLocation() { this.start = new Position(); this.end = null; } function WrappingSourceLocation(startToken) { this.start = { line: startToken.lineNumber, column: startToken.start - startToken.lineStart }; this.end = null; } function Node() { if (extra.range) { this.range = [startIndex, 0]; } if (extra.loc) { this.loc = new SourceLocation(); } } function WrappingNode(startToken) { if (extra.range) { this.range = [startToken.start, 0]; } if (extra.loc) { this.loc = new WrappingSourceLocation(startToken); } } WrappingNode.prototype = Node.prototype = { processComment: function () { var lastChild, innerComments, leadingComments, trailingComments, bottomRight = extra.bottomRightStack, i, comment, last = bottomRight[bottomRight.length - 1]; if (this.type === Syntax.Program) { if (this.body.length > 0) { return; } } /** * patch innnerComments for properties empty block * `function a() {/** comments **\/}` */ if (this.type === Syntax.BlockStatement && this.body.length === 0) { innerComments = []; for (i = extra.leadingComments.length - 1; i >= 0; --i) { comment = extra.leadingComments[i]; if (this.range[1] >= comment.range[1]) { innerComments.unshift(comment); extra.leadingComments.splice(i, 1); extra.trailingComments.splice(i, 1); } } if (innerComments.length) { this.innerComments = innerComments; //bottomRight.push(this); return; } } if (extra.trailingComments.length > 0) { trailingComments = []; for (i = extra.trailingComments.length - 1; i >= 0; --i) { comment = extra.trailingComments[i]; if (comment.range[0] >= this.range[1]) { trailingComments.unshift(comment); extra.trailingComments.splice(i, 1); } } extra.trailingComments = []; } else { if (last && last.trailingComments && last.trailingComments[0].range[0] >= this.range[1]) { trailingComments = last.trailingComments; delete last.trailingComments; } } // Eating the stack. while (last && last.range[0] >= this.range[0]) { lastChild = bottomRight.pop(); last = bottomRight[bottomRight.length - 1]; } if (lastChild) { if (lastChild.leadingComments) { leadingComments = []; for (i = lastChild.leadingComments.length - 1; i >= 0; --i) { comment = lastChild.leadingComments[i]; if (comment.range[1] <= this.range[0]) { leadingComments.unshift(comment); lastChild.leadingComments.splice(i, 1); } } if (!lastChild.leadingComments.length) { lastChild.leadingComments = undefined; } } } else if (extra.leadingComments.length > 0) { leadingComments = []; for (i = extra.leadingComments.length - 1; i >= 0; --i) { comment = extra.leadingComments[i]; if (comment.range[1] <= this.range[0]) { leadingComments.unshift(comment); extra.leadingComments.splice(i, 1); } } } if (leadingComments && leadingComments.length > 0) { this.leadingComments = leadingComments; } if (trailingComments && trailingComments.length > 0) { this.trailingComments = trailingComments; } bottomRight.push(this); }, finish: function () { if (extra.range) { this.range[1] = lastIndex; } if (extra.loc) { this.loc.end = { line: lastLineNumber, column: lastIndex - lastLineStart }; if (extra.source) { this.loc.source = extra.source; } } if (extra.attachComment) { this.processComment(); } }, finishArrayExpression: function (elements) { this.type = Syntax.ArrayExpression; this.elements = elements; this.finish(); return this; }, finishArrayPattern: function (elements) { this.type = Syntax.ArrayPattern; this.elements = elements; this.finish(); return this; }, finishArrowFunctionExpression: function (params, defaults, body, expression) { this.type = Syntax.ArrowFunctionExpression; this.id = null; this.params = params; this.defaults = defaults; this.body = body; this.generator = false; this.expression = expression; this.finish(); return this; }, finishAssignmentExpression: function (operator, left, right) { this.type = Syntax.AssignmentExpression; this.operator = operator; this.left = left; this.right = right; this.finish(); return this; }, finishAssignmentPattern: function (left, right) { this.type = Syntax.AssignmentPattern; this.left = left; this.right = right; this.finish(); return this; }, finishBinaryExpression: function (operator, left, right) { this.type = (operator === '||' || operator === '&&') ? Syntax.LogicalExpression : Syntax.BinaryExpression; this.operator = operator; this.left = left; this.right = right; this.finish(); return this; }, finishBlockStatement: function (body) { this.type = Syntax.BlockStatement; this.body = body; this.finish(); return this; }, finishBreakStatement: function (label) { this.type = Syntax.BreakStatement; this.label = label; this.finish(); return this; }, finishCallExpression: function (callee, args) { this.type = Syntax.CallExpression; this.callee = callee; this.arguments = args; this.finish(); return this; }, finishCatchClause: function (param, body) { this.type = Syntax.CatchClause; this.param = param; this.body = body; this.finish(); return this; }, finishClassBody: function (body) { this.type = Syntax.ClassBody; this.body = body; this.finish(); return this; }, finishClassDeclaration: function (id, superClass, body) { this.type = Syntax.ClassDeclaration; this.id = id; this.superClass = superClass; this.body = body; this.finish(); return this; }, finishClassExpression: function (id, superClass, body) { this.type = Syntax.ClassExpression; this.id = id; this.superClass = superClass; this.body = body; this.finish(); return this; }, finishConditionalExpression: function (test, consequent, alternate) { this.type = Syntax.ConditionalExpression; this.test = test; this.consequent = consequent; this.alternate = alternate; this.finish(); return this; }, finishContinueStatement: function (label) { this.type = Syntax.ContinueStatement; this.label = label; this.finish(); return this; }, finishDebuggerStatement: function () { this.type = Syntax.DebuggerStatement; this.finish(); return this; }, finishDoWhileStatement: function (body, test) { this.type = Syntax.DoWhileStatement; this.body = body; this.test = test; this.finish(); return this; }, finishEmptyStatement: function () { this.type = Syntax.EmptyStatement; this.finish(); return this; }, finishExpressionStatement: function (expression) { this.type = Syntax.ExpressionStatement; this.expression = expression; this.finish(); return this; }, finishForStatement: function (init, test, update, body) { this.type = Syntax.ForStatement; this.init = init; this.test = test; this.update = update; this.body = body; this.finish(); return this; }, finishForOfStatement: function (left, right, body) { this.type = Syntax.ForOfStatement; this.left = left; this.right = right; this.body = body; this.finish(); return this; }, finishForInStatement: function (left, right, body) { this.type = Syntax.ForInStatement; this.left = left; this.right = right; this.body = body; this.each = false; this.finish(); return this; }, finishFunctionDeclaration: function (id, params, defaults, body, generator) { this.type = Syntax.FunctionDeclaration; this.id = id; this.params = params; this.defaults = defaults; this.body = body; this.generator = generator; this.expression = false; this.finish(); return this; }, finishFunctionExpression: function (id, params, defaults, body, generator) { this.type = Syntax.FunctionExpression; this.id = id; this.params = params; this.defaults = defaults; this.body = body; this.generator = generator; this.expression = false; this.finish(); return this; }, finishIdentifier: function (name) { this.type = Syntax.Identifier; this.name = name; this.finish(); return this; }, finishIfStatement: function (test, consequent, alternate) { this.type = Syntax.IfStatement; this.test = test; this.consequent = consequent; this.alternate = alternate; this.finish(); return this; }, finishLabeledStatement: function (label, body) { this.type = Syntax.LabeledStatement; this.label = label; this.body = body; this.finish(); return this; }, finishLiteral: function (token) { this.type = Syntax.Literal; this.value = token.value; this.raw = source.slice(token.start, token.end); if (token.regex) { this.regex = token.regex; } this.finish(); return this; }, finishMemberExpression: function (accessor, object, property) { this.type = Syntax.MemberExpression; this.computed = accessor === '['; this.object = object; this.property = property; this.finish(); return this; }, finishMetaProperty: function (meta, property) { this.type = Syntax.MetaProperty; this.meta = meta; this.property = property; this.finish(); return this; }, finishNewExpression: function (callee, args) { this.type = Syntax.NewExpression; this.callee = callee; this.arguments = args; this.finish(); return this; }, finishObjectExpression: function (properties) { this.type = Syntax.ObjectExpression; this.properties = properties; this.finish(); return this; }, finishObjectPattern: function (properties) { this.type = Syntax.ObjectPattern; this.properties = properties; this.finish(); return this; }, finishPostfixExpression: function (operator, argument) { this.type = Syntax.UpdateExpression; this.operator = operator; this.argument = argument; this.prefix = false; this.finish(); return this; }, finishProgram: function (body, sourceType) { this.type = Syntax.Program; this.body = body; this.sourceType = sourceType; this.finish(); return this; }, finishProperty: function (kind, key, computed, value, method, shorthand) { this.type = Syntax.Property; this.key = key; this.computed = computed; this.value = value; this.kind = kind; this.method = method; this.shorthand = shorthand; this.finish(); return this; }, finishRestElement: function (argument) { this.type = Syntax.RestElement; this.argument = argument; this.finish(); return this; }, finishReturnStatement: function (argument) { this.type = Syntax.ReturnStatement; this.argument = argument; this.finish(); return this; }, finishSequenceExpression: function (expressions) { this.type = Syntax.SequenceExpression; this.expressions = expressions; this.finish(); return this; }, finishSpreadElement: function (argument) { this.type = Syntax.SpreadElement; this.argument = argument; this.finish(); return this; }, finishSwitchCase: function (test, consequent) { this.type = Syntax.SwitchCase; this.test = test; this.consequent = consequent; this.finish(); return this; }, finishSuper: function () { this.type = Syntax.Super; this.finish(); return this; }, finishSwitchStatement: function (discriminant, cases) { this.type = Syntax.SwitchStatement; this.discriminant = discriminant; this.cases = cases; this.finish(); return this; }, finishTaggedTemplateExpression: function (tag, quasi) { this.type = Syntax.TaggedTemplateExpression; this.tag = tag; this.quasi = quasi; this.finish(); return this; }, finishTemplateElement: function (value, tail) { this.type = Syntax.TemplateElement; this.value = value; this.tail = tail; this.finish(); return this; }, finishTemplateLiteral: function (quasis, expressions) { this.type = Syntax.TemplateLiteral; this.quasis = quasis; this.expressions = expressions; this.finish(); return this; }, finishThisExpression: function () { this.type = Syntax.ThisExpression; this.finish(); return this; }, finishThrowStatement: function (argument) { this.type = Syntax.ThrowStatement; this.argument = argument; this.finish(); return this; }, finishTryStatement: function (block, handler, finalizer) { this.type = Syntax.TryStatement; this.block = block; this.guardedHandlers = []; this.handlers = handler ? [handler] : []; this.handler = handler; this.finalizer = finalizer; this.finish(); return this; }, finishUnaryExpression: function (operator, argument) { this.type = (operator === '++' || operator === '--') ? Syntax.UpdateExpression : Syntax.UnaryExpression; this.operator = operator; this.argument = argument; this.prefix = true; this.finish(); return this; }, finishVariableDeclaration: function (declarations) { this.type = Syntax.VariableDeclaration; this.declarations = declarations; this.kind = 'var'; this.finish(); return this; }, finishLexicalDeclaration: function (declarations, kind) { this.type = Syntax.VariableDeclaration; this.declarations = declarations; this.kind = kind; this.finish(); return this; }, finishVariableDeclarator: function (id, init) { this.type = Syntax.VariableDeclarator; this.id = id; this.init = init; this.finish(); return this; }, finishWhileStatement: function (test, body) { this.type = Syntax.WhileStatement; this.test = test; this.body = body; this.finish(); return this; }, finishWithStatement: function (object, body) { this.type = Syntax.WithStatement; this.object = object; this.body = body; this.finish(); return this; }, finishExportSpecifier: function (local, exported) { this.type = Syntax.ExportSpecifier; this.exported = exported || local; this.local = local; this.finish(); return this; }, finishImportDefaultSpecifier: function (local) { this.type = Syntax.ImportDefaultSpecifier; this.local = local; this.finish(); return this; }, finishImportNamespaceSpecifier: function (local) { this.type = Syntax.ImportNamespaceSpecifier; this.local = local; this.finish(); return this; }, finishExportNamedDeclaration: function (declaration, specifiers, src) { this.type = Syntax.ExportNamedDeclaration; this.declaration = declaration; this.specifiers = specifiers; this.source = src; this.finish(); return this; }, finishExportDefaultDeclaration: function (declaration) { this.type = Syntax.ExportDefaultDeclaration; this.declaration = declaration; this.finish(); return this; }, finishExportAllDeclaration: function (src) { this.type = Syntax.ExportAllDeclaration; this.source = src; this.finish(); return this; }, finishImportSpecifier: function (local, imported) { this.type = Syntax.ImportSpecifier; this.local = local || imported; this.imported = imported; this.finish(); return this; }, finishImportDeclaration: function (specifiers, src) { this.type = Syntax.ImportDeclaration; this.specifiers = specifiers; this.source = src; this.finish(); return this; }, finishYieldExpression: function (argument, delegate) { this.type = Syntax.YieldExpression; this.argument = argument; this.delegate = delegate; this.finish(); return this; } }; function recordError(error) { var e, existing; for (e = 0; e < extra.errors.length; e++) { existing = extra.errors[e]; // Prevent duplicated error. /* istanbul ignore next */ if (existing.index === error.index && existing.message === error.message) { return; } } extra.errors.push(error); } function constructError(msg, column) { var error = new Error(msg); try { throw error; } catch (base) { /* istanbul ignore else */ if (Object.create && Object.defineProperty) { error = Object.create(base); Object.defineProperty(error, 'column', { value: column }); } } finally { return error; } } function createError(line, pos, description) { var msg, column, error; msg = 'Line ' + line + ': ' + description; column = pos - (scanning ? lineStart : lastLineStart) + 1; error = constructError(msg, column); error.lineNumber = line; error.description = description; error.index = pos; return error; } // Throw an exception function throwError(messageFormat) { var args, msg; args = Array.prototype.slice.call(arguments, 1); msg = messageFormat.replace(/%(\d)/g, function (whole, idx) { assert(idx < args.length, 'Message reference must be in range'); return args[idx]; } ); throw createError(lastLineNumber, lastIndex, msg); } function tolerateError(messageFormat) { var args, msg, error; args = Array.prototype.slice.call(arguments, 1); /* istanbul ignore next */ msg = messageFormat.replace(/%(\d)/g, function (whole, idx) { assert(idx < args.length, 'Message reference must be in range'); return args[idx]; } ); error = createError(lineNumber, lastIndex, msg); if (extra.errors) { recordError(error); } else { throw error; } } // Throw an exception because of the token. function unexpectedTokenError(token, message) { var value, msg = message || Messages.UnexpectedToken; if (token) { if (!message) { msg = (token.type === Token.EOF) ? Messages.UnexpectedEOS : (token.type === Token.Identifier) ? Messages.UnexpectedIdentifier : (token.type === Token.NumericLiteral) ? Messages.UnexpectedNumber : (token.type === Token.StringLiteral) ? Messages.UnexpectedString : (token.type === Token.Template) ? Messages.UnexpectedTemplate : Messages.UnexpectedToken; if (token.type === Token.Keyword) { if (isFutureReservedWord(token.value)) { msg = Messages.UnexpectedReserved; } else if (strict && isStrictModeReservedWord(token.value)) { msg = Messages.StrictReservedWord; } } } value = (token.type === Token.Template) ? token.value.raw : token.value; } else { value = 'ILLEGAL'; } msg = msg.replace('%0', value); return (token && typeof token.lineNumber === 'number') ? createError(token.lineNumber, token.start, msg) : createError(scanning ? lineNumber : lastLineNumber, scanning ? index : lastIndex, msg); } function throwUnexpectedToken(token, message) { throw unexpectedTokenError(token, message); } function tolerateUnexpectedToken(token, message) { var error = unexpectedTokenError(token, message); if (extra.errors) { recordError(error); } else { throw error; } } // Expect the next token to match the specified punctuator. // If not, an exception will be thrown. function expect(value) { var token = lex(); if (token.type !== Token.Punctuator || token.value !== value) { throwUnexpectedToken(token); } } /** * @name expectCommaSeparator * @description Quietly expect a comma when in tolerant mode, otherwise delegates * to expect(value) * @since 2.0 */ function expectCommaSeparator() { var token; if (extra.errors) { token = lookahead; if (token.type === Token.Punctuator && token.value === ',') { lex(); } else if (token.type === Token.Punctuator && token.value === ';') { lex(); tolerateUnexpectedToken(token); } else { tolerateUnexpectedToken(token, Messages.UnexpectedToken); } } else { expect(','); } } // Expect the next token to match the specified keyword. // If not, an exception will be thrown. function expectKeyword(keyword) { var token = lex(); if (token.type !== Token.Keyword || token.value !== keyword) { throwUnexpectedToken(token); } } // Return true if the next token matches the specified punctuator. function match(value) { return lookahead.type === Token.Punctuator && lookahead.value === value; } // Return true if the next token matches the specified keyword function matchKeyword(keyword) { return lookahead.type === Token.Keyword && lookahead.value === keyword; } // Return true if the next token matches the specified contextual keyword // (where an identifier is sometimes a keyword depending on the context) function matchContextualKeyword(keyword) { return lookahead.type === Token.Identifier && lookahead.value === keyword; } // Return true if the next token is an assignment operator function matchAssign() { var op; if (lookahead.type !== Token.Punctuator) { return false; } op = lookahead.value; return op === '=' || op === '*=' || op === '/=' || op === '%=' || op === '+=' || op === '-=' || op === '<<=' || op === '>>=' || op === '>>>=' || op === '&=' || op === '^=' || op === '|='; } function consumeSemicolon() { // Catch the very common case first: immediately a semicolon (U+003B). if (source.charCodeAt(startIndex) === 0x3B || match(';')) { lex(); return; } if (hasLineTerminator) { return; } // FIXME(ikarienator): this is seemingly an issue in the previous location info convention. lastIndex = startIndex; lastLineNumber = startLineNumber; lastLineStart = startLineStart; if (lookahead.type !== Token.EOF && !match('}')) { throwUnexpectedToken(lookahead); } } // Cover grammar support. // // When an assignment expression position starts with an left parenthesis, the determination of the type // of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead) // or the first comma. This situation also defers the determination of all the expressions nested in the pair. // // There are three productions that can be parsed in a parentheses pair that needs to be determined // after the outermost pair is closed. They are: // // 1. AssignmentExpression // 2. BindingElements // 3. AssignmentTargets // // In order to avoid exponential backtracking, we use two flags to denote if the production can be // binding element or assignment target. // // The three productions have the relationship: // // BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression // // with a single exception that CoverInitializedName when used directly in an Expression, generates // an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the // first usage of CoverInitializedName and report it when we reached the end of the parentheses pair. // // isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not // effect the current flags. This means the production the parser parses is only used as an expression. Therefore // the CoverInitializedName check is conducted. // // inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates // the flags outside of the parser. This means the production the parser parses is used as a part of a potential // pattern. The CoverInitializedName check is deferred. function isolateCoverGrammar(parser) { var oldIsBindingElement = isBindingElement, oldIsAssignmentTarget = isAssignmentTarget, oldFirstCoverInitializedNameError = firstCoverInitializedNameError, result; isBindingElement = true; isAssignmentTarget = true; firstCoverInitializedNameError = null; result = parser(); if (firstCoverInitializedNameError !== null) { throwUnexpectedToken(firstCoverInitializedNameError); } isBindingElement = oldIsBindingElement; isAssignmentTarget = oldIsAssignmentTarget; firstCoverInitializedNameError = oldFirstCoverInitializedNameError; return result; } function inheritCoverGrammar(parser) { var oldIsBindingElement = isBindingElement, oldIsAssignmentTarget = isAssignmentTarget, oldFirstCoverInitializedNameError = firstCoverInitializedNameError, result; isBindingElement = true; isAssignmentTarget = true; firstCoverInitializedNameError = null; result = parser(); isBindingElement = isBindingElement && oldIsBindingElement; isAssignmentTarget = isAssignmentTarget && oldIsAssignmentTarget; firstCoverInitializedNameError = oldFirstCoverInitializedNameError || firstCoverInitializedNameError; return result; } // ECMA-262 13.3.3 Destructuring Binding Patterns function parseArrayPattern(params, kind) { var node = new Node(), elements = [], rest, restNode; expect('['); while (!match(']')) { if (match(',')) { lex(); elements.push(null); } else { if (match('...')) { restNode = new Node(); lex(); params.push(lookahead); rest = parseVariableIdentifier(kind); elements.push(restNode.finishRestElement(rest)); break; } else { elements.push(parsePatternWithDefault(params, kind)); } if (!match(']')) { expect(','); } } } expect(']'); return node.finishArrayPattern(elements); } function parsePropertyPattern(params, kind) { var node = new Node(), key, keyToken, computed = match('['), init; if (lookahead.type === Token.Identifier) { keyToken = lookahead; key = parseVariableIdentifier(); if (match('=')) { params.push(keyToken); lex(); init = parseAssignmentExpression(); return node.finishProperty( 'init', key, false, new WrappingNode(keyToken).finishAssignmentPattern(key, init), false, true); } else if (!match(':')) { params.push(keyToken); return node.finishProperty('init', key, false, key, false, true); } } else { key = parseObjectPropertyKey(); } expect(':'); init = parsePatternWithDefault(params, kind); return node.finishProperty('init', key, computed, init, false, false); } function parseObjectPattern(params, kind) { var node = new Node(), properties = []; expect('{'); while (!match('}')) { properties.push(parsePropertyPattern(params, kind)); if (!match('}')) { expect(','); } } lex(); return node.finishObjectPattern(properties); } function parsePattern(params, kind) { if (match('[')) { return parseArrayPattern(params, kind); } else if (match('{')) { return parseObjectPattern(params, kind); } else if (matchKeyword('let')) { if (kind === 'const' || kind === 'let') { tolerateUnexpectedToken(lookahead, Messages.UnexpectedToken); } } params.push(lookahead); return parseVariableIdentifier(kind); } function parsePatternWithDefault(params, kind) { var startToken = lookahead, pattern, previousAllowYield, right; pattern = parsePattern(params, kind); if (match('=')) { lex(); previousAllowYield = state.allowYield; state.allowYield = true; right = isolateCoverGrammar(parseAssignmentExpression); state.allowYield = previousAllowYield; pattern = new WrappingNode(startToken).finishAssignmentPattern(pattern, right); } return pattern; } // ECMA-262 12.2.5 Array Initializer function parseArrayInitializer() { var elements = [], node = new Node(), restSpread; expect('['); while (!match(']')) { if (match(',')) { lex(); elements.push(null); } else if (match('...')) { restSpread = new Node(); lex(); restSpread.finishSpreadElement(inheritCoverGrammar(parseAssignmentExpression)); if (!match(']')) { isAssignmentTarget = isBindingElement = false; expect(','); } elements.push(restSpread); } else { elements.push(inheritCoverGrammar(parseAssignmentExpression)); if (!match(']')) { expect(','); } } } lex(); return node.finishArrayExpression(elements); } // ECMA-262 12.2.6 Object Initializer function parsePropertyFunction(node, paramInfo, isGenerator) { var previousStrict, body; isAssignmentTarget = isBindingElement = false; previousStrict = strict; body = isolateCoverGrammar(parseFunctionSourceElements); if (strict && paramInfo.firstRestricted) { tolerateUnexpectedToken(paramInfo.firstRestricted, paramInfo.message); } if (strict && paramInfo.stricted) { tolerateUnexpectedToken(paramInfo.stricted, paramInfo.message); } strict = previousStrict; return node.finishFunctionExpression(null, paramInfo.params, paramInfo.defaults, body, isGenerator); } function parsePropertyMethodFunction() { var params, method, node = new Node(), previousAllowYield = state.allowYield; state.allowYield = false; params = parseParams(); state.allowYield = previousAllowYield; state.allowYield = false; method = parsePropertyFunction(node, params, false); state.allowYield = previousAllowYield; return method; } function parseObjectPropertyKey() { var token, node = new Node(), expr; token = lex(); // Note: This function is called only from parseObjectProperty(), where // EOF and Punctuator tokens are already filtered out. switch (token.type) { case Token.StringLiteral: case Token.NumericLiteral: if (strict && token.octal) { tolerateUnexpectedToken(token, Messages.StrictOctalLiteral); } return node.finishLiteral(token); case Token.Identifier: case Token.BooleanLiteral: case Token.NullLiteral: case Token.Keyword: return node.finishIdentifier(token.value); case Token.Punctuator: if (token.value === '[') { expr = isolateCoverGrammar(parseAssignmentExpression); expect(']'); return expr; } break; } throwUnexpectedToken(token); } function lookaheadPropertyName() { switch (lookahead.type) { case Token.Identifier: case Token.StringLiteral: case Token.BooleanLiteral: case Token.NullLiteral: case Token.NumericLiteral: case Token.Keyword: return true; case Token.Punctuator: return lookahead.value === '['; } return false; } // This function is to try to parse a MethodDefinition as defined in 14.3. But in the case of object literals, // it might be called at a position where there is in fact a short hand identifier pattern or a data property. // This can only be determined after we consumed up to the left parentheses. // // In order to avoid back tracking, it returns `null` if the position is not a MethodDefinition and the caller // is responsible to visit other options. function tryParseMethodDefinition(token, key, computed, node) { var value, options, methodNode, params, previousAllowYield = state.allowYield; if (token.type === Token.Identifier) { // check for `get` and `set`; if (token.value === 'get' && lookaheadPropertyName()) { computed = match('['); key = parseObjectPropertyKey(); methodNode = new Node(); expect('('); expect(')'); state.allowYield = false; value = parsePropertyFunction(methodNode, { params: [], defaults: [], stricted: null, firstRestricted: null, message: null }, false); state.allowYield = previousAllowYield; return node.finishProperty('get', key, computed, value, false, false); } else if (token.value === 'set' && lookaheadPropertyName()) { computed = match('['); key = parseObjectPropertyKey(); methodNode = new Node(); expect('('); options = { params: [], defaultCount: 0, defaults: [], firstRestricted: null, paramSet: {} }; if (match(')')) { tolerateUnexpectedToken(lookahead); } else { state.allowYield = false; parseParam(options); state.allowYield = previousAllowYield; if (options.defaultCount === 0) { options.defaults = []; } } expect(')'); state.allowYield = false; value = parsePropertyFunction(methodNode, options, false); state.allowYield = previousAllowYield; return node.finishProperty('set', key, computed, value, false, false); } } else if (token.type === Token.Punctuator && token.value === '*' && lookaheadPropertyName()) { computed = match('['); key = parseObjectPropertyKey(); methodNode = new Node(); state.allowYield = true; params = parseParams(); state.allowYield = previousAllowYield; state.allowYield = false; value = parsePropertyFunction(methodNode, params, true); state.allowYield = previousAllowYield; return node.finishProperty('init', key, computed, value, true, false); } if (key && match('(')) { value = parsePropertyMethodFunction(); return node.finishProperty('init', key, computed, value, true, false); } // Not a MethodDefinition. return null; } function parseObjectProperty(hasProto) { var token = lookahead, node = new Node(), computed, key, maybeMethod, proto, value; computed = match('['); if (match('*')) { lex(); } else { key = parseObjectPropertyKey(); } maybeMethod = tryParseMethodDefinition(token, key, computed, node); if (maybeMethod) { return maybeMethod; } if (!key) { throwUnexpectedToken(lookahead); } // Check for duplicated __proto__ if (!computed) { proto = (key.type === Syntax.Identifier && key.name === '__proto__') || (key.type === Syntax.Literal && key.value === '__proto__'); if (hasProto.value && proto) { tolerateError(Messages.DuplicateProtoProperty); } hasProto.value |= proto; } if (match(':')) { lex(); value = inheritCoverGrammar(parseAssignmentExpression); return node.finishProperty('init', key, computed, value, false, false); } if (token.type === Token.Identifier) { if (match('=')) { firstCoverInitializedNameError = lookahead; lex(); value = isolateCoverGrammar(parseAssignmentExpression); return node.finishProperty('init', key, computed, new WrappingNode(token).finishAssignmentPattern(key, value), false, true); } return node.finishProperty('init', key, computed, key, false, true); } throwUnexpectedToken(lookahead); } function parseObjectInitializer() { var properties = [], hasProto = { value: false }, node = new Node(); expect('{'); while (!match('}')) { properties.push(parseObjectProperty(hasProto)); if (!match('}')) { expectCommaSeparator(); } } expect('}'); return node.finishObjectExpression(properties); } function reinterpretExpressionAsPattern(expr) { var i; switch (expr.type) { case Syntax.Identifier: case Syntax.MemberExpression: case Syntax.RestElement: case Syntax.AssignmentPattern: break; case Syntax.SpreadElement: expr.type = Syntax.RestElement; reinterpretExpressionAsPattern(expr.argument); break; case Syntax.ArrayExpression: expr.type = Syntax.ArrayPattern; for (i = 0; i < expr.elements.length; i++) { if (expr.elements[i] !== null) { reinterpretExpressionAsPattern(expr.elements[i]); } } break; case Syntax.ObjectExpression: expr.type = Syntax.ObjectPattern; for (i = 0; i < expr.properties.length; i++) { reinterpretExpressionAsPattern(expr.properties[i].value); } break; case Syntax.AssignmentExpression: expr.type = Syntax.AssignmentPattern; reinterpretExpressionAsPattern(expr.left); break; default: // Allow other node type for tolerant parsing. break; } } // ECMA-262 12.2.9 Template Literals function parseTemplateElement(option) { var node, token; if (lookahead.type !== Token.Template || (option.head && !lookahead.head)) { throwUnexpectedToken(); } node = new Node(); token = lex(); return node.finishTemplateElement({ raw: token.value.raw, cooked: token.value.cooked }, token.tail); } function parseTemplateLiteral() { var quasi, quasis, expressions, node = new Node(); quasi = parseTemplateElement({ head: true }); quasis = [quasi]; expressions = []; while (!quasi.tail) { expressions.push(parseExpression()); quasi = parseTemplateElement({ head: false }); quasis.push(quasi); } return node.finishTemplateLiteral(quasis, expressions); } // ECMA-262 12.2.10 The Grouping Operator function parseGroupExpression() { var expr, expressions, startToken, i, params = []; expect('('); if (match(')')) { lex(); if (!match('=>')) { expect('=>'); } return { type: PlaceHolders.ArrowParameterPlaceHolder, params: [], rawParams: [] }; } startToken = lookahead; if (match('...')) { expr = parseRestElement(params); expect(')'); if (!match('=>')) { expect('=>'); } return { type: PlaceHolders.ArrowParameterPlaceHolder, params: [expr] }; } isBindingElement = true; expr = inheritCoverGrammar(parseAssignmentExpression); if (match(',')) { isAssignmentTarget = false; expressions = [expr]; while (startIndex < length) { if (!match(',')) { break; } lex(); if (match('...')) { if (!isBindingElement) { throwUnexpectedToken(lookahead); } expressions.push(parseRestElement(params)); expect(')'); if (!match('=>')) { expect('=>'); } isBindingElement = false; for (i = 0; i < expressions.length; i++) { reinterpretExpressionAsPattern(expressions[i]); } return { type: PlaceHolders.ArrowParameterPlaceHolder, params: expressions }; } expressions.push(inheritCoverGrammar(parseAssignmentExpression)); } expr = new WrappingNode(startToken).finishSequenceExpression(expressions); } expect(')'); if (match('=>')) { if (expr.type === Syntax.Identifier && expr.name === 'yield') { return { type: PlaceHolders.ArrowParameterPlaceHolder, params: [expr] }; } if (!isBindingElement) { throwUnexpectedToken(lookahead); } if (expr.type === Syntax.SequenceExpression) { for (i = 0; i < expr.expressions.length; i++) { reinterpretExpressionAsPattern(expr.expressions[i]); } } else { reinterpretExpressionAsPattern(expr); } expr = { type: PlaceHolders.ArrowParameterPlaceHolder, params: expr.type === Syntax.SequenceExpression ? expr.expressions : [expr] }; } isBindingElement = false; return expr; } // ECMA-262 12.2 Primary Expressions function parsePrimaryExpression() { var type, token, expr, node; if (match('(')) { isBindingElement = false; return inheritCoverGrammar(parseGroupExpression); } if (match('[')) { return inheritCoverGrammar(parseArrayInitializer); } if (match('{')) { return inheritCoverGrammar(parseObjectInitializer); } type = lookahead.type; node = new Node(); if (type === Token.Identifier) { if (state.sourceType === 'module' && lookahead.value === 'await') { tolerateUnexpectedToken(lookahead); } expr = node.finishIdentifier(lex().value); } else if (type === Token.StringLiteral || type === Token.NumericLiteral) { isAssignmentTarget = isBindingElement = false; if (strict && lookahead.octal) { tolerateUnexpectedToken(lookahead, Messages.StrictOctalLiteral); } expr = node.finishLiteral(lex()); } else if (type === Token.Keyword) { if (!strict && state.allowYield && matchKeyword('yield')) { return parseNonComputedProperty(); } if (!strict && matchKeyword('let')) { return node.finishIdentifier(lex().value); } isAssignmentTarget = isBindingElement = false; if (matchKeyword('function')) { return parseFunctionExpression(); } if (matchKeyword('this')) { lex(); return node.finishThisExpression(); } if (matchKeyword('class')) { return parseClassExpression(); } throwUnexpectedToken(lex()); } else if (type === Token.BooleanLiteral) { isAssignmentTarget = isBindingElement = false; token = lex(); token.value = (token.value === 'true'); expr = node.finishLiteral(token); } else if (type === Token.NullLiteral) { isAssignmentTarget = isBindingElement = false; token = lex(); token.value = null; expr = node.finishLiteral(token); } else if (match('/') || match('/=')) { isAssignmentTarget = isBindingElement = false; index = startIndex; if (typeof extra.tokens !== 'undefined') { token = collectRegex(); } else { token = scanRegExp(); } lex(); expr = node.finishLiteral(token); } else if (type === Token.Template) { expr = parseTemplateLiteral(); } else { throwUnexpectedToken(lex()); } return expr; } // ECMA-262 12.3 Left-Hand-Side Expressions function parseArguments() { var args = [], expr; expect('('); if (!match(')')) { while (startIndex < length) { if (match('...')) { expr = new Node(); lex(); expr.finishSpreadElement(isolateCoverGrammar(parseAssignmentExpression)); } else { expr = isolateCoverGrammar(parseAssignmentExpression); } args.push(expr); if (match(')')) { break; } expectCommaSeparator(); } } expect(')'); return args; } function parseNonComputedProperty() { var token, node = new Node(); token = lex(); if (!isIdentifierName(token)) { throwUnexpectedToken(token); } return node.finishIdentifier(token.value); } function parseNonComputedMember() { expect('.'); return parseNonComputedProperty(); } function parseComputedMember() { var expr; expect('['); expr = isolateCoverGrammar(parseExpression); expect(']'); return expr; } // ECMA-262 12.3.3 The new Operator function parseNewExpression() { var callee, args, node = new Node(); expectKeyword('new'); if (match('.')) { lex(); if (lookahead.type === Token.Identifier && lookahead.value === 'target') { if (state.inFunctionBody) { lex(); return node.finishMetaProperty('new', 'target'); } } throwUnexpectedToken(lookahead); } callee = isolateCoverGrammar(parseLeftHandSideExpression); args = match('(') ? parseArguments() : []; isAssignmentTarget = isBindingElement = false; return node.finishNewExpression(callee, args); } // ECMA-262 12.3.4 Function Calls function parseLeftHandSideExpressionAllowCall() { var quasi, expr, args, property, startToken, previousAllowIn = state.allowIn; startToken = lookahead; state.allowIn = true; if (matchKeyword('super') && state.inFunctionBody) { expr = new Node(); lex(); expr = expr.finishSuper(); if (!match('(') && !match('.') && !match('[')) { throwUnexpectedToken(lookahead); } } else { expr = inheritCoverGrammar(matchKeyword('new') ? parseNewExpression : parsePrimaryExpression); } for (; ;) { if (match('.')) { isBindingElement = false; isAssignmentTarget = true; property = parseNonComputedMember(); expr = new WrappingNode(startToken).finishMemberExpression('.', expr, property); } else if (match('(')) { isBindingElement = false; isAssignmentTarget = false; args = parseArguments(); expr = new WrappingNode(startToken).finishCallExpression(expr, args); } else if (match('[')) { isBindingElement = false; isAssignmentTarget = true; property = parseComputedMember(); expr = new WrappingNode(startToken).finishMemberExpression('[', expr, property); } else if (lookahead.type === Token.Template && lookahead.head) { quasi = parseTemplateLiteral(); expr = new WrappingNode(startToken).finishTaggedTemplateExpression(expr, quasi); } else { break; } } state.allowIn = previousAllowIn; return expr; } // ECMA-262 12.3 Left-Hand-Side Expressions function parseLeftHandSideExpression() { var quasi, expr, property, startToken; assert(state.allowIn, 'callee of new expression always allow in keyword.'); startToken = lookahead; if (matchKeyword('super') && state.inFunctionBody) { expr = new Node(); lex(); expr = expr.finishSuper(); if (!match('[') && !match('.')) { throwUnexpectedToken(lookahead); } } else { expr = inheritCoverGrammar(matchKeyword('new') ? parseNewExpression : parsePrimaryExpression); } for (; ;) { if (match('[')) { isBindingElement = false; isAssignmentTarget = true; property = parseComputedMember(); expr = new WrappingNode(startToken).finishMemberExpression('[', expr, property); } else if (match('.')) { isBindingElement = false; isAssignmentTarget = true; property = parseNonComputedMember(); expr = new WrappingNode(startToken).finishMemberExpression('.', expr, property); } else if (lookahead.type === Token.Template && lookahead.head) { quasi = parseTemplateLiteral(); expr = new WrappingNode(startToken).finishTaggedTemplateExpression(expr, quasi); } else { break; } } return expr; } // ECMA-262 12.4 Postfix Expressions function parsePostfixExpression() { var expr, token, startToken = lookahead; expr = inheritCoverGrammar(parseLeftHandSideExpressionAllowCall); if (!hasLineTerminator && lookahead.type === Token.Punctuator) { if (match('++') || match('--')) { // ECMA-262 11.3.1, 11.3.2 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { tolerateError(Messages.StrictLHSPostfix); } if (!isAssignmentTarget) { tolerateError(Messages.InvalidLHSInAssignment); } isAssignmentTarget = isBindingElement = false; token = lex(); expr = new WrappingNode(startToken).finishPostfixExpression(token.value, expr); } } return expr; } // ECMA-262 12.5 Unary Operators function parseUnaryExpression() { var token, expr, startToken; if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) { expr = parsePostfixExpression(); } else if (match('++') || match('--')) { startToken = lookahead; token = lex(); expr = inheritCoverGrammar(parseUnaryExpression); // ECMA-262 11.4.4, 11.4.5 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { tolerateError(Messages.StrictLHSPrefix); } if (!isAssignmentTarget) { tolerateError(Messages.InvalidLHSInAssignment); } expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr); isAssignmentTarget = isBindingElement = false; } else if (match('+') || match('-') || match('~') || match('!')) { startToken = lookahead; token = lex(); expr = inheritCoverGrammar(parseUnaryExpression); expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr); isAssignmentTarget = isBindingElement = false; } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { startToken = lookahead; token = lex(); expr = inheritCoverGrammar(parseUnaryExpression); expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr); if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) { tolerateError(Messages.StrictDelete); } isAssignmentTarget = isBindingElement = false; } else { expr = parsePostfixExpression(); } return expr; } function binaryPrecedence(token, allowIn) { var prec = 0; if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { return 0; } switch (token.value) { case '||': prec = 1; break; case '&&': prec = 2; break; case '|': prec = 3; break; case '^': prec = 4; break; case '&': prec = 5; break; case '==': case '!=': case '===': case '!==': prec = 6; break; case '<': case '>': case '<=': case '>=': case 'instanceof': prec = 7; break; case 'in': prec = allowIn ? 7 : 0; break; case '<<': case '>>': case '>>>': prec = 8; break; case '+': case '-': prec = 9; break; case '*': case '/': case '%': prec = 11; break; default: break; } return prec; } // ECMA-262 12.6 Multiplicative Operators // ECMA-262 12.7 Additive Operators // ECMA-262 12.8 Bitwise Shift Operators // ECMA-262 12.9 Relational Operators // ECMA-262 12.10 Equality Operators // ECMA-262 12.11 Binary Bitwise Operators // ECMA-262 12.12 Binary Logical Operators function parseBinaryExpression() { var marker, markers, expr, token, prec, stack, right, operator, left, i; marker = lookahead; left = inheritCoverGrammar(parseUnaryExpression); token = lookahead; prec = binaryPrecedence(token, state.allowIn); if (prec === 0) { return left; } isAssignmentTarget = isBindingElement = false; token.prec = prec; lex(); markers = [marker, lookahead]; right = isolateCoverGrammar(parseUnaryExpression); stack = [left, token, right]; while ((prec = binaryPrecedence(lookahead, state.allowIn)) > 0) { // Reduce: make a binary expression from the three topmost entries. while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { right = stack.pop(); operator = stack.pop().value; left = stack.pop(); markers.pop(); expr = new WrappingNode(markers[markers.length - 1]).finishBinaryExpression(operator, left, right); stack.push(expr); } // Shift. token = lex(); token.prec = prec; stack.push(token); markers.push(lookahead); expr = isolateCoverGrammar(parseUnaryExpression); stack.push(expr); } // Final reduce to clean-up the stack. i = stack.length - 1; expr = stack[i]; markers.pop(); while (i > 1) { expr = new WrappingNode(markers.pop()).finishBinaryExpression(stack[i - 1].value, stack[i - 2], expr); i -= 2; } return expr; } // ECMA-262 12.13 Conditional Operator function parseConditionalExpression() { var expr, previousAllowIn, consequent, alternate, startToken; startToken = lookahead; expr = inheritCoverGrammar(parseBinaryExpression); if (match('?')) { lex(); previousAllowIn = state.allowIn; state.allowIn = true; consequent = isolateCoverGrammar(parseAssignmentExpression); state.allowIn = previousAllowIn; expect(':'); alternate = isolateCoverGrammar(parseAssignmentExpression); expr = new WrappingNode(startToken).finishConditionalExpression(expr, consequent, alternate); isAssignmentTarget = isBindingElement = false; } return expr; } // ECMA-262 14.2 Arrow Function Definitions function parseConciseBody() { if (match('{')) { return parseFunctionSourceElements(); } return isolateCoverGrammar(parseAssignmentExpression); } function checkPatternParam(options, param) { var i; switch (param.type) { case Syntax.Identifier: validateParam(options, param, param.name); break; case Syntax.RestElement: checkPatternParam(options, param.argument); break; case Syntax.AssignmentPattern: checkPatternParam(options, param.left); break; case Syntax.ArrayPattern: for (i = 0; i < param.elements.length; i++) { if (param.elements[i] !== null) { checkPatternParam(options, param.elements[i]); } } break; case Syntax.YieldExpression: break; default: assert(param.type === Syntax.ObjectPattern, 'Invalid type'); for (i = 0; i < param.properties.length; i++) { checkPatternParam(options, param.properties[i].value); } break; } } function reinterpretAsCoverFormalsList(expr) { var i, len, param, params, defaults, defaultCount, options, token; defaults = []; defaultCount = 0; params = [expr]; switch (expr.type) { case Syntax.Identifier: break; case PlaceHolders.ArrowParameterPlaceHolder: params = expr.params; break; default: return null; } options = { paramSet: {} }; for (i = 0, len = params.length; i < len; i += 1) { param = params[i]; switch (param.type) { case Syntax.AssignmentPattern: params[i] = param.left; if (param.right.type === Syntax.YieldExpression) { if (param.right.argument) { throwUnexpectedToken(lookahead); } param.right.type = Syntax.Identifier; param.right.name = 'yield'; delete param.right.argument; delete param.right.delegate; } defaults.push(param.right); ++defaultCount; checkPatternParam(options, param.left); break; default: checkPatternParam(options, param); params[i] = param; defaults.push(null); break; } } if (strict || !state.allowYield) { for (i = 0, len = params.length; i < len; i += 1) { param = params[i]; if (param.type === Syntax.YieldExpression) { throwUnexpectedToken(lookahead); } } } if (options.message === Messages.StrictParamDupe) { token = strict ? options.stricted : options.firstRestricted; throwUnexpectedToken(token, options.message); } if (defaultCount === 0) { defaults = []; } return { params: params, defaults: defaults, stricted: options.stricted, firstRestricted: options.firstRestricted, message: options.message }; } function parseArrowFunctionExpression(options, node) { var previousStrict, previousAllowYield, body; if (hasLineTerminator) { tolerateUnexpectedToken(lookahead); } expect('=>'); previousStrict = strict; previousAllowYield = state.allowYield; state.allowYield = true; body = parseConciseBody(); if (strict && options.firstRestricted) { throwUnexpectedToken(options.firstRestricted, options.message); } if (strict && options.stricted) { tolerateUnexpectedToken(options.stricted, options.message); } strict = previousStrict; state.allowYield = previousAllowYield; return node.finishArrowFunctionExpression(options.params, options.defaults, body, body.type !== Syntax.BlockStatement); } // ECMA-262 14.4 Yield expression function parseYieldExpression() { var argument, expr, delegate, previousAllowYield; argument = null; expr = new Node(); delegate = false; expectKeyword('yield'); if (!hasLineTerminator) { previousAllowYield = state.allowYield; state.allowYield = false; delegate = match('*'); if (delegate) { lex(); argument = parseAssignmentExpression(); } else { if (!match(';') && !match('}') && !match(')') && lookahead.type !== Token.EOF) { argument = parseAssignmentExpression(); } } state.allowYield = previousAllowYield; } return expr.finishYieldExpression(argument, delegate); } // ECMA-262 12.14 Assignment Operators function parseAssignmentExpression() { var token, expr, right, list, startToken; startToken = lookahead; token = lookahead; if (!state.allowYield && matchKeyword('yield')) { return parseYieldExpression(); } expr = parseConditionalExpression(); if (expr.type === PlaceHolders.ArrowParameterPlaceHolder || match('=>')) { isAssignmentTarget = isBindingElement = false; list = reinterpretAsCoverFormalsList(expr); if (list) { firstCoverInitializedNameError = null; return parseArrowFunctionExpression(list, new WrappingNode(startToken)); } return expr; } if (matchAssign()) { if (!isAssignmentTarget) { tolerateError(Messages.InvalidLHSInAssignment); } // ECMA-262 12.1.1 if (strict && expr.type === Syntax.Identifier) { if (isRestrictedWord(expr.name)) { tolerateUnexpectedToken(token, Messages.StrictLHSAssignment); } if (isStrictModeReservedWord(expr.name)) { tolerateUnexpectedToken(token, Messages.StrictReservedWord); } } if (!match('=')) { isAssignmentTarget = isBindingElement = false; } else { reinterpretExpressionAsPattern(expr); } token = lex(); right = isolateCoverGrammar(parseAssignmentExpression); expr = new WrappingNode(startToken).finishAssignmentExpression(token.value, expr, right); firstCoverInitializedNameError = null; } return expr; } // ECMA-262 12.15 Comma Operator function parseExpression() { var expr, startToken = lookahead, expressions; expr = isolateCoverGrammar(parseAssignmentExpression); if (match(',')) { expressions = [expr]; while (startIndex < length) { if (!match(',')) { break; } lex(); expressions.push(isolateCoverGrammar(parseAssignmentExpression)); } expr = new WrappingNode(startToken).finishSequenceExpression(expressions); } return expr; } // ECMA-262 13.2 Block function parseStatementListItem() { if (lookahead.type === Token.Keyword) { switch (lookahead.value) { case 'export': if (state.sourceType !== 'module') { tolerateUnexpectedToken(lookahead, Messages.IllegalExportDeclaration); } return parseExportDeclaration(); case 'import': if (state.sourceType !== 'module') { tolerateUnexpectedToken(lookahead, Messages.IllegalImportDeclaration); } return parseImportDeclaration(); case 'const': return parseLexicalDeclaration({ inFor: false }); case 'function': return parseFunctionDeclaration(new Node()); case 'class': return parseClassDeclaration(); } } if (matchKeyword('let') && isLexicalDeclaration()) { return parseLexicalDeclaration({ inFor: false }); } return parseStatement(); } function parseStatementList() { var list = []; while (startIndex < length) { if (match('}')) { break; } list.push(parseStatementListItem()); } return list; } function parseBlock() { var block, node = new Node(); expect('{'); block = parseStatementList(); expect('}'); return node.finishBlockStatement(block); } // ECMA-262 13.3.2 Variable Statement function parseVariableIdentifier(kind) { var token, node = new Node(); token = lex(); if (token.type === Token.Keyword && token.value === 'yield') { if (strict) { tolerateUnexpectedToken(token, Messages.StrictReservedWord); } if (!state.allowYield) { throwUnexpectedToken(token); } } else if (token.type !== Token.Identifier) { if (strict && token.type === Token.Keyword && isStrictModeReservedWord(token.value)) { tolerateUnexpectedToken(token, Messages.StrictReservedWord); } else { if (strict || token.value !== 'let' || kind !== 'var') { throwUnexpectedToken(token); } } } else if (state.sourceType === 'module' && token.type === Token.Identifier && token.value === 'await') { tolerateUnexpectedToken(token); } return node.finishIdentifier(token.value); } function parseVariableDeclaration(options) { var init = null, id, node = new Node(), params = []; id = parsePattern(params, 'var'); // ECMA-262 12.2.1 if (strict && isRestrictedWord(id.name)) { tolerateError(Messages.StrictVarName); } if (match('=')) { lex(); init = isolateCoverGrammar(parseAssignmentExpression); } else if (id.type !== Syntax.Identifier && !options.inFor) { expect('='); } return node.finishVariableDeclarator(id, init); } function parseVariableDeclarationList(options) { var opt, list; opt = { inFor: options.inFor }; list = [parseVariableDeclaration(opt)]; while (match(',')) { lex(); list.push(parseVariableDeclaration(opt)); } return list; } function parseVariableStatement(node) { var declarations; expectKeyword('var'); declarations = parseVariableDeclarationList({ inFor: false }); consumeSemicolon(); return node.finishVariableDeclaration(declarations); } // ECMA-262 13.3.1 Let and Const Declarations function parseLexicalBinding(kind, options) { var init = null, id, node = new Node(), params = []; id = parsePattern(params, kind); // ECMA-262 12.2.1 if (strict && id.type === Syntax.Identifier && isRestrictedWord(id.name)) { tolerateError(Messages.StrictVarName); } if (kind === 'const') { if (!matchKeyword('in') && !matchContextualKeyword('of')) { expect('='); init = isolateCoverGrammar(parseAssignmentExpression); } } else if ((!options.inFor && id.type !== Syntax.Identifier) || match('=')) { expect('='); init = isolateCoverGrammar(parseAssignmentExpression); } return node.finishVariableDeclarator(id, init); } function parseBindingList(kind, options) { var list = [parseLexicalBinding(kind, options)]; while (match(',')) { lex(); list.push(parseLexicalBinding(kind, options)); } return list; } function tokenizerState() { return { index: index, lineNumber: lineNumber, lineStart: lineStart, hasLineTerminator: hasLineTerminator, lastIndex: lastIndex, lastLineNumber: lastLineNumber, lastLineStart: lastLineStart, startIndex: startIndex, startLineNumber: startLineNumber, startLineStart: startLineStart, lookahead: lookahead, tokenCount: extra.tokens ? extra.tokens.length : 0 }; } function resetTokenizerState(ts) { index = ts.index; lineNumber = ts.lineNumber; lineStart = ts.lineStart; hasLineTerminator = ts.hasLineTerminator; lastIndex = ts.lastIndex; lastLineNumber = ts.lastLineNumber; lastLineStart = ts.lastLineStart; startIndex = ts.startIndex; startLineNumber = ts.startLineNumber; startLineStart = ts.startLineStart; lookahead = ts.lookahead; if (extra.tokens) { extra.tokens.splice(ts.tokenCount, extra.tokens.length); } } function isLexicalDeclaration() { var lexical, ts; ts = tokenizerState(); lex(); lexical = (lookahead.type === Token.Identifier) || match('[') || match('{') || matchKeyword('let') || matchKeyword('yield'); resetTokenizerState(ts); return lexical; } function parseLexicalDeclaration(options) { var kind, declarations, node = new Node(); kind = lex().value; assert(kind === 'let' || kind === 'const', 'Lexical declaration must be either let or const'); declarations = parseBindingList(kind, options); consumeSemicolon(); return node.finishLexicalDeclaration(declarations, kind); } function parseRestElement(params) { var param, node = new Node(); lex(); if (match('{')) { throwError(Messages.ObjectPatternAsRestParameter); } params.push(lookahead); param = parseVariableIdentifier(); if (match('=')) { throwError(Messages.DefaultRestParameter); } if (!match(')')) { throwError(Messages.ParameterAfterRestParameter); } return node.finishRestElement(param); } // ECMA-262 13.4 Empty Statement function parseEmptyStatement(node) { expect(';'); return node.finishEmptyStatement(); } // ECMA-262 12.4 Expression Statement function parseExpressionStatement(node) { var expr = parseExpression(); consumeSemicolon(); return node.finishExpressionStatement(expr); } // ECMA-262 13.6 If statement function parseIfStatement(node) { var test, consequent, alternate; expectKeyword('if'); expect('('); test = parseExpression(); expect(')'); consequent = parseStatement(); if (matchKeyword('else')) { lex(); alternate = parseStatement(); } else { alternate = null; } return node.finishIfStatement(test, consequent, alternate); } // ECMA-262 13.7 Iteration Statements function parseDoWhileStatement(node) { var body, test, oldInIteration; expectKeyword('do'); oldInIteration = state.inIteration; state.inIteration = true; body = parseStatement(); state.inIteration = oldInIteration; expectKeyword('while'); expect('('); test = parseExpression(); expect(')'); if (match(';')) { lex(); } return node.finishDoWhileStatement(body, test); } function parseWhileStatement(node) { var test, body, oldInIteration; expectKeyword('while'); expect('('); test = parseExpression(); expect(')'); oldInIteration = state.inIteration; state.inIteration = true; body = parseStatement(); state.inIteration = oldInIteration; return node.finishWhileStatement(test, body); } function parseForStatement(node) { var init, forIn, initSeq, initStartToken, test, update, left, right, kind, declarations, body, oldInIteration, previousAllowIn = state.allowIn; init = test = update = null; forIn = true; expectKeyword('for'); expect('('); if (match(';')) { lex(); } else { if (matchKeyword('var')) { init = new Node(); lex(); state.allowIn = false; declarations = parseVariableDeclarationList({ inFor: true }); state.allowIn = previousAllowIn; if (declarations.length === 1 && matchKeyword('in')) { init = init.finishVariableDeclaration(declarations); lex(); left = init; right = parseExpression(); init = null; } else if (declarations.length === 1 && declarations[0].init === null && matchContextualKeyword('of')) { init = init.finishVariableDeclaration(declarations); lex(); left = init; right = parseAssignmentExpression(); init = null; forIn = false; } else { init = init.finishVariableDeclaration(declarations); expect(';'); } } else if (matchKeyword('const') || matchKeyword('let')) { init = new Node(); kind = lex().value; if (!strict && lookahead.value === 'in') { init = init.finishIdentifier(kind); lex(); left = init; right = parseExpression(); init = null; } else { state.allowIn = false; declarations = parseBindingList(kind, { inFor: true }); state.allowIn = previousAllowIn; if (declarations.length === 1 && declarations[0].init === null && matchKeyword('in')) { init = init.finishLexicalDeclaration(declarations, kind); lex(); left = init; right = parseExpression(); init = null; } else if (declarations.length === 1 && declarations[0].init === null && matchContextualKeyword('of')) { init = init.finishLexicalDeclaration(declarations, kind); lex(); left = init; right = parseAssignmentExpression(); init = null; forIn = false; } else { consumeSemicolon(); init = init.finishLexicalDeclaration(declarations, kind); } } } else { initStartToken = lookahead; state.allowIn = false; init = inheritCoverGrammar(parseAssignmentExpression); state.allowIn = previousAllowIn; if (matchKeyword('in')) { if (!isAssignmentTarget) { tolerateError(Messages.InvalidLHSInForIn); } lex(); reinterpretExpressionAsPattern(init); left = init; right = parseExpression(); init = null; } else if (matchContextualKeyword('of')) { if (!isAssignmentTarget) { tolerateError(Messages.InvalidLHSInForLoop); } lex(); reinterpretExpressionAsPattern(init); left = init; right = parseAssignmentExpression(); init = null; forIn = false; } else { if (match(',')) { initSeq = [init]; while (match(',')) { lex(); initSeq.push(isolateCoverGrammar(parseAssignmentExpression)); } init = new WrappingNode(initStartToken).finishSequenceExpression(initSeq); } expect(';'); } } } if (typeof left === 'undefined') { if (!match(';')) { test = parseExpression(); } expect(';'); if (!match(')')) { update = parseExpression(); } } expect(')'); oldInIteration = state.inIteration; state.inIteration = true; body = isolateCoverGrammar(parseStatement); state.inIteration = oldInIteration; return (typeof left === 'undefined') ? node.finishForStatement(init, test, update, body) : forIn ? node.finishForInStatement(left, right, body) : node.finishForOfStatement(left, right, body); } // ECMA-262 13.8 The continue statement function parseContinueStatement(node) { var label = null, key; expectKeyword('continue'); // Optimize the most common form: 'continue;'. if (source.charCodeAt(startIndex) === 0x3B) { lex(); if (!state.inIteration) { throwError(Messages.IllegalContinue); } return node.finishContinueStatement(null); } if (hasLineTerminator) { if (!state.inIteration) { throwError(Messages.IllegalContinue); } return node.finishContinueStatement(null); } if (lookahead.type === Token.Identifier) { label = parseVariableIdentifier(); key = '$' + label.name; if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { throwError(Messages.UnknownLabel, label.name); } } consumeSemicolon(); if (label === null && !state.inIteration) { throwError(Messages.IllegalContinue); } return node.finishContinueStatement(label); } // ECMA-262 13.9 The break statement function parseBreakStatement(node) { var label = null, key; expectKeyword('break'); // Catch the very common case first: immediately a semicolon (U+003B). if (source.charCodeAt(lastIndex) === 0x3B) { lex(); if (!(state.inIteration || state.inSwitch)) { throwError(Messages.IllegalBreak); } return node.finishBreakStatement(null); } if (hasLineTerminator) { if (!(state.inIteration || state.inSwitch)) { throwError(Messages.IllegalBreak); } } else if (lookahead.type === Token.Identifier) { label = parseVariableIdentifier(); key = '$' + label.name; if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { throwError(Messages.UnknownLabel, label.name); } } consumeSemicolon(); if (label === null && !(state.inIteration || state.inSwitch)) { throwError(Messages.IllegalBreak); } return node.finishBreakStatement(label); } // ECMA-262 13.10 The return statement function parseReturnStatement(node) { var argument = null; expectKeyword('return'); if (!state.inFunctionBody) { tolerateError(Messages.IllegalReturn); } // 'return' followed by a space and an identifier is very common. if (source.charCodeAt(lastIndex) === 0x20) { if (isIdentifierStart(source.charCodeAt(lastIndex + 1))) { argument = parseExpression(); consumeSemicolon(); return node.finishReturnStatement(argument); } } if (hasLineTerminator) { // HACK return node.finishReturnStatement(null); } if (!match(';')) { if (!match('}') && lookahead.type !== Token.EOF) { argument = parseExpression(); } } consumeSemicolon(); return node.finishReturnStatement(argument); } // ECMA-262 13.11 The with statement function parseWithStatement(node) { var object, body; if (strict) { tolerateError(Messages.StrictModeWith); } expectKeyword('with'); expect('('); object = parseExpression(); expect(')'); body = parseStatement(); return node.finishWithStatement(object, body); } // ECMA-262 13.12 The switch statement function parseSwitchCase() { var test, consequent = [], statement, node = new Node(); if (matchKeyword('default')) { lex(); test = null; } else { expectKeyword('case'); test = parseExpression(); } expect(':'); while (startIndex < length) { if (match('}') || matchKeyword('default') || matchKeyword('case')) { break; } statement = parseStatementListItem(); consequent.push(statement); } return node.finishSwitchCase(test, consequent); } function parseSwitchStatement(node) { var discriminant, cases, clause, oldInSwitch, defaultFound; expectKeyword('switch'); expect('('); discriminant = parseExpression(); expect(')'); expect('{'); cases = []; if (match('}')) { lex(); return node.finishSwitchStatement(discriminant, cases); } oldInSwitch = state.inSwitch; state.inSwitch = true; defaultFound = false; while (startIndex < length) { if (match('}')) { break; } clause = parseSwitchCase(); if (clause.test === null) { if (defaultFound) { throwError(Messages.MultipleDefaultsInSwitch); } defaultFound = true; } cases.push(clause); } state.inSwitch = oldInSwitch; expect('}'); return node.finishSwitchStatement(discriminant, cases); } // ECMA-262 13.14 The throw statement function parseThrowStatement(node) { var argument; expectKeyword('throw'); if (hasLineTerminator) { throwError(Messages.NewlineAfterThrow); } argument = parseExpression(); consumeSemicolon(); return node.finishThrowStatement(argument); } // ECMA-262 13.15 The try statement function parseCatchClause() { var param, params = [], paramMap = {}, key, i, body, node = new Node(); expectKeyword('catch'); expect('('); if (match(')')) { throwUnexpectedToken(lookahead); } param = parsePattern(params); for (i = 0; i < params.length; i++) { key = '$' + params[i].value; if (Object.prototype.hasOwnProperty.call(paramMap, key)) { tolerateError(Messages.DuplicateBinding, params[i].value); } paramMap[key] = true; } // ECMA-262 12.14.1 if (strict && isRestrictedWord(param.name)) { tolerateError(Messages.StrictCatchVariable); } expect(')'); body = parseBlock(); return node.finishCatchClause(param, body); } function parseTryStatement(node) { var block, handler = null, finalizer = null; expectKeyword('try'); block = parseBlock(); if (matchKeyword('catch')) { handler = parseCatchClause(); } if (matchKeyword('finally')) { lex(); finalizer = parseBlock(); } if (!handler && !finalizer) { throwError(Messages.NoCatchOrFinally); } return node.finishTryStatement(block, handler, finalizer); } // ECMA-262 13.16 The debugger statement function parseDebuggerStatement(node) { expectKeyword('debugger'); consumeSemicolon(); return node.finishDebuggerStatement(); } // 13 Statements function parseStatement() { var type = lookahead.type, expr, labeledBody, key, node; if (type === Token.EOF) { throwUnexpectedToken(lookahead); } if (type === Token.Punctuator && lookahead.value === '{') { return parseBlock(); } isAssignmentTarget = isBindingElement = true; node = new Node(); if (type === Token.Punctuator) { switch (lookahead.value) { case ';': return parseEmptyStatement(node); case '(': return parseExpressionStatement(node); default: break; } } else if (type === Token.Keyword) { switch (lookahead.value) { case 'break': return parseBreakStatement(node); case 'continue': return parseContinueStatement(node); case 'debugger': return parseDebuggerStatement(node); case 'do': return parseDoWhileStatement(node); case 'for': return parseForStatement(node); case 'function': return parseFunctionDeclaration(node); case 'if': return parseIfStatement(node); case 'return': return parseReturnStatement(node); case 'switch': return parseSwitchStatement(node); case 'throw': return parseThrowStatement(node); case 'try': return parseTryStatement(node); case 'var': return parseVariableStatement(node); case 'while': return parseWhileStatement(node); case 'with': return parseWithStatement(node); default: break; } } expr = parseExpression(); // ECMA-262 12.12 Labelled Statements if ((expr.type === Syntax.Identifier) && match(':')) { lex(); key = '$' + expr.name; if (Object.prototype.hasOwnProperty.call(state.labelSet, key)) { throwError(Messages.Redeclaration, 'Label', expr.name); } state.labelSet[key] = true; labeledBody = parseStatement(); delete state.labelSet[key]; return node.finishLabeledStatement(expr, labeledBody); } consumeSemicolon(); return node.finishExpressionStatement(expr); } // ECMA-262 14.1 Function Definition function parseFunctionSourceElements() { var statement, body = [], token, directive, firstRestricted, oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, node = new Node(); expect('{'); while (startIndex < length) { if (lookahead.type !== Token.StringLiteral) { break; } token = lookahead; statement = parseStatementListItem(); body.push(statement); if (statement.expression.type !== Syntax.Literal) { // this is not directive break; } directive = source.slice(token.start + 1, token.end - 1); if (directive === 'use strict') { strict = true; if (firstRestricted) { tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral); } } else { if (!firstRestricted && token.octal) { firstRestricted = token; } } } oldLabelSet = state.labelSet; oldInIteration = state.inIteration; oldInSwitch = state.inSwitch; oldInFunctionBody = state.inFunctionBody; state.labelSet = {}; state.inIteration = false; state.inSwitch = false; state.inFunctionBody = true; while (startIndex < length) { if (match('}')) { break; } body.push(parseStatementListItem()); } expect('}'); state.labelSet = oldLabelSet; state.inIteration = oldInIteration; state.inSwitch = oldInSwitch; state.inFunctionBody = oldInFunctionBody; return node.finishBlockStatement(body); } function validateParam(options, param, name) { var key = '$' + name; if (strict) { if (isRestrictedWord(name)) { options.stricted = param; options.message = Messages.StrictParamName; } if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { options.stricted = param; options.message = Messages.StrictParamDupe; } } else if (!options.firstRestricted) { if (isRestrictedWord(name)) { options.firstRestricted = param; options.message = Messages.StrictParamName; } else if (isStrictModeReservedWord(name)) { options.firstRestricted = param; options.message = Messages.StrictReservedWord; } else if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { options.stricted = param; options.message = Messages.StrictParamDupe; } } options.paramSet[key] = true; } function parseParam(options) { var token, param, params = [], i, def; token = lookahead; if (token.value === '...') { param = parseRestElement(params); validateParam(options, param.argument, param.argument.name); options.params.push(param); options.defaults.push(null); return false; } param = parsePatternWithDefault(params); for (i = 0; i < params.length; i++) { validateParam(options, params[i], params[i].value); } if (param.type === Syntax.AssignmentPattern) { def = param.right; param = param.left; ++options.defaultCount; } options.params.push(param); options.defaults.push(def); return !match(')'); } function parseParams(firstRestricted) { var options; options = { params: [], defaultCount: 0, defaults: [], firstRestricted: firstRestricted }; expect('('); if (!match(')')) { options.paramSet = {}; while (startIndex < length) { if (!parseParam(options)) { break; } expect(','); } } expect(')'); if (options.defaultCount === 0) { options.defaults = []; } return { params: options.params, defaults: options.defaults, stricted: options.stricted, firstRestricted: options.firstRestricted, message: options.message }; } function parseFunctionDeclaration(node, identifierIsOptional) { var id = null, params = [], defaults = [], body, token, stricted, tmp, firstRestricted, message, previousStrict, isGenerator, previousAllowYield; previousAllowYield = state.allowYield; expectKeyword('function'); isGenerator = match('*'); if (isGenerator) { lex(); } if (!identifierIsOptional || !match('(')) { token = lookahead; id = parseVariableIdentifier(); if (strict) { if (isRestrictedWord(token.value)) { tolerateUnexpectedToken(token, Messages.StrictFunctionName); } } else { if (isRestrictedWord(token.value)) { firstRestricted = token; message = Messages.StrictFunctionName; } else if (isStrictModeReservedWord(token.value)) { firstRestricted = token; message = Messages.StrictReservedWord; } } } state.allowYield = !isGenerator; tmp = parseParams(firstRestricted); params = tmp.params; defaults = tmp.defaults; stricted = tmp.stricted; firstRestricted = tmp.firstRestricted; if (tmp.message) { message = tmp.message; } previousStrict = strict; body = parseFunctionSourceElements(); if (strict && firstRestricted) { throwUnexpectedToken(firstRestricted, message); } if (strict && stricted) { tolerateUnexpectedToken(stricted, message); } strict = previousStrict; state.allowYield = previousAllowYield; return node.finishFunctionDeclaration(id, params, defaults, body, isGenerator); } function parseFunctionExpression() { var token, id = null, stricted, firstRestricted, message, tmp, params = [], defaults = [], body, previousStrict, node = new Node(), isGenerator, previousAllowYield; previousAllowYield = state.allowYield; expectKeyword('function'); isGenerator = match('*'); if (isGenerator) { lex(); } state.allowYield = !isGenerator; if (!match('(')) { token = lookahead; id = (!strict && !isGenerator && matchKeyword('yield')) ? parseNonComputedProperty() : parseVariableIdentifier(); if (strict) { if (isRestrictedWord(token.value)) { tolerateUnexpectedToken(token, Messages.StrictFunctionName); } } else { if (isRestrictedWord(token.value)) { firstRestricted = token; message = Messages.StrictFunctionName; } else if (isStrictModeReservedWord(token.value)) { firstRestricted = token; message = Messages.StrictReservedWord; } } } tmp = parseParams(firstRestricted); params = tmp.params; defaults = tmp.defaults; stricted = tmp.stricted; firstRestricted = tmp.firstRestricted; if (tmp.message) { message = tmp.message; } previousStrict = strict; body = parseFunctionSourceElements(); if (strict && firstRestricted) { throwUnexpectedToken(firstRestricted, message); } if (strict && stricted) { tolerateUnexpectedToken(stricted, message); } strict = previousStrict; state.allowYield = previousAllowYield; return node.finishFunctionExpression(id, params, defaults, body, isGenerator); } // ECMA-262 14.5 Class Definitions function parseClassBody() { var classBody, token, isStatic, hasConstructor = false, body, method, computed, key; classBody = new Node(); expect('{'); body = []; while (!match('}')) { if (match(';')) { lex(); } else { method = new Node(); token = lookahead; isStatic = false; computed = match('['); if (match('*')) { lex(); } else { key = parseObjectPropertyKey(); if (key.name === 'static' && (lookaheadPropertyName() || match('*'))) { token = lookahead; isStatic = true; computed = match('['); if (match('*')) { lex(); } else { key = parseObjectPropertyKey(); } } } method = tryParseMethodDefinition(token, key, computed, method); if (method) { method['static'] = isStatic; // jscs:ignore requireDotNotation if (method.kind === 'init') { method.kind = 'method'; } if (!isStatic) { if (!method.computed && (method.key.name || method.key.value.toString()) === 'constructor') { if (method.kind !== 'method' || !method.method || method.value.generator) { throwUnexpectedToken(token, Messages.ConstructorSpecialMethod); } if (hasConstructor) { throwUnexpectedToken(token, Messages.DuplicateConstructor); } else { hasConstructor = true; } method.kind = 'constructor'; } } else { if (!method.computed && (method.key.name || method.key.value.toString()) === 'prototype') { throwUnexpectedToken(token, Messages.StaticPrototype); } } method.type = Syntax.MethodDefinition; delete method.method; delete method.shorthand; body.push(method); } else { throwUnexpectedToken(lookahead); } } } lex(); return classBody.finishClassBody(body); } function parseClassDeclaration(identifierIsOptional) { var id = null, superClass = null, classNode = new Node(), classBody, previousStrict = strict; strict = true; expectKeyword('class'); if (!identifierIsOptional || lookahead.type === Token.Identifier) { id = parseVariableIdentifier(); } if (matchKeyword('extends')) { lex(); superClass = isolateCoverGrammar(parseLeftHandSideExpressionAllowCall); } classBody = parseClassBody(); strict = previousStrict; return classNode.finishClassDeclaration(id, superClass, classBody); } function parseClassExpression() { var id = null, superClass = null, classNode = new Node(), classBody, previousStrict = strict; strict = true; expectKeyword('class'); if (lookahead.type === Token.Identifier) { id = parseVariableIdentifier(); } if (matchKeyword('extends')) { lex(); superClass = isolateCoverGrammar(parseLeftHandSideExpressionAllowCall); } classBody = parseClassBody(); strict = previousStrict; return classNode.finishClassExpression(id, superClass, classBody); } // ECMA-262 15.2 Modules function parseModuleSpecifier() { var node = new Node(); if (lookahead.type !== Token.StringLiteral) { throwError(Messages.InvalidModuleSpecifier); } return node.finishLiteral(lex()); } // ECMA-262 15.2.3 Exports function parseExportSpecifier() { var exported, local, node = new Node(), def; if (matchKeyword('default')) { // export {default} from 'something'; def = new Node(); lex(); local = def.finishIdentifier('default'); } else { local = parseVariableIdentifier(); } if (matchContextualKeyword('as')) { lex(); exported = parseNonComputedProperty(); } return node.finishExportSpecifier(local, exported); } function parseExportNamedDeclaration(node) { var declaration = null, isExportFromIdentifier, src = null, specifiers = []; // non-default export if (lookahead.type === Token.Keyword) { // covers: // export var f = 1; switch (lookahead.value) { case 'let': case 'const': declaration = parseLexicalDeclaration({ inFor: false }); return node.finishExportNamedDeclaration(declaration, specifiers, null); case 'var': case 'class': case 'function': declaration = parseStatementListItem(); return node.finishExportNamedDeclaration(declaration, specifiers, null); } } expect('{'); while (!match('}')) { isExportFromIdentifier = isExportFromIdentifier || matchKeyword('default'); specifiers.push(parseExportSpecifier()); if (!match('}')) { expect(','); if (match('}')) { break; } } } expect('}'); if (matchContextualKeyword('from')) { // covering: // export {default} from 'foo'; // export {foo} from 'foo'; lex(); src = parseModuleSpecifier(); consumeSemicolon(); } else if (isExportFromIdentifier) { // covering: // export {default}; // missing fromClause throwError(lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); } else { // cover // export {foo}; consumeSemicolon(); } return node.finishExportNamedDeclaration(declaration, specifiers, src); } function parseExportDefaultDeclaration(node) { var declaration = null, expression = null; // covers: // export default ... expectKeyword('default'); if (matchKeyword('function')) { // covers: // export default function foo () {} // export default function () {} declaration = parseFunctionDeclaration(new Node(), true); return node.finishExportDefaultDeclaration(declaration); } if (matchKeyword('class')) { declaration = parseClassDeclaration(true); return node.finishExportDefaultDeclaration(declaration); } if (matchContextualKeyword('from')) { throwError(Messages.UnexpectedToken, lookahead.value); } // covers: // export default {}; // export default []; // export default (1 + 2); if (match('{')) { expression = parseObjectInitializer(); } else if (match('[')) { expression = parseArrayInitializer(); } else { expression = parseAssignmentExpression(); } consumeSemicolon(); return node.finishExportDefaultDeclaration(expression); } function parseExportAllDeclaration(node) { var src; // covers: // export * from 'foo'; expect('*'); if (!matchContextualKeyword('from')) { throwError(lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); } lex(); src = parseModuleSpecifier(); consumeSemicolon(); return node.finishExportAllDeclaration(src); } function parseExportDeclaration() { var node = new Node(); if (state.inFunctionBody) { throwError(Messages.IllegalExportDeclaration); } expectKeyword('export'); if (matchKeyword('default')) { return parseExportDefaultDeclaration(node); } if (match('*')) { return parseExportAllDeclaration(node); } return parseExportNamedDeclaration(node); } // ECMA-262 15.2.2 Imports function parseImportSpecifier() { // import {} ...; var local, imported, node = new Node(); imported = parseNonComputedProperty(); if (matchContextualKeyword('as')) { lex(); local = parseVariableIdentifier(); } return node.finishImportSpecifier(local, imported); } function parseNamedImports() { var specifiers = []; // {foo, bar as bas} expect('{'); while (!match('}')) { specifiers.push(parseImportSpecifier()); if (!match('}')) { expect(','); if (match('}')) { break; } } } expect('}'); return specifiers; } function parseImportDefaultSpecifier() { // import ...; var local, node = new Node(); local = parseNonComputedProperty(); return node.finishImportDefaultSpecifier(local); } function parseImportNamespaceSpecifier() { // import <* as foo> ...; var local, node = new Node(); expect('*'); if (!matchContextualKeyword('as')) { throwError(Messages.NoAsAfterImportNamespace); } lex(); local = parseNonComputedProperty(); return node.finishImportNamespaceSpecifier(local); } function parseImportDeclaration() { var specifiers = [], src, node = new Node(); if (state.inFunctionBody) { throwError(Messages.IllegalImportDeclaration); } expectKeyword('import'); if (lookahead.type === Token.StringLiteral) { // import 'foo'; src = parseModuleSpecifier(); } else { if (match('{')) { // import {bar} specifiers = specifiers.concat(parseNamedImports()); } else if (match('*')) { // import * as foo specifiers.push(parseImportNamespaceSpecifier()); } else if (isIdentifierName(lookahead) && !matchKeyword('default')) { // import foo specifiers.push(parseImportDefaultSpecifier()); if (match(',')) { lex(); if (match('*')) { // import foo, * as foo specifiers.push(parseImportNamespaceSpecifier()); } else if (match('{')) { // import foo, {bar} specifiers = specifiers.concat(parseNamedImports()); } else { throwUnexpectedToken(lookahead); } } } else { throwUnexpectedToken(lex()); } if (!matchContextualKeyword('from')) { throwError(lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); } lex(); src = parseModuleSpecifier(); } consumeSemicolon(); return node.finishImportDeclaration(specifiers, src); } // ECMA-262 15.1 Scripts function parseScriptBody() { var statement, body = [], token, directive, firstRestricted; while (startIndex < length) { token = lookahead; if (token.type !== Token.StringLiteral) { break; } statement = parseStatementListItem(); body.push(statement); if (statement.expression.type !== Syntax.Literal) { // this is not directive break; } directive = source.slice(token.start + 1, token.end - 1); if (directive === 'use strict') { strict = true; if (firstRestricted) { tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral); } } else { if (!firstRestricted && token.octal) { firstRestricted = token; } } } while (startIndex < length) { statement = parseStatementListItem(); /* istanbul ignore if */ if (typeof statement === 'undefined') { break; } body.push(statement); } return body; } function parseProgram() { var body, node; peek(); node = new Node(); body = parseScriptBody(); return node.finishProgram(body, state.sourceType); } function filterTokenLocation() { var i, entry, token, tokens = []; for (i = 0; i < extra.tokens.length; ++i) { entry = extra.tokens[i]; token = { type: entry.type, value: entry.value }; if (entry.regex) { token.regex = { pattern: entry.regex.pattern, flags: entry.regex.flags }; } if (extra.range) { token.range = entry.range; } if (extra.loc) { token.loc = entry.loc; } tokens.push(token); } extra.tokens = tokens; } function tokenize(code, options, delegate) { var toString, tokens; toString = String; if (typeof code !== 'string' && !(code instanceof String)) { code = toString(code); } source = code; index = 0; lineNumber = (source.length > 0) ? 1 : 0; lineStart = 0; startIndex = index; startLineNumber = lineNumber; startLineStart = lineStart; length = source.length; lookahead = null; state = { allowIn: true, allowYield: true, labelSet: {}, inFunctionBody: false, inIteration: false, inSwitch: false, lastCommentStart: -1, curlyStack: [] }; extra = {}; // Options matching. options = options || {}; // Of course we collect tokens here. options.tokens = true; extra.tokens = []; extra.tokenValues = []; extra.tokenize = true; extra.delegate = delegate; // The following two fields are necessary to compute the Regex tokens. extra.openParenToken = -1; extra.openCurlyToken = -1; extra.range = (typeof options.range === 'boolean') && options.range; extra.loc = (typeof options.loc === 'boolean') && options.loc; if (typeof options.comment === 'boolean' && options.comment) { extra.comments = []; } if (typeof options.tolerant === 'boolean' && options.tolerant) { extra.errors = []; } try { peek(); if (lookahead.type === Token.EOF) { return extra.tokens; } lex(); while (lookahead.type !== Token.EOF) { try { lex(); } catch (lexError) { if (extra.errors) { recordError(lexError); // We have to break on the first error // to avoid infinite loops. break; } else { throw lexError; } } } tokens = extra.tokens; if (typeof extra.errors !== 'undefined') { tokens.errors = extra.errors; } } catch (e) { throw e; } finally { extra = {}; } return tokens; } function parse(code, options) { var program, toString; toString = String; if (typeof code !== 'string' && !(code instanceof String)) { code = toString(code); } source = code; index = 0; lineNumber = (source.length > 0) ? 1 : 0; lineStart = 0; startIndex = index; startLineNumber = lineNumber; startLineStart = lineStart; length = source.length; lookahead = null; state = { allowIn: true, allowYield: true, labelSet: {}, inFunctionBody: false, inIteration: false, inSwitch: false, lastCommentStart: -1, curlyStack: [], sourceType: 'script' }; strict = false; extra = {}; if (typeof options !== 'undefined') { extra.range = (typeof options.range === 'boolean') && options.range; extra.loc = (typeof options.loc === 'boolean') && options.loc; extra.attachComment = (typeof options.attachComment === 'boolean') && options.attachComment; if (extra.loc && options.source !== null && options.source !== undefined) { extra.source = toString(options.source); } if (typeof options.tokens === 'boolean' && options.tokens) { extra.tokens = []; } if (typeof options.comment === 'boolean' && options.comment) { extra.comments = []; } if (typeof options.tolerant === 'boolean' && options.tolerant) { extra.errors = []; } if (extra.attachComment) { extra.range = true; extra.comments = []; extra.bottomRightStack = []; extra.trailingComments = []; extra.leadingComments = []; } if (options.sourceType === 'module') { // very restrictive condition for now state.sourceType = options.sourceType; strict = true; } } try { program = parseProgram(); if (typeof extra.comments !== 'undefined') { program.comments = extra.comments; } if (typeof extra.tokens !== 'undefined') { filterTokenLocation(); program.tokens = extra.tokens; } if (typeof extra.errors !== 'undefined') { program.errors = extra.errors; } } catch (e) { throw e; } finally { extra = {}; } return program; } // Sync with *.json manifests. exports.version = '2.7.3'; exports.tokenize = tokenize; exports.parse = parse; // Deep copy. /* istanbul ignore next */ exports.Syntax = (function () { var name, types = {}; if (typeof Object.create === 'function') { types = Object.create(null); } for (name in Syntax) { if (Syntax.hasOwnProperty(name)) { types[name] = Syntax[name]; } } if (typeof Object.freeze === 'function') { Object.freeze(types); } return types; }()); })); /* vim: set sw=4 ts=4 et tw=80 : */ ================================================ FILE: src/robomongo/gui/resources/scripts/uuidhelpers.js ================================================ // Javascript helper functions for parsing and displaying UUIDs in the MongoDB shell. // This is a temporary solution until SERVER-3153 is implemented. // To create BinData values corresponding to the various driver encodings use: // var s = "{00112233-4455-6677-8899-aabbccddeeff}"; // var uuid = UUID(s); // new Standard encoding // var juuid = JUUID(s); // JavaLegacy encoding // var nuuid = NUUID(s); // CSharpLegacy encoding // var pyuuid = PYUUID(s); // PythonLegacy encoding function HexToBase64(hex) { var base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var base64 = ""; var group; for (var i = 0; i < 30; i += 6) { group = parseInt(hex.substr(i, 6), 16); base64 += base64Digits[(group >> 18) & 0x3f]; base64 += base64Digits[(group >> 12) & 0x3f]; base64 += base64Digits[(group >> 6) & 0x3f]; base64 += base64Digits[group & 0x3f]; } group = parseInt(hex.substr(30, 2), 16); base64 += base64Digits[(group >> 2) & 0x3f]; base64 += base64Digits[(group << 4) & 0x3f]; base64 += "=="; return base64; } function Base64ToHex(base64) { var base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var hexDigits = "0123456789abcdef"; var hex = ""; for (var i = 0; i < 24; ) { var e1 = base64Digits.indexOf(base64[i++]); var e2 = base64Digits.indexOf(base64[i++]); var e3 = base64Digits.indexOf(base64[i++]); var e4 = base64Digits.indexOf(base64[i++]); var c1 = (e1 << 2) | (e2 >> 4); var c2 = ((e2 & 15) << 4) | (e3 >> 2); var c3 = ((e3 & 3) << 6) | e4; hex += hexDigits[c1 >> 4]; hex += hexDigits[c1 & 15]; if (e3 != 64) { hex += hexDigits[c2 >> 4]; hex += hexDigits[c2 & 15]; } if (e4 != 64) { hex += hexDigits[c3 >> 4]; hex += hexDigits[c3 & 15]; } } return hex; } // Legacy UUID in unspecified encoding function LUUID(uuid) { var hex = uuid.replace(/[{}-]/g, ""); // remove extra characters var base64 = HexToBase64(hex); return new BinData(3, base64); } function JUUID(uuid) { var hex = uuid.replace(/[{}-]/g, ""); // remove extra characters var msb = hex.substr(0, 16); var lsb = hex.substr(16, 16); msb = msb.substr(14, 2) + msb.substr(12, 2) + msb.substr(10, 2) + msb.substr(8, 2) + msb.substr(6, 2) + msb.substr(4, 2) + msb.substr(2, 2) + msb.substr(0, 2); lsb = lsb.substr(14, 2) + lsb.substr(12, 2) + lsb.substr(10, 2) + lsb.substr(8, 2) + lsb.substr(6, 2) + lsb.substr(4, 2) + lsb.substr(2, 2) + lsb.substr(0, 2); hex = msb + lsb; var base64 = HexToBase64(hex); return new BinData(3, base64); } function NUUID(uuid) { var hex = uuid.replace(/[{}-]/g, ""); // remove extra characters var a = hex.substr(6, 2) + hex.substr(4, 2) + hex.substr(2, 2) + hex.substr(0, 2); var b = hex.substr(10, 2) + hex.substr(8, 2); var c = hex.substr(14, 2) + hex.substr(12, 2); var d = hex.substr(16, 16); hex = a + b + c + d; var base64 = HexToBase64(hex); return new BinData(3, base64); } function PYUUID(uuid) { var hex = uuid.replace(/[{}-]/g, ""); // remove extra characters var base64 = HexToBase64(hex); return new BinData(3, base64); } ================================================ FILE: src/robomongo/gui/utils/ComboBoxUtils.cpp ================================================ #include "robomongo/gui/utils/ComboBoxUtils.h" namespace Robomongo { namespace utils { /** * @brief This function behaves identically to Qt5 QComboBox::setCurrentText(). * We are using this function in order to support Qt4 */ void setCurrentText(QComboBox *comboBox, const QString &text) { if (comboBox->isEditable()) { comboBox->setEditText(text); } else { const int i = comboBox->findText(text); if (i > -1) comboBox->setCurrentIndex(i); } } } } ================================================ FILE: src/robomongo/gui/utils/ComboBoxUtils.h ================================================ #pragma once #include namespace Robomongo { namespace utils { /** * @brief This function behaves identically to Qt5 QComboBox::setCurrentText(). * We are using this function in order to support Qt4 */ void setCurrentText(QComboBox *comboBox, const QString &text); } } ================================================ FILE: src/robomongo/gui/utils/DialogUtils.cpp ================================================ #include "robomongo/gui/utils/DialogUtils.h" namespace Robomongo { namespace utils { namespace { const QString titleTemaple = QString("%1 %2"); const QString textTemaple = QString("%1 %3 %2?"); } int questionDialog(QWidget *parent, const QString &actionText, const QString &itemText, const QString& valueText) { return questionDialog(parent, actionText, itemText, textTemaple, valueText); } int questionDialog(QWidget *parent, const QString &actionText, const QString &itemText, const QString &templateText, const QString &valueText) { return QMessageBox::question(parent, titleTemaple.arg(actionText).arg(itemText), templateText.arg(actionText).arg(itemText.toLower()).arg(valueText), QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton); } } } ================================================ FILE: src/robomongo/gui/utils/DialogUtils.h ================================================ #pragma once #include namespace Robomongo { namespace utils { int questionDialog(QWidget *parent, const QString &actionText, const QString &itemText, const QString &valueText); int questionDialog(QWidget *parent, const QString &actionText, const QString &itemText, const QString &templateText, const QString &valueText); } } ================================================ FILE: src/robomongo/gui/utils/GuiConstants.h ================================================ #pragma once namespace Robomongo { namespace HighDpiConstants { int const WIN_HIGH_DPI_BUTTON_HEIGHT = 23; int const MACOS_HIGH_DPI_BUTTON_HEIGHT = 34; } enum AuthMechanism { SCRAM_SHA_1, SCRAM_SHA_256, MONGODB_CR }; inline AuthMechanism authMechanismFromStr(std::string_view str) { if (str == "SCRAM-SHA-1") return SCRAM_SHA_1; else if (str == "SCRAM-SHA-256") return SCRAM_SHA_256; else if (str == "MONGODB-CR") return MONGODB_CR; else return SCRAM_SHA_1; } } /* end of Robomongo namespace */ ================================================ FILE: src/robomongo/gui/widgets/LogWidget.cpp ================================================ #include "robomongo/gui/widgets/LogWidget.h" #include #include #include #include #include #include #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { LogWidget::LogWidget(QWidget* parent) : BaseClass(parent), _logTextEdit(new QTextEdit(this)) { _logTextEdit->setReadOnly(true); _logTextEdit->setContextMenuPolicy(Qt::CustomContextMenu); VERIFY(connect(_logTextEdit, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint &)))); QHBoxLayout *hlayout = new QHBoxLayout; hlayout->setContentsMargins(0, 0, 0, 0); hlayout->addWidget(_logTextEdit); _clear = new QAction("Clear All", this); VERIFY(connect(_clear, SIGNAL(triggered()), _logTextEdit, SLOT(clear()))); setLayout(hlayout); } void LogWidget::showContextMenu(const QPoint &pt) { QMenu *menu = _logTextEdit->createStandardContextMenu(); menu->addAction(_clear); _clear->setEnabled(!_logTextEdit->toPlainText().isEmpty()); menu->exec(_logTextEdit->mapToGlobal(pt)); delete menu; } void LogWidget::addMessage(const QString &message, mongo::logger::LogSeverity level) { // Print time QTime time = QTime::currentTime(); _logTextEdit->moveCursor (QTextCursor::End); _logTextEdit->setTextColor(QColor("#aaaaaa")); _logTextEdit->insertPlainText(time.toString("h:mm:ss AP") + "\t"); // Print message _logTextEdit->moveCursor (QTextCursor::End); // Nice color for the future: "#CD9800" :) QColor textColor = QColor(Qt::black); if (level == mongo::logger::LogSeverity::Error()) textColor = QColor("#CD0000"); else if (level == mongo::logger::LogSeverity::Log()) textColor = QColor("#777777"); else if (level == mongo::logger::LogSeverity::Warning()) textColor = QColor("#CD9800"); _logTextEdit->setTextColor(textColor); const int maxLength = 500; if (message.length() <= maxLength) { _logTextEdit->insertPlainText(message.trimmed() + "\n"); } else { _logTextEdit->insertPlainText(QString("(truncated) ") + message.left(maxLength).trimmed() + "...\n"); } // Scroll to the bottom QScrollBar *sb = _logTextEdit->verticalScrollBar(); sb->setValue(sb->maximum()); } } ================================================ FILE: src/robomongo/gui/widgets/LogWidget.h ================================================ #pragma once #include #include QT_BEGIN_NAMESPACE class QTextEdit; class QAction; QT_END_NAMESPACE namespace Robomongo { class LogWidget : public QWidget { Q_OBJECT public: typedef QWidget BaseClass; LogWidget(QWidget* parent = 0); public Q_SLOTS: void addMessage(const QString &message, mongo::logger::LogSeverity level); private Q_SLOTS: void showContextMenu(const QPoint &pt); private: QTextEdit *const _logTextEdit; QAction *_clear; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/AddEditIndexDialog.cpp ================================================ #include "robomongo/gui/widgets/explorer/AddEditIndexDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h" #include "robomongo/gui/editors/JSLexer.h" #include "robomongo/gui/editors/FindFrame.h" #include "robomongo/gui/editors/PlainJavaScriptEditor.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/domain/MongoCollection.h" #include "robomongo/core/utils/QtUtils.h" #include namespace { bool isValidJson(const QString &text) { bool result = false; if (!text.isEmpty()) { try { mongo::Robomongo::fromjson(text.toUtf8()); result = true; } catch (const std::exception &) { } } return result; } QLabel *createHelpLabel(const QString &text, int marginLeft = 0, int marginTop = 0, int marginRight = 0, int marginBottom = 0) { QLabel *helpLabel = new QLabel(text); helpLabel->setWordWrap(true); helpLabel->setContentsMargins(marginLeft, marginTop, marginRight, marginBottom); QPalette palette = helpLabel->palette(); palette.setColor(QPalette::WindowText, QColor(110, 110, 110)); helpLabel->setPalette(palette); return helpLabel; } Robomongo::FindFrame *createFindFrame(QWidget *parent = NULL, const QString &text = QString()) { const QFont &textFont = Robomongo::GuiRegistry::instance().font(); QsciLexerJavaScript *javaScriptLexer = new Robomongo::JSLexer(parent); javaScriptLexer->setFont(textFont); Robomongo::FindFrame *findFrame = new Robomongo::FindFrame(parent); findFrame->sciScintilla()->setLexer(javaScriptLexer); findFrame->sciScintilla()->setTabWidth(4); findFrame->sciScintilla()->setAppropriateBraceMatching(); findFrame->sciScintilla()->setFont(textFont); findFrame->sciScintilla()->setStyleSheet("QFrame {background-color: rgb(73, 76, 78); border: 1px solid #c7c5c4; border-radius: 4px; margin: 0px; padding: 0px;}"); findFrame->sciScintilla()->setText(text); findFrame->setMaximumHeight(120); return findFrame; } } namespace Robomongo { AddEditIndexDialog::AddEditIndexDialog( const IndexInfo &info, const QString &databaseName, const QString &serverAdress, bool const isAddIndex, QWidget *parent) : BaseClass(parent), _info(info), _isAddIndex(isAddIndex) { setWindowTitle("Index Properties"); Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), serverAdress); Indicator *collectionIndicator = new Indicator(GuiRegistry::instance().collectionIcon(), QtUtils::toQString(_info._collection.name())); Indicator *databaseIndicator = new Indicator(GuiRegistry::instance().databaseIcon(), databaseName); QHBoxLayout *hlayout = new QHBoxLayout; hlayout->setContentsMargins(2, 0, 5, 1); hlayout->setSpacing(0); hlayout->addWidget(serverIndicator, 0, Qt::AlignLeft); hlayout->addWidget(databaseIndicator, 0, Qt::AlignLeft); hlayout->addWidget(collectionIndicator, 0, Qt::AlignLeft); hlayout->addStretch(1); QTabWidget *mainTab = new QTabWidget(this); mainTab->addTab(createBasicTab(), tr("Basic")); mainTab->addTab(createAdvancedTab(), tr("Advanced")); mainTab->addTab(createTextSearchTab(), tr("Text Search")); mainTab->setTabsClosable(false); QDialogButtonBox *buttonBox = new QDialogButtonBox (this); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); VERIFY(connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()))); VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()))); QVBoxLayout *vlayout = new QVBoxLayout; vlayout->addLayout(hlayout); vlayout->addSpacing(5); vlayout->addWidget(mainTab); vlayout->addWidget(buttonBox); setLayout(vlayout); _nameLineEdit->setFocus(); } QWidget* AddEditIndexDialog::createBasicTab() { QWidget *basicTab = new QWidget(this); _nameLineEdit = new QLineEdit(QtUtils::toQString(_info._name), basicTab); _nameLineEdit->setFocus(); _jsonText = createFindFrame(basicTab, QtUtils::toQString(_info._keys)); _uniqueCheckBox = new QCheckBox(tr("Unique")); _uniqueCheckBox->setChecked(_info._unique); QLabel *nameHelpLabel = createHelpLabel( "Choose any name that will help you to identify this index.", 0, -2, 0, 15); QLabel *keyHelpLabel = createHelpLabel( "Document that contains pairs with the name of the field or fields to index " "and order of the index. A 1 specifies ascending and a -1 specifies descending.", 0, -2, 0, 20); QLabel *uniqueHelpLabel = createHelpLabel( "If set, creates a unique index so that the collection will not accept insertion " "of documents where the index key or keys match an existing value in the index.", 20, -2, 0, 20); QGridLayout *layout = new QGridLayout; layout->addWidget(new QLabel("Name: "), 0, 0); layout->addWidget(_nameLineEdit, 0, 1); layout->addWidget(nameHelpLabel, 1, 1, Qt::AlignTop); layout->addWidget(new QLabel("Keys: "), 2, 0, Qt::AlignTop); layout->addWidget(_jsonText, 2, 1, Qt::AlignTop); layout->addWidget(keyHelpLabel, 3, 1, Qt::AlignTop); layout->addWidget(_uniqueCheckBox, 4, 0, 1, 2); layout->addWidget(uniqueHelpLabel, 5, 0, 1, 2, Qt::AlignTop); layout->setAlignment(Qt::AlignTop); basicTab->setLayout(layout); return basicTab; } void AddEditIndexDialog::expireStateChanged(int value) { _expireAfterLineEdit->setEnabled(value); if (!value) { _expireAfterLineEdit->setText(""); } } QWidget* AddEditIndexDialog::createAdvancedTab() { QWidget *advanced = new QWidget(this); _sparceCheckBox = new QCheckBox(tr("Sparse"), advanced); _sparceCheckBox->setChecked(_info._sparse); _backGroundCheckBox = new QCheckBox(tr("Create index in background"), advanced); _backGroundCheckBox->setChecked(_info._backGround); QHBoxLayout *expireLayout = new QHBoxLayout; _expireAfterLineEdit = new QLineEdit(advanced); _expireAfterLineEdit->setMaximumWidth(150); QRegExp rx("\\d+"); _expireAfterLineEdit->setValidator(new QRegExpValidator(rx, this)); QLabel *secLabel = new QLabel(tr("seconds"), advanced); expireLayout->addWidget(_expireAfterLineEdit); expireLayout->addWidget(secLabel); expireLayout->addStretch(1); QCheckBox *expireCheckBox = new QCheckBox(tr("Expire after")); expireCheckBox->setChecked(false); if (_info._ttl >= 0) { expireCheckBox->setChecked(true); _expireAfterLineEdit->setText(QString("%1").arg(_info._ttl)); } expireStateChanged(expireCheckBox->checkState()); VERIFY(connect(expireCheckBox, SIGNAL(stateChanged(int)), this, SLOT(expireStateChanged(int)))); QLabel *sparseHelpLabel = createHelpLabel( "If set, the index only references documents with the specified field. " "These indexes use less space but behave differently in some situations (particularly sorts).", 20, -2, 0, 20); QLabel *backgroundHelpLabel = createHelpLabel( "Builds the index in the background so that building an index does not block other database activities.", 20, -2, 0, 20); QLabel *expireHelpLabel = createHelpLabel( "Specifies a time to live, in seconds, to control how long MongoDB retains documents in this collection", 20, -2, 0, 20); QGridLayout *layout = new QGridLayout; layout->addWidget(_sparceCheckBox, 0, 0, 1, 2); layout->addWidget(sparseHelpLabel, 1, 0, 1, 2); layout->addWidget(_backGroundCheckBox, 2, 0, 1, 2); layout->addWidget(backgroundHelpLabel, 3, 0, 1, 2); layout->addWidget(expireCheckBox, 4, 0); layout->addLayout(expireLayout, 4, 1); layout->addWidget(expireHelpLabel, 5, 0, 1, 2); layout->setAlignment(Qt::AlignTop); advanced->setLayout(layout); return advanced; } QWidget* AddEditIndexDialog::createTextSearchTab() { QWidget *textSearch = new QWidget(this); _defaultLanguageLineEdit = new QLineEdit(QtUtils::toQString(_info._defaultLanguage), textSearch); _languageOverrideLineEdit = new QLineEdit(QtUtils::toQString(_info._languageOverride), textSearch); _textWeightsLineEdit = createFindFrame(textSearch, QtUtils::toQString(_info._textWeights)); QLabel *defaultLanguageHelpLabel = createHelpLabel( "For a text index, the language that determines the list of stop words and the rules for the stemmer and tokenizer. The default value is english", 0, -2, 0, 20); QLabel *languageOverrideHelpLabel = createHelpLabel( "For a text index, specify the name of the field in the document that contains, for that document, the language to override the default language. The default value is language", 0, -2, 0, 20); QLabel *textWeightsHelpLabel = createHelpLabel( "Document that contains field and weight pairs. The weight is a number ranging from 1 to 99,999 " "and denotes the significance of the field relative to the other indexed fields. ", 0, -2, 0, 20); QGridLayout *layout = new QGridLayout; layout->addWidget(new QLabel(tr("Default language:")), 0, 0); layout->addWidget(_defaultLanguageLineEdit, 0, 1); layout->addWidget(defaultLanguageHelpLabel, 1, 1); layout->addWidget(new QLabel(tr("Language override:")), 2, 0, Qt::AlignTop); layout->addWidget(_languageOverrideLineEdit, 2, 1); layout->addWidget(languageOverrideHelpLabel, 3, 1); layout->addWidget(new QLabel(tr("Text weights")), 4, 0, Qt::AlignTop); layout->addWidget(_textWeightsLineEdit, 4, 1, Qt::AlignTop); layout->addWidget(textWeightsHelpLabel, 5, 1); layout->setAlignment(Qt::AlignTop); textSearch->setLayout(layout); return textSearch; } IndexInfo AddEditIndexDialog::info() const { const QString &expAft = _expireAfterLineEdit->text(); int expAftInt = _info._ttl; if (!expAft.isEmpty()) { expAftInt = _expireAfterLineEdit->text().toInt(); } return IndexInfo( _info._collection, _nameLineEdit->text().toStdString(), _jsonText->sciScintilla()->text().toStdString(), _uniqueCheckBox->checkState() == Qt::Checked, _backGroundCheckBox->checkState() == Qt::Checked, _sparceCheckBox->checkState() == Qt::Checked, expAftInt, QtUtils::toStdString(_defaultLanguageLineEdit->text()), QtUtils::toStdString(_languageOverrideLineEdit->text()), QtUtils::toStdString(_textWeightsLineEdit->sciScintilla()->text())); } void AddEditIndexDialog::accept() { if (isValidJson(_jsonText->sciScintilla()->text())) { const QString &weightText = _textWeightsLineEdit->sciScintilla()->text(); if (!weightText.isEmpty() && !isValidJson(weightText)) { QMessageBox::warning(this, "Invalid json", "Please check json text.\n"); _textWeightsLineEdit->setFocus(); return ; } if (!_isAddIndex) { // Ask user int const answer = QMessageBox::question(this, "Warning", QString("MongoDB does not support direct (one step) edit index. " "\nTo edit an existing index, the index must be dropped and recreated. " "This means if the recreate step fails, the index being edited might " "have already been dropped. " "In this case, Robo 3T will try to recover (recreate) the index being edited. " "Please consider backing up your index first. " "\n\nAre you sure you want to proceed?" ), QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton ); if (answer == QMessageBox::No) return; } return BaseClass::accept(); } else { QMessageBox::warning(this, "Invalid json", "Please check json text.\n"); _jsonText->setFocus(); } } } ================================================ FILE: src/robomongo/gui/widgets/explorer/AddEditIndexDialog.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QLineEdit; class QCheckBox; class QTextEdit; QT_END_NAMESPACE #include "robomongo/core/events/MongoEventsInfo.h" namespace Robomongo { class FindFrame; class AddEditIndexDialog: public QDialog { Q_OBJECT public: typedef QDialog BaseClass; enum { HeightWidget = 320, WidthWidget = 480 }; explicit AddEditIndexDialog( const IndexInfo &info, const QString &databaseName, const QString &serverAdress, bool const isAddIndex, QWidget *parent = nullptr ); IndexInfo info() const; public Q_SLOTS: virtual void accept(); void expireStateChanged(int value); private: QWidget *createBasicTab(); QWidget *createAdvancedTab(); QWidget *createTextSearchTab(); bool const _isAddIndex; IndexInfo const _info; QLineEdit *_nameLineEdit; FindFrame *_jsonText; QCheckBox *_uniqueCheckBox; QCheckBox *_backGroundCheckBox; QCheckBox *_sparceCheckBox; QLineEdit *_expireAfterLineEdit; QLineEdit *_defaultLanguageLineEdit; QLineEdit *_languageOverrideLineEdit; FindFrame *_textWeightsLineEdit; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerCollectionIndexItem.cpp ================================================ #include "ExplorerCollectionIndexItem.h" #include #include #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/utils/DialogUtils.h" #include "robomongo/gui/widgets/explorer/AddEditIndexDialog.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionIndexesDir.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h" namespace Robomongo { ExplorerCollectionIndexItem::ExplorerCollectionIndexItem( ExplorerCollectionIndexesDir *parent, const IndexInfo &info) : BaseClass(parent), _info(info) { auto dropIndex = new QAction("Drop Index...", this); connect(dropIndex, SIGNAL(triggered()), SLOT(ui_dropIndex())); auto editIndex = new QAction("Edit Index...", this); connect(editIndex, SIGNAL(triggered()), SLOT(ui_edit())); BaseClass::_contextMenu->addAction(editIndex); BaseClass::_contextMenu->addAction(dropIndex); setText(0, QtUtils::toQString(_info._name)); setIcon(0, Robomongo::GuiRegistry::instance().indexIcon()); } void ExplorerCollectionIndexItem::ui_dropIndex() { // Ask user auto const answer = utils::questionDialog(treeWidget(), "Drop", "Index", text(0)); if (answer != QMessageBox::Yes) return; auto const par = dynamic_cast(parent()); if (!par) return; auto const grandParent = dynamic_cast(par->parent()); if (!grandParent) return; grandParent->dropIndex(this); } void ExplorerCollectionIndexItem::ui_edit() { auto const par = dynamic_cast(parent()); if (par) { auto const grPar = dynamic_cast(par->parent()); if (!grPar) return; auto const& db { grPar->databaseItem()->database() }; AddEditIndexDialog dlg { _info, QtUtils::toQString(db->name()), QtUtils::toQString(db->server()->connectionRecord()->getFullAddress()), false, treeWidget() }; auto const result = dlg.exec(); if (result != QDialog::Accepted) return; auto const databaseTreeItem = dynamic_cast(grPar->databaseItem()); if (!databaseTreeItem) return; databaseTreeItem->addEditIndex(grPar, _info, dlg.info()); } } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerCollectionIndexItem.h ================================================ #pragma once #include "robomongo/core/events/MongoEventsInfo.h" #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" namespace Robomongo { class ExplorerCollectionIndexesDir; class ExplorerCollectionIndexItem : public ExplorerTreeItem { Q_OBJECT public: using BaseClass = ExplorerTreeItem ; explicit ExplorerCollectionIndexItem( ExplorerCollectionIndexesDir *parent, const IndexInfo &info); private Q_SLOTS: void ui_dropIndex(); void ui_edit(); private: IndexInfo _info; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerCollectionIndexesDir.cpp ================================================ #include "ExplorerCollectionIndexesDir.h" #include #include #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/widgets/explorer/AddEditIndexDialog.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h" namespace Robomongo { ExplorerCollectionIndexesDir::ExplorerCollectionIndexesDir(QTreeWidgetItem *parent) :BaseClass(parent) { QAction *addIndex = new QAction("Add Index...", this); VERIFY(connect(addIndex, SIGNAL(triggered()), SLOT(ui_addIndex()))); QAction *reIndex = new QAction("Rebuild Indexes...", this); VERIFY(connect(reIndex, SIGNAL(triggered()), SLOT(ui_reIndex()))); QAction *viewIndex = new QAction("View Indexes", this); VERIFY(connect(viewIndex, SIGNAL(triggered()), SLOT(ui_viewIndex()))); QAction *refreshIndex = new QAction("Refresh", this); VERIFY(connect(refreshIndex, SIGNAL(triggered()), SLOT(ui_refreshIndex()))); BaseClass::_contextMenu->addAction(viewIndex); BaseClass::_contextMenu->addAction(addIndex); BaseClass::_contextMenu->addAction(reIndex); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(refreshIndex); setText(0, "Indexes"); setIcon(0, Robomongo::GuiRegistry::instance().folderIcon()); setExpanded(false); setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); } void ExplorerCollectionIndexesDir::expand() { auto const par = dynamic_cast(parent()); if (!par) return; par->expand(); } void ExplorerCollectionIndexesDir::ui_viewIndex() { auto const par = dynamic_cast(parent()); if (par) par->openCurrentCollectionShell("getIndexes()"); } void ExplorerCollectionIndexesDir::ui_refreshIndex() { auto const par = dynamic_cast(parent()); if (par) par->expand(); } void ExplorerCollectionIndexesDir::ui_addIndex() { auto par = dynamic_cast(parent()); if (!par) return; IndexInfo const fakeInfo(par->collection()->info(), ""); auto const& db { par->databaseItem()->database() }; AddEditIndexDialog dlg { fakeInfo, QtUtils::toQString(db->name()), QtUtils::toQString(db->server()->connectionRecord()->getFullAddress()), true, treeWidget() }; auto const result = dlg.exec(); if (result != QDialog::Accepted) return; auto const databaseTreeItem = dynamic_cast(par->databaseItem()); if (!databaseTreeItem) return; databaseTreeItem->addEditIndex(par, fakeInfo, dlg.info()); } void ExplorerCollectionIndexesDir::ui_reIndex() { auto const par = dynamic_cast(parent()); if (par) par->openCurrentCollectionShell("reIndex()", false); } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerCollectionIndexesDir.h ================================================ #pragma once #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" namespace Robomongo { class ExplorerCollectionIndexesDir : public ExplorerTreeItem { Q_OBJECT public: using BaseClass = ExplorerTreeItem; static const QString labelText; explicit ExplorerCollectionIndexesDir(QTreeWidgetItem *parent); void expand(); private Q_SLOTS: void ui_addIndex(); void ui_reIndex(); void ui_viewIndex(); void ui_refreshIndex(); }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h" #include #include #include "robomongo/gui/widgets/explorer/AddEditIndexDialog.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionIndexesDir.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionIndexItem.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h" #include "robomongo/gui/dialogs/CreateDatabaseDialog.h" #include "robomongo/gui/dialogs/CopyCollectionDialog.h" #include "robomongo/gui/dialogs/DocumentTextEditor.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/utils/DialogUtils.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/domain/MongoCollection.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" namespace { const char *tooltipTemplate = "%s " "" "" "" "
Count:   %lld
Size:  %s
" ; } namespace Robomongo { R_REGISTER_EVENT(CollectionIndexesLoadingEvent) ExplorerCollectionTreeItem::ExplorerCollectionTreeItem( QTreeWidgetItem *parent, ExplorerDatabaseTreeItem *databaseItem, MongoCollection *collection) : BaseClass(parent), _collection(collection), _databaseItem(databaseItem) { QAction *addDocument = new QAction("Insert Document...", this); VERIFY(connect(addDocument, SIGNAL(triggered()), SLOT(ui_addDocument()))); QAction *updateDocument = new QAction("Update Documents...", this); VERIFY(connect(updateDocument, SIGNAL(triggered()), SLOT(ui_updateDocument()))); QAction *removeDocument = new QAction("Remove Documents...", this); VERIFY(connect(removeDocument, SIGNAL(triggered()), SLOT(ui_removeDocument()))); QAction *removeAllDocuments = new QAction("Remove All Documents...", this); VERIFY(connect(removeAllDocuments, SIGNAL(triggered()), SLOT(ui_removeAllDocuments()))); QAction *collectionStats = new QAction("Statistics", this); VERIFY(connect(collectionStats, SIGNAL(triggered()), SLOT(ui_collectionStatistics()))); QAction *storageSize = new QAction("Storage Size", this); VERIFY(connect(storageSize, SIGNAL(triggered()), SLOT(ui_storageSize()))); QAction *totalIndexSize = new QAction("Total Index Size", this); VERIFY(connect(totalIndexSize, SIGNAL(triggered()), SLOT(ui_totalIndexSize()))); QAction *totalSize = new QAction("Total Size", this); VERIFY(connect(totalSize, SIGNAL(triggered()), SLOT(ui_totalSize()))); QAction *shardVersion = new QAction("Shard Version", this); VERIFY(connect(shardVersion, SIGNAL(triggered()), SLOT(ui_shardVersion()))); QAction *shardDistribution = new QAction("Shard Distribution", this); VERIFY(connect(shardDistribution, SIGNAL(triggered()), SLOT(ui_shardDistribution()))); QAction *dropCollection = new QAction("Drop Collection...", this); VERIFY(connect(dropCollection, SIGNAL(triggered()), SLOT(ui_dropCollection()))); QAction *renameCollection = new QAction("Rename Collection...", this); VERIFY(connect(renameCollection, SIGNAL(triggered()), SLOT(ui_renameCollection()))); QAction *duplicateCollection = new QAction("Duplicate Collection...", this); VERIFY(connect(duplicateCollection, SIGNAL(triggered()), SLOT(ui_duplicateCollection()))); // Disabling for 0.8.5 release as this is currently a broken misfeature (see discussion on issue #398) // QAction *copyCollectionToDiffrentServer = new QAction("Copy Collection to Database...", this); // VERIFY(connect(copyCollectionToDiffrentServer, SIGNAL(triggered()), SLOT(ui_copyToCollectionToDiffrentServer()))); QAction *viewCollection = new QAction("View Documents", this); VERIFY(connect(viewCollection, SIGNAL(triggered()), SLOT(ui_viewCollection()))); BaseClass::_contextMenu->addAction(viewCollection); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(addDocument); BaseClass::_contextMenu->addAction(updateDocument); BaseClass::_contextMenu->addAction(removeDocument); BaseClass::_contextMenu->addAction(removeAllDocuments); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(renameCollection); BaseClass::_contextMenu->addAction(duplicateCollection); // Disabling for 0.8.5 release as this is currently a broken misfeature (see discussion on issue #398) // BaseClass::_contextMenu->addAction(copyCollectionToDiffrentServer); BaseClass::_contextMenu->addAction(dropCollection); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(collectionStats); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(shardVersion); BaseClass::_contextMenu->addAction(shardDistribution); AppRegistry::instance().bus()->subscribe(_databaseItem, LoadCollectionIndexesResponse::Type, this); AppRegistry::instance().bus()->subscribe(_databaseItem, AddEditIndexResponse::Type, this); AppRegistry::instance().bus()->subscribe(_databaseItem, DropCollectionIndexResponse::Type, this); AppRegistry::instance().bus()->subscribe(this, CollectionIndexesLoadingEvent::Type, this); setText(0, QtUtils::toQString(_collection->name())); setIcon(0, GuiRegistry::instance().collectionIcon()); _indexDir = new ExplorerCollectionIndexesDir(this); addChild(_indexDir); setExpanded(false); setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); } void ExplorerCollectionTreeItem::handle(LoadCollectionIndexesResponse *event) { if (event->isError()) { _indexDir->setText(0, "Indexes"); _indexDir->setExpanded(false); QtUtils::clearChildItems(_indexDir); std::stringstream ss; ss << "Cannot load list of indexes.\n\nError:\n" << event->error().errorMessage(); QMessageBox::information(NULL, "Error", QtUtils::toQString(ss.str())); return; } QtUtils::clearChildItems(_indexDir); const std::vector &indexes = event->indexes(); // Do not expand, when we do not have functions if (indexes.size() == 0) _indexDir->setExpanded(false); for (std::vector::const_iterator it = indexes.begin(); it != indexes.end(); ++it) { _indexDir->addChild(new ExplorerCollectionIndexItem(_indexDir, *it)); } _indexDir->setText(0, detail::buildName("Indexes", _indexDir->childCount())); } void ExplorerCollectionTreeItem::handle(AddEditIndexResponse *event) { bool const isAddIndex{ event->oldIndex_._name.empty() }; QString const action{ isAddIndex ? "add" : "edit" }; auto const index{ QString::fromStdString( isAddIndex ? event->newIndex_._name : event->oldIndex_._name )}; if (event->isError()) { QString const header{ "Operation failed" }; QString const msg{ "Failed to " + action + " index \"" + index + '\"'}; auto const err{ QString::fromStdString(event->error().errorMessage()) }; LOG_MSG((msg + ". " + err).toStdString(), mongo::logger::LogSeverity::Error()); QMessageBox::critical(nullptr, "Error: " + header, msg + "\n\n" + err); return; } LOG_MSG(("Succeeded to " + action + " index \"" + index + '\"').toStdString(), mongo::logger::LogSeverity::Info()); } void ExplorerCollectionTreeItem::handle(DropCollectionIndexResponse *event) { if (event->isError()) { QString const header{"Operation failed"}; QString const msg{ "Failed to drop index \"" + QString::fromStdString(event->index()) + '\"' }; auto const err{ "Reason: " + QString::fromStdString(event->error().errorMessage()) }; LOG_MSG((msg + ". " + err).toStdString(), mongo::logger::LogSeverity::Error()); QMessageBox::critical(nullptr, "Error: " + header, msg + "\n\n" + err); return; } for (int i = 0; i < _indexDir->childCount(); ++i) { QTreeWidgetItem *item = _indexDir->child(i); if (item->text(0) == QString::fromStdString(event->index())) { removeChild(item); delete item; break; } } LOG_MSG("Succeeded to drop index \"" + event->index() + '\"', mongo::logger::LogSeverity::Info()); _indexDir->setText(0, detail::buildName("Indexes", _indexDir->childCount())); } void ExplorerCollectionTreeItem::handle(CollectionIndexesLoadingEvent *event) { _indexDir->setText(0, detail::buildName("Indexes", -1)); } void ExplorerCollectionTreeItem::expand() { AppRegistry::instance().bus()->publish(new CollectionIndexesLoadingEvent(this)); if (_databaseItem) { _databaseItem->expandColection(this); } } void ExplorerCollectionTreeItem::dropIndex(const QTreeWidgetItem * const ind) { if (!_databaseItem) return; _databaseItem->dropIndexFromCollection(this, QtUtils::toStdString(ind->text(0))); } QString ExplorerCollectionTreeItem::buildToolTip(MongoCollection *collection) { // This function does not used now char buff[2048] = {0}; // sprintf(buff,tooltipTemplate,collection->name().c_str(),collection->info().count(),collection->sizeString().c_str()); return buff; } void ExplorerCollectionTreeItem::ui_addDocument() { MongoDatabase *database = _collection->database(); MongoServer *server = database->server(); ConnectionSettings *settings = server->connectionRecord(); DocumentTextEditor editor(CollectionInfo(settings->getFullAddress(), database->name(), _collection->name()), "{\n \n}"); editor.setCursorPosition(1, 4); editor.setWindowTitle("Insert Document"); int result = editor.exec(); treeWidget()->activateWindow(); if (result == QDialog::Accepted) { server->insertDocuments(editor.bsonObj(), MongoNamespace(database->name(), _collection->name()) ); } } void ExplorerCollectionTreeItem::ui_removeDocument() { openCurrentCollectionShell( "remove({ '' : '' });" , false, CursorPosition(0, -10)); } void ExplorerCollectionTreeItem::ui_removeAllDocuments() { MongoDatabase *database = _collection->database(); // Ask user int answer = QMessageBox::question(treeWidget(), "Remove All Documents", QString("Remove all documents from %1 collection?").arg(QtUtils::toQString(_collection->name())), QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton); if (answer == QMessageBox::Yes) { MongoServer *server = database->server(); mongo::BSONObjBuilder builder; mongo::BSONObj bsonQuery = builder.obj(); mongo::Query query(bsonQuery); server->removeDocuments(query, MongoNamespace(database->name(), _collection->name()), RemoveDocumentCount::ALL); } } void ExplorerCollectionTreeItem::ui_updateDocument() { openCurrentCollectionShell( "update(\n" " // query \n" " {\n" " \"key\" : \"value\"\n" " },\n" " \n" " // update \n" " {\n" " },\n" " \n" " // options \n" " {\n" " \"multi\" : false, // update only one document \n" " \"upsert\" : false // insert a new document, if no existing document match the query \n" " }\n" ");", false); } void ExplorerCollectionTreeItem::ui_collectionStatistics() { openCurrentCollectionShell("stats()"); } void ExplorerCollectionTreeItem::ui_dropCollection() { // Ask user int answer = utils::questionDialog(treeWidget(), "Drop", "collection", QtUtils::toQString(_collection->name())); if (answer == QMessageBox::Yes) { MongoDatabase *database = _collection->database(); database->dropCollection(_collection->name()); } } void ExplorerCollectionTreeItem::ui_duplicateCollection() { MongoDatabase *database = _collection->database(); MongoServer *server = database->server(); ConnectionSettings *settings = server->connectionRecord(); CreateDatabaseDialog dlg(QtUtils::toQString(settings->getFullAddress()), QtUtils::toQString(database->name()), QtUtils::toQString(_collection->name()), treeWidget()); dlg.setWindowTitle("Duplicate Collection"); dlg.setOkButtonText("&Duplicate"); dlg.setInputLabelText("New Collection Name:"); dlg.setInputText(QtUtils::toQString(_collection->name() + "_copy")); int result = dlg.exec(); if (result == QDialog::Accepted) { database->duplicateCollection(_collection->name(), QtUtils::toStdString(dlg.databaseName())); } } void ExplorerCollectionTreeItem::ui_copyToCollectionToDiffrentServer() { MongoDatabase *databaseFrom = _collection->database(); MongoServer *server = databaseFrom->server(); ConnectionSettings *settings = server->connectionRecord(); CopyCollection dlg(QtUtils::toQString(settings->getFullAddress()), QtUtils::toQString(databaseFrom->name()), QtUtils::toQString(_collection->name()) ); int result = dlg.exec(); if (result == QDialog::Accepted) { MongoDatabase *databaseTo = dlg.selectedDatabase(); databaseTo->copyCollection(server, databaseFrom->name(), _collection->name()); databaseTo->loadCollections(); } } void ExplorerCollectionTreeItem::ui_renameCollection() { MongoDatabase *database = _collection->database(); MongoServer *server = database->server(); ConnectionSettings *connSettings = server->connectionRecord(); CreateDatabaseDialog dlg(QtUtils::toQString(connSettings->getFullAddress()), QtUtils::toQString(database->name()), QtUtils::toQString(_collection->name()), treeWidget()); dlg.setWindowTitle("Rename Collection"); dlg.setOkButtonText("&Rename"); dlg.setInputLabelText("New Collection Name:"); dlg.setInputText(QtUtils::toQString(_collection->name())); int result = dlg.exec(); if (result == QDialog::Accepted) { database->renameCollection(_collection->name(), QtUtils::toStdString(dlg.databaseName())); } } void ExplorerCollectionTreeItem::ui_viewCollection() { CursorPosition cp(0, -2); openCurrentCollectionShell("find({})", true, cp); } void ExplorerCollectionTreeItem::ui_storageSize() { openCurrentCollectionShell("storageSize()"); } void ExplorerCollectionTreeItem::ui_totalIndexSize() { openCurrentCollectionShell("totalIndexSize()"); } void ExplorerCollectionTreeItem::ui_totalSize() { openCurrentCollectionShell("totalSize()"); } void ExplorerCollectionTreeItem::ui_shardVersion() { openCurrentCollectionShell("getShardVersion()"); } void ExplorerCollectionTreeItem::ui_shardDistribution() { openCurrentCollectionShell("getShardDistribution()"); } void ExplorerCollectionTreeItem::openCurrentCollectionShell(const QString &script, bool execute, const CursorPosition &cursor) { QString query = detail::buildCollectionQuery(_collection->name(), script); AppRegistry::instance().app()->openShell(_collection->database(), query, execute, QtUtils::toQString(_collection->name()), cursor); } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h ================================================ #pragma once #include "robomongo/core/Event.h" #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionIndexesDir.h" #include "robomongo/core/domain/CursorPosition.h" #include "robomongo/core/events/MongoEventsInfo.h" #include "robomongo/core/domain/MongoCollection.h" namespace Robomongo { class LoadCollectionIndexesResponse; struct AddEditIndexResponse; class DropCollectionIndexResponse; class ExplorerCollectionIndexesDir; class ExplorerDatabaseTreeItem; class CollectionIndexesLoadingEvent : public Event { R_EVENT CollectionIndexesLoadingEvent(QObject *sender) : Event(sender) {} }; class ExplorerCollectionTreeItem: public ExplorerTreeItem { Q_OBJECT public: typedef ExplorerTreeItem BaseClass; ExplorerCollectionTreeItem(QTreeWidgetItem *parent, ExplorerDatabaseTreeItem *databaseItem, MongoCollection *collection); MongoCollection *collection() const { return _collection; } void expand(); void dropIndex(const QTreeWidgetItem * const ind); void openCurrentCollectionShell(const QString &script, bool execute = true, const CursorPosition &cursor = CursorPosition()); ExplorerDatabaseTreeItem *const databaseItem() const { return _databaseItem; } public Q_SLOTS: void handle(LoadCollectionIndexesResponse *event); void handle(AddEditIndexResponse *event); void handle(DropCollectionIndexResponse *event); void handle(CollectionIndexesLoadingEvent *event); private Q_SLOTS: void ui_addDocument(); void ui_removeDocument(); void ui_updateDocument(); void ui_collectionStatistics(); void ui_removeAllDocuments(); void ui_storageSize(); void ui_totalIndexSize(); void ui_totalSize(); void ui_shardVersion(); void ui_shardDistribution(); void ui_dropCollection(); void ui_renameCollection(); void ui_duplicateCollection(); void ui_copyToCollectionToDiffrentServer(); void ui_viewCollection(); private: QString buildToolTip(MongoCollection *collection); ExplorerCollectionIndexesDir *_indexDir; MongoCollection *const _collection; ExplorerDatabaseTreeItem *const _databaseItem; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.h" #include #include #include "robomongo/gui/dialogs/FunctionTextEditor.h" #include "robomongo/gui/dialogs/CreateUserDialog.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h" #include "robomongo/gui/dialogs/CreateCollectionDialog.h" #include "robomongo/gui/dialogs/CreateDatabaseDialog.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { void openDatabaseShell(MongoDatabase *database, const QString &script, bool execute = true, const CursorPosition &cursor = CursorPosition()) { AppRegistry::instance().app()->openShell(database, script, execute, QtUtils::toQString(database->name()), cursor); } ExplorerDatabaseCategoryTreeItem::ExplorerDatabaseCategoryTreeItem( ExplorerDatabaseTreeItem *databaseItem, ExplorerDatabaseCategory category) : BaseClass(databaseItem), _category(category) { if (_category == Collections) { QAction *createCollection = new QAction("Create Collection...", this); VERIFY(connect(createCollection, SIGNAL(triggered()), SLOT(ui_createCollection()))); QAction *dbCollectionsStats = new QAction("Collections Statistics", this); VERIFY(connect(dbCollectionsStats, SIGNAL(triggered()), SLOT(ui_dbCollectionsStatistics()))); QAction *refreshCollections = new QAction("Refresh", this); VERIFY(connect(refreshCollections, SIGNAL(triggered()), SLOT(ui_refreshCollections()))); BaseClass::_contextMenu->addAction(dbCollectionsStats); BaseClass::_contextMenu->addAction(createCollection); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(refreshCollections); } else if (_category == Users) { QAction *refreshUsers = new QAction("Refresh", this); VERIFY(connect(refreshUsers, SIGNAL(triggered()), SLOT(ui_refreshUsers()))); QAction *viewUsers = new QAction("View Users", this); VERIFY(connect(viewUsers, SIGNAL(triggered()), SLOT(ui_viewUsers()))); QAction *addUser = new QAction("Add User...", this); VERIFY(connect(addUser, SIGNAL(triggered()), SLOT(ui_addUser()))); BaseClass::_contextMenu->addAction(viewUsers); BaseClass::_contextMenu->addAction(addUser); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(refreshUsers); } else if (_category == Functions) { QAction *refreshFunctions = new QAction("Refresh", this); VERIFY(connect(refreshFunctions, SIGNAL(triggered()), SLOT(ui_refreshFunctions()))); QAction *viewFunctions = new QAction("View Functions", this); VERIFY(connect(viewFunctions, SIGNAL(triggered()), SLOT(ui_viewFunctions()))); QAction *addFunction = new QAction("Add Function...", this); VERIFY(connect(addFunction, SIGNAL(triggered()), SLOT(ui_addFunction()))); BaseClass::_contextMenu->addAction(viewFunctions); BaseClass::_contextMenu->addAction(addFunction); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(refreshFunctions); } setExpanded(false); setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); } void ExplorerDatabaseCategoryTreeItem::expand() { ExplorerDatabaseTreeItem *databaseItem = ExplorerDatabaseCategoryTreeItem::databaseItem(); if (!databaseItem) return; switch(_category) { case Collections: databaseItem->expandCollections(); break; case Files: break; case Functions: databaseItem->expandFunctions(); break; case Users: databaseItem->expandUsers(); break; } } ExplorerDatabaseTreeItem *ExplorerDatabaseCategoryTreeItem::databaseItem() const { return static_cast(parent()); } void ExplorerDatabaseCategoryTreeItem::ui_dbCollectionsStatistics() { ExplorerDatabaseTreeItem *databaseItem = ExplorerDatabaseCategoryTreeItem::databaseItem(); if (databaseItem) { openDatabaseShell(databaseItem->database(), "db.printCollectionStats()"); } } void ExplorerDatabaseCategoryTreeItem::ui_refreshUsers() { ExplorerDatabaseTreeItem *databaseItem = ExplorerDatabaseCategoryTreeItem::databaseItem(); if (databaseItem) { databaseItem->expandUsers(); } } void ExplorerDatabaseCategoryTreeItem::ui_refreshFunctions() { ExplorerDatabaseTreeItem *databaseItem = ExplorerDatabaseCategoryTreeItem::databaseItem(); if (databaseItem) { databaseItem->expandFunctions(); } } void ExplorerDatabaseCategoryTreeItem::ui_viewUsers() { ExplorerDatabaseTreeItem *databaseItem = ExplorerDatabaseCategoryTreeItem::databaseItem(); if (databaseItem) { openDatabaseShell(databaseItem->database(), "db.getUsers()"); } } void ExplorerDatabaseCategoryTreeItem::ui_viewFunctions() { ExplorerDatabaseTreeItem *databaseItem = ExplorerDatabaseCategoryTreeItem::databaseItem(); if (databaseItem) { openDatabaseShell(databaseItem->database(), "db.system.js.find()"); } } void ExplorerDatabaseCategoryTreeItem::ui_createCollection() { ExplorerDatabaseTreeItem *databaseItem = ExplorerDatabaseCategoryTreeItem::databaseItem(); if (!databaseItem) return; const float dbVersion = databaseItem->database()->server()->version(); const std::string& engineName = databaseItem->database()->server()->getStorageEngineType(); const QString& serverName = QtUtils::toQString(databaseItem->database()->server()->connectionRecord()->getFullAddress()); const QString& dbName = QtUtils::toQString(databaseItem->database()->name()); CreateCollectionDialog dlg(serverName, dbVersion, engineName, dbName, QString(), treeWidget()); int result = dlg.exec(); if (result != QDialog::Accepted) return; std::string collectionName = QtUtils::toStdString(dlg.getCollectionName()); databaseItem->database()->createCollection(collectionName, dlg.getSizeInputValue(), dlg.isCapped(), dlg.getMaxDocNumberInputValue(), dlg.getExtraOptions()); } void ExplorerDatabaseCategoryTreeItem::ui_addUser() { ExplorerDatabaseTreeItem *databaseItem = ExplorerDatabaseCategoryTreeItem::databaseItem(); if (!databaseItem) return; std::unique_ptr dlg = nullptr; float const version = databaseItem->database()->server()->version(); if (version < MongoUser::minimumSupportedVersion) { dlg.reset(new CreateUserDialog( QtUtils::toQString(databaseItem->database()->server()->connectionRecord()->getFullAddress()), QtUtils::toQString(databaseItem->database()->name()), MongoUser(version), treeWidget())); } else { dlg.reset(new CreateUserDialog(databaseItem->database()->server()->getDatabasesNames(), QtUtils::toQString(databaseItem->database()->server()->connectionRecord()->getFullAddress()), QtUtils::toQString(databaseItem->database()->name()), MongoUser(version), treeWidget())); } if (dlg->exec() != QDialog::Accepted) return; MongoUser user = dlg->user(); databaseItem->database()->createUser(user); } void ExplorerDatabaseCategoryTreeItem::ui_addFunction() { ExplorerDatabaseTreeItem *databaseItem = ExplorerDatabaseCategoryTreeItem::databaseItem(); if (!databaseItem) return; FunctionTextEditor dlg( QtUtils::toQString(databaseItem->database()->server()->connectionRecord()->getFullAddress()), QtUtils::toQString(databaseItem->database()->name()), MongoFunction()); dlg.setWindowTitle("Create Function"); dlg.setCode( "function() {\n" " // write your code here\n" "}"); if (dlg.exec() != QDialog::Accepted) return; MongoFunction function = dlg.function(); databaseItem->database()->createFunction(function); } void ExplorerDatabaseCategoryTreeItem::ui_refreshCollections() { ExplorerDatabaseTreeItem *databaseItem = ExplorerDatabaseCategoryTreeItem::databaseItem(); if (databaseItem) databaseItem->expandCollections(); } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.h ================================================ #pragma once #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" namespace Robomongo { class ExplorerDatabaseTreeItem; enum ExplorerDatabaseCategory { Collections, Functions, Files, Users }; /* ** Database category Tree Item (looks like folder in the UI) */ class ExplorerDatabaseCategoryTreeItem : public ExplorerTreeItem { Q_OBJECT public: typedef ExplorerTreeItem BaseClass; ExplorerDatabaseCategoryTreeItem(ExplorerDatabaseTreeItem *databaseItem, ExplorerDatabaseCategory category); void expand(); private Q_SLOTS: void ui_createCollection(); void ui_addUser(); void ui_addFunction(); void ui_refreshCollections(); void ui_dbCollectionsStatistics(); void ui_refreshUsers(); void ui_refreshFunctions(); void ui_viewUsers(); void ui_viewFunctions(); private: ExplorerDatabaseTreeItem *databaseItem() const; const ExplorerDatabaseCategory _category; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h" #include #include #include #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/domain/MongoCollection.h" #include "robomongo/core/domain/MongoUser.h" #include "robomongo/core/domain/MongoFunction.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/mongodb/MongoWorker.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerUserTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerFunctionTreeItem.h" #include "robomongo/gui/GuiRegistry.h" namespace { void openCurrentDatabaseShell(Robomongo::MongoDatabase *database, const QString &script, bool execute = true, const Robomongo::CursorPosition &cursor = Robomongo::CursorPosition()) { Robomongo::AppRegistry::instance().app()->openShell(database, script, execute, Robomongo::QtUtils::toQString(database->name()), cursor); } } namespace Robomongo { namespace detail { QString buildName(const QString& text, int count) { if (count == -1) return QString("%1 ...").arg(text); return QString("%1 (%2)").arg(text).arg(count); } } ExplorerDatabaseTreeItem::ExplorerDatabaseTreeItem(QTreeWidgetItem *parent, MongoDatabase *const database) : BaseClass(parent), _database(database), _bus(AppRegistry::instance().bus()), _collectionSystemFolderItem(NULL) { auto openDbShellAction = new QAction("Open Shell", this); #ifdef __APPLE__ openDbShellAction->setIcon(GuiRegistry::instance().mongodbIconForMAC()); #else openDbShellAction->setIcon(GuiRegistry::instance().mongodbIcon()); #endif VERIFY(connect(openDbShellAction, SIGNAL(triggered()), SLOT(ui_dbOpenShell()))); QAction *dbStats = new QAction("Database Statistics", this); VERIFY(connect(dbStats, SIGNAL(triggered()), SLOT(ui_dbStatistics()))); QAction *dbCurrOps = new QAction("Current Operations", this); VERIFY(connect(dbCurrOps, SIGNAL(triggered()), SLOT(ui_dbCurrentOps()))); QAction *dbKillOp = new QAction("Kill Operation...", this); VERIFY(connect(dbKillOp, SIGNAL(triggered()), SLOT(ui_dbKillOp()))); QAction *dbDrop = new QAction("Drop Database...", this); VERIFY(connect(dbDrop, SIGNAL(triggered()), SLOT(ui_dbDrop()))); QAction *dbRepair = new QAction("Repair Database...", this); VERIFY(connect(dbRepair, SIGNAL(triggered()), SLOT(ui_dbRepair()))); QAction *refreshDatabase = new QAction("Refresh", this); VERIFY(connect(refreshDatabase, SIGNAL(triggered()), SLOT(ui_refreshDatabase()))); BaseClass::_contextMenu->addAction(openDbShellAction); BaseClass::_contextMenu->addAction(refreshDatabase); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(dbStats); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(dbCurrOps); BaseClass::_contextMenu->addAction(dbKillOp); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(dbRepair); BaseClass::_contextMenu->addAction(dbDrop); _bus->subscribe(this, MongoDatabaseCollectionListLoadedEvent::Type, _database); _bus->subscribe(this, MongoDatabaseUsersLoadedEvent::Type, _database); _bus->subscribe(this, MongoDatabaseFunctionsLoadedEvent::Type, _database); _bus->subscribe(this, MongoDatabaseCollectionsLoadingEvent::Type, _database); _bus->subscribe(this, MongoDatabaseFunctionsLoadingEvent::Type, _database); _bus->subscribe(this, MongoDatabaseUsersLoadingEvent::Type, _database); setText(0, QtUtils::toQString(_database->name())); setIcon(0, GuiRegistry::instance().databaseIcon()); setExpanded(false); setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); _collectionFolderItem = new ExplorerDatabaseCategoryTreeItem(this, Collections); _collectionFolderItem->setText(0, "Collections"); _collectionFolderItem->setIcon(0, GuiRegistry::instance().folderIcon()); addChild(_collectionFolderItem); _functionsFolderItem = new ExplorerDatabaseCategoryTreeItem(this, Functions); _functionsFolderItem->setText(0, "Functions"); _functionsFolderItem->setIcon(0, GuiRegistry::instance().folderIcon()); addChild(_functionsFolderItem); _usersFolderItem = new ExplorerDatabaseCategoryTreeItem(this, Users); _usersFolderItem->setText(0, "Users"); _usersFolderItem->setIcon(0, GuiRegistry::instance().folderIcon()); addChild(_usersFolderItem); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); } void ExplorerDatabaseTreeItem::expandCollections() { _database->loadCollections(); } void ExplorerDatabaseTreeItem::expandUsers() { _database->loadUsers(); } void ExplorerDatabaseTreeItem::expandColection(ExplorerCollectionTreeItem *const item) { _bus->send(_database->server()->worker(), new LoadCollectionIndexesRequest(item, item->collection()->info())); } void ExplorerDatabaseTreeItem::dropIndexFromCollection(ExplorerCollectionTreeItem *const item, const std::string &indexName) { _bus->send(_database->server()->worker(), new DropCollectionIndexRequest(item, item->collection()->info(), indexName)); } void ExplorerDatabaseTreeItem::addEditIndex( ExplorerCollectionTreeItem *const item, const IndexInfo &oldInfo, const IndexInfo &newInfo) const { _bus->send(_database->server()->worker(), new AddEditIndexRequest(item, oldInfo, newInfo)); } void ExplorerDatabaseTreeItem::expandFunctions() { _database->loadFunctions(); } void ExplorerDatabaseTreeItem::handle(MongoDatabaseCollectionListLoadedEvent *event) { if (event->isError()) { _collectionFolderItem->setText(0, "Collections"); _collectionFolderItem->setExpanded(false); return; } std::vector collections = event->collections; int count = collections.size(); _collectionFolderItem->setText(0, detail::buildName("Collections", count)); QtUtils::clearChildItems(_collectionFolderItem); // Do not expand, when we do not have collections if (count == 0) { _collectionFolderItem->setExpanded(false); return; } _collectionSystemFolderItem = new ExplorerTreeItem(_collectionFolderItem); _collectionSystemFolderItem->setIcon(0, GuiRegistry::instance().folderIcon()); _collectionSystemFolderItem->setText(0, "System"); _collectionFolderItem->addChild(_collectionSystemFolderItem); for (int i = 0; i < collections.size(); ++i) { MongoCollection *collection = collections[i]; if (collection->isSystem()) { addSystemCollectionItem(collection); } else { addCollectionItem(collection); } } showCollectionSystemFolderIfNeeded(); } void ExplorerDatabaseTreeItem::handle(MongoDatabaseUsersLoadedEvent *event) { if (event->isError()) { _usersFolderItem->setText(0, "Users"); _usersFolderItem->setExpanded(false); return; } auto const& users = event->users(); _usersFolderItem->setText(0, detail::buildName("Users", users.size())); // Do not expand, when we do not have any users if (users.size() == 0) _usersFolderItem->setExpanded(false); QtUtils::clearChildItems(_usersFolderItem); for(auto const& user : users) addUserItem(event->database(), user); } void ExplorerDatabaseTreeItem::handle(MongoDatabaseFunctionsLoadedEvent *event) { if (event->isError()) { _functionsFolderItem->setText(0, "Functions"); _functionsFolderItem->setExpanded(false); return; } std::vector functions = event->functions(); int count = functions.size(); _functionsFolderItem->setText(0, detail::buildName("Functions", count)); // Do not expand, when we do not have functions if (count == 0) _functionsFolderItem->setExpanded(false); QtUtils::clearChildItems(_functionsFolderItem); for (int i = 0; i < functions.size(); ++i) { MongoFunction fun = functions[i]; addFunctionItem(event->database(), fun); } } void ExplorerDatabaseTreeItem::handle(MongoDatabaseCollectionsLoadingEvent *event) { _collectionFolderItem->setText(0, detail::buildName("Collections", -1)); } void ExplorerDatabaseTreeItem::handle(MongoDatabaseFunctionsLoadingEvent *event) { _functionsFolderItem->setText(0, detail::buildName("Functions", -1)); } void ExplorerDatabaseTreeItem::handle(MongoDatabaseUsersLoadingEvent *event) { _usersFolderItem->setText(0, detail::buildName("Users", -1)); } void ExplorerDatabaseTreeItem::addCollectionItem(MongoCollection *collection) { auto collectionItem = new ExplorerCollectionTreeItem(_collectionFolderItem, this, collection); collectionItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); _collectionFolderItem->addChild(collectionItem); } void ExplorerDatabaseTreeItem::addSystemCollectionItem(MongoCollection *collection) { auto collectionItem = new ExplorerCollectionTreeItem(_collectionSystemFolderItem, this, collection); collectionItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); _collectionSystemFolderItem->addChild(collectionItem); } void ExplorerDatabaseTreeItem::showCollectionSystemFolderIfNeeded() { _collectionSystemFolderItem->setHidden(_collectionSystemFolderItem->childCount() == 0); } void ExplorerDatabaseTreeItem::addUserItem(MongoDatabase *database, const MongoUser &user) { ExplorerUserTreeItem *userItem = new ExplorerUserTreeItem(_usersFolderItem, database, user); _usersFolderItem->addChild(userItem); } void ExplorerDatabaseTreeItem::addFunctionItem(MongoDatabase *database, const MongoFunction &function) { ExplorerFunctionTreeItem *functionItem = new ExplorerFunctionTreeItem(_functionsFolderItem, database, function); _functionsFolderItem->addChild(functionItem); } void ExplorerDatabaseTreeItem::ui_refreshDatabase() { expandCollections(); } void ExplorerDatabaseTreeItem::ui_dbStatistics() { openCurrentDatabaseShell(_database, "db.stats()"); } void ExplorerDatabaseTreeItem::ui_dbCurrentOps() { openCurrentDatabaseShell(_database, "db.currentOp()"); } void ExplorerDatabaseTreeItem::ui_dbKillOp() { openCurrentDatabaseShell(_database, "db.killOp()", false, CursorPosition(0, -1)); } void ExplorerDatabaseTreeItem::ui_dbDrop() { auto const& buff = QString("Drop %1 database?").arg(QtUtils::toQString(_database->name())); int const answer = QMessageBox::question(treeWidget(), "Drop Database", buff, QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton); if (answer != QMessageBox::Yes) return; _database->server()->dropDatabase(_database->name()); } void ExplorerDatabaseTreeItem::ui_dbRepair() { auto const& buff = QString("Repair %1 database?").arg(QtUtils::toQString(_database->name())); int const answer = QMessageBox::question(treeWidget(), "Repair Database", buff, QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton); if (answer != QMessageBox::Yes) return; openCurrentDatabaseShell(_database, "db.repairDatabase()", false); } void ExplorerDatabaseTreeItem::ui_dbOpenShell() { openCurrentDatabaseShell(_database, ""); } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h ================================================ #pragma once #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" namespace Robomongo { namespace detail { QString buildName(const QString& text, int count); } class ExplorerCollectionTreeItem; class ExplorerDatabaseCategoryTreeItem; class EventBus; class MongoDatabaseCollectionListLoadedEvent; class MongoDatabaseUsersLoadedEvent; class MongoDatabaseFunctionsLoadedEvent; class MongoDatabaseCollectionsLoadingEvent; class MongoDatabaseFunctionsLoadingEvent; class MongoDatabaseUsersLoadingEvent; class MongoDatabase; class MongoUser; class MongoFunction; class MongoCollection; struct IndexInfo; class ExplorerDatabaseTreeItem : public ExplorerTreeItem { Q_OBJECT public: typedef ExplorerTreeItem BaseClass; ExplorerDatabaseTreeItem(QTreeWidgetItem *parent, MongoDatabase *const database); MongoDatabase *database() const { return _database; } void expandCollections(); void expandUsers(); void expandFunctions(); void expandColection(ExplorerCollectionTreeItem *const item); void dropIndexFromCollection(ExplorerCollectionTreeItem *const item, const std::string &indexName); void addEditIndex(ExplorerCollectionTreeItem *const item, const IndexInfo &oldInfo, const IndexInfo &newInfo) const; public Q_SLOTS: void handle(MongoDatabaseCollectionListLoadedEvent *event); void handle(MongoDatabaseUsersLoadedEvent *event); void handle(MongoDatabaseFunctionsLoadedEvent *event); void handle(MongoDatabaseCollectionsLoadingEvent *event); void handle(MongoDatabaseFunctionsLoadingEvent *event); void handle(MongoDatabaseUsersLoadingEvent *event); private Q_SLOTS: void ui_dbStatistics(); void ui_dbCurrentOps(); void ui_dbKillOp(); void ui_dbDrop(); void ui_dbRepair(); void ui_dbOpenShell(); void ui_refreshDatabase(); private: void addCollectionItem(MongoCollection *collection); void addSystemCollectionItem(MongoCollection *collection); void showCollectionSystemFolderIfNeeded(); void addUserItem(MongoDatabase *database, const MongoUser &user); void addFunctionItem(MongoDatabase *database, const MongoFunction &function); EventBus *_bus; ExplorerDatabaseCategoryTreeItem *_collectionFolderItem; ExplorerDatabaseCategoryTreeItem *_functionsFolderItem; ExplorerDatabaseCategoryTreeItem *_usersFolderItem; ExplorerTreeItem *_collectionSystemFolderItem; MongoDatabase *const _database; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerFunctionTreeItem.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerFunctionTreeItem.h" #include #include #include "robomongo/gui/dialogs/FunctionTextEditor.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/utils/DialogUtils.h" #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { ExplorerFunctionTreeItem::ExplorerFunctionTreeItem(QTreeWidgetItem *parent, MongoDatabase *database, const MongoFunction &function) : BaseClass(parent), _function(function), _database(database) { QAction *dropFunction = new QAction("Remove Function", this); VERIFY(connect(dropFunction, SIGNAL(triggered()), SLOT(ui_dropFunction()))); QAction *editFunction = new QAction("Edit Function", this); VERIFY(connect(editFunction, SIGNAL(triggered()), SLOT(ui_editFunction()))); BaseClass::_contextMenu->addAction(editFunction); BaseClass::_contextMenu->addAction(dropFunction); setText(0, QtUtils::toQString(_function.name())); setIcon(0, GuiRegistry::instance().functionIcon()); setToolTip(0, buildToolTip(_function)); setExpanded(false); } QString ExplorerFunctionTreeItem::buildToolTip(const MongoFunction &function) { return QString("%0").arg(QtUtils::toQString(function.name())); } void ExplorerFunctionTreeItem::ui_editFunction() { std::string name = _function.name(); FunctionTextEditor dlg(QString::fromStdString(_database->server()->connectionRecord()->getFullAddress()), QString::fromStdString(_database->name()), _function); dlg.setWindowTitle("Edit Function"); if (dlg.exec() == QDialog::Accepted) { MongoFunction editedFunction = dlg.function(); _database->updateFunction(name, editedFunction); } } void ExplorerFunctionTreeItem::ui_dropFunction() { // Ask user int answer = utils::questionDialog(treeWidget(), "Drop", "Function", QtUtils::toQString(_function.name())); if (answer != QMessageBox::Yes) return; _database->dropFunction(_function.name()); } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerFunctionTreeItem.h ================================================ #pragma once #include "robomongo/core/domain/MongoFunction.h" #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" namespace Robomongo { class MongoDatabase; class ExplorerFunctionTreeItem :public ExplorerTreeItem { Q_OBJECT public: typedef ExplorerTreeItem BaseClass; ExplorerFunctionTreeItem(QTreeWidgetItem *parent, MongoDatabase *database, const MongoFunction &function); MongoFunction function() const { return _function; } MongoDatabase *database() const { return _database; } private Q_SLOTS: void ui_editFunction(); void ui_dropFunction(); private: QString buildToolTip(const MongoFunction &function); MongoFunction _function; MongoDatabase *_database; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerReplicaSetFolderItem.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerReplicaSetFolderItem.h" #include #include #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/gui/GuiRegistry.h" namespace { void openCurrentServerShell(Robomongo::MongoServer *const server, const QString &script, bool execute = true, const Robomongo::CursorPosition &cursor = Robomongo::CursorPosition()) { Robomongo::AppRegistry::instance().app()->openShell(server, script, std::string(), execute, Robomongo::QtUtils::toQString(server->connectionRecord()->getReadableName()), cursor); } void openCurrentServerShell(Robomongo::MongoServer* server, Robomongo::ConnectionSettings* connSettings, const QString &script) { Robomongo::AppRegistry::instance().app()->openShell(server, connSettings, Robomongo::ScriptInfo(script, true)); } } namespace Robomongo { ExplorerReplicaSetFolderItem::ExplorerReplicaSetFolderItem(ExplorerTreeItem *parent, MongoServer *const server) : BaseClass(parent), _server(server) { auto repSetStatus = new QAction("Status of Replica Set", this); VERIFY(connect(repSetStatus, SIGNAL(triggered()), SLOT(on_repSetStatus()))); auto refresh = new QAction("Refresh", this); VERIFY(connect(refresh, SIGNAL(triggered()), SLOT(on_refresh()))); BaseClass::_contextMenu->addAction(repSetStatus); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(refresh); AppRegistry::instance().bus()->subscribe(this, ReplicaSetFolderLoading::Type, _server); setIcon(0, GuiRegistry::instance().folderIcon()); setText(0, "Replica Set (" + QString::number(_server->replicaSetInfo()->membersAndHealths.size()) + " nodes)"); setExpanded(false); setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); } void ExplorerReplicaSetFolderItem::updateText() { setText(0, "Replica Set (" + QString::number(_server->replicaSetInfo()->membersAndHealths.size()) + " nodes)"); } void ExplorerReplicaSetFolderItem::disableSomeContextMenuActions() { if (BaseClass::_contextMenu->actions().size() < 1) return; // Find out if there is at least one reachable member mongo::HostAndPort onlineMember; for (auto const& member : _server->replicaSetInfo()->membersAndHealths) { if (member.second) { onlineMember = mongo::HostAndPort(member.first); break; } } // Show menu item "Status of Replica Set" only if there is at least one reachable member // If there is no reachable member, disable it. BaseClass::_contextMenu->actions().at(0)->setDisabled(onlineMember.empty()); } void ExplorerReplicaSetFolderItem::expand() { if (!_refreshFlag) { _refreshFlag = true; return; } _server->tryRefreshReplicaSetFolder(true); } void ExplorerReplicaSetFolderItem::on_repSetStatus() { if (!_server->replicaSetInfo()->primary.empty()) { openCurrentServerShell(_server, "rs.status()"); } else // Primary is unreachable, try to find and run rs.status on a reachable secondary { mongo::HostAndPort onlineMember; for (auto const& member : _server->replicaSetInfo()->membersAndHealths) { if (member.second) { onlineMember = mongo::HostAndPort(member.first); break; } } if (onlineMember.empty()) { LOG_MSG("Unable to find a reachable replica set member to run rs.status().", mongo::logger::LogSeverity::Error()); return; } auto connSetting = std::unique_ptr(_server->connectionRecord()->clone()); // Set connection settings of this replica member connSetting->setConnectionName(onlineMember.toString() + " [member of " + connSetting->connectionName() + "]"); connSetting->setServerHost(onlineMember.host()); connSetting->setServerPort(onlineMember.port()); connSetting->setReplicaSet(false); connSetting->replicaSetSettings()->setMembers(std::vector()); openCurrentServerShell(_server, connSetting.get(), "rs.status()"); } } void ExplorerReplicaSetFolderItem::handle(ReplicaSetFolderLoading *event) { setText(0, "Replica Set ..."); } void ExplorerReplicaSetFolderItem::on_refresh() { _server->tryRefreshReplicaSetFolder(true); } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerReplicaSetFolderItem.h ================================================ #pragma once #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" namespace Robomongo { class MongoServer; struct ReplicaSetFolderLoading; class ExplorerReplicaSetFolderItem : public ExplorerTreeItem { Q_OBJECT public: using BaseClass = ExplorerTreeItem; ExplorerReplicaSetFolderItem(ExplorerTreeItem *item, MongoServer *const server); void updateText(); void disableSomeContextMenuActions(); void expand(); void setRefreshFlag(bool state) { _refreshFlag = state; } bool refreshFlag() const { return _refreshFlag; } private Q_SLOTS: void on_refresh(); void on_repSetStatus(); void handle(ReplicaSetFolderLoading *event); private: MongoServer *const _server = nullptr; bool _refreshFlag = true; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerReplicaSetTreeItem.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerReplicaSetTreeItem.h" #include #include #include #include #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h" #include "robomongo/gui/dialogs/CreateDatabaseDialog.h" #include "robomongo/gui/GuiRegistry.h" namespace { void openCurrentServerShell(Robomongo::MongoServer* server, Robomongo::ConnectionSettings* connSettings, const QString &script) { auto const scriptStr = Robomongo::ScriptInfo(script, true); Robomongo::AppRegistry::instance().app()->openShell(server, connSettings, scriptStr); } } namespace Robomongo { ExplorerReplicaSetTreeItem::ExplorerReplicaSetTreeItem(QTreeWidgetItem *parent, MongoServer *const server, const mongo::HostAndPort& repMemberHostAndPort, const bool isPrimary, const bool isUp) : BaseClass(parent), _repMemberHostAndPort(repMemberHostAndPort), _isPrimary(isPrimary), _isUp(isUp), _server(server), _connSettings(server->connectionRecord()->clone()), _bus(AppRegistry::instance().bus()) { // Set connection settings of this replica member _connSettings->setConnectionName(_repMemberHostAndPort.toString() + " [member of " + _connSettings->connectionName() + "]"); _connSettings->setServerHost(_repMemberHostAndPort.host()); _connSettings->setServerPort(_repMemberHostAndPort.port()); _connSettings->setReplicaSet(false); _connSettings->replicaSetSettings()->deleteAllMembers(); // Add Actions auto openShellAction = new QAction("Open Shell", this); #ifdef __APPLE__ openShellAction->setIcon(GuiRegistry::instance().mongodbIconForMAC()); #else openShellAction->setIcon(GuiRegistry::instance().mongodbIcon()); #endif VERIFY(connect(openShellAction, SIGNAL(triggered()), SLOT(ui_openShell()))); auto openDirectConnection = new QAction("Open Direct Connection", this); VERIFY(connect(openDirectConnection, SIGNAL(triggered()), SLOT(ui_openDirectConnection()))); auto serverStatus = new QAction("Server Status", this); VERIFY(connect(serverStatus, SIGNAL(triggered()), SLOT(ui_serverStatus()))); auto serverVersion = new QAction("MongoDB Version", this); VERIFY(connect(serverVersion, SIGNAL(triggered()), SLOT(ui_serverVersion()))); auto serverHostInfo = new QAction("Host Info", this); VERIFY(connect(serverHostInfo, SIGNAL(triggered()), SLOT(ui_serverHostInfo()))); auto showLog = new QAction("Show Log", this); VERIFY(connect(showLog, SIGNAL(triggered()), SLOT(ui_showLog()))); BaseClass::_contextMenu->addAction(openShellAction); BaseClass::_contextMenu->addAction(openDirectConnection); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(serverStatus); BaseClass::_contextMenu->addAction(serverHostInfo); BaseClass::_contextMenu->addAction(serverVersion); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(showLog); updateTextAndIcon(_isUp, _isPrimary); setExpanded(true); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator); } void ExplorerReplicaSetTreeItem::updateTextAndIcon(bool isUp, bool isPrimary) { _isUp = isUp; _isPrimary = isPrimary; QString stateStr("[Unknown]"); if (_isUp) stateStr = _isPrimary ? "[Primary]" : "[Secondary]"; else stateStr = "[Not Reachable]"; setDisabled(_isUp ? false : true); setText(0, QString::fromStdString(_repMemberHostAndPort.toString()) + " " + stateStr); setIcon(0, _isPrimary ? GuiRegistry::instance().serverPrimaryIcon() : GuiRegistry::instance().serverSecondaryIcon()); } void ExplorerReplicaSetTreeItem::ui_serverHostInfo() { openCurrentServerShell(_server, _connSettings.get(), "db.hostInfo()"); } void ExplorerReplicaSetTreeItem::ui_serverStatus() { openCurrentServerShell(_server, _connSettings.get(), "db.serverStatus()"); } void ExplorerReplicaSetTreeItem::ui_serverVersion() { openCurrentServerShell(_server, _connSettings.get(), "db.version()"); } void ExplorerReplicaSetTreeItem::ui_showLog() { openCurrentServerShell(_server, _connSettings.get(), "show log"); } void ExplorerReplicaSetTreeItem::ui_openShell() { openCurrentServerShell(_server, _connSettings.get(), ""); } void ExplorerReplicaSetTreeItem::ui_openDirectConnection() { AppRegistry::instance().app()->openServer(_connSettings.get(), ConnectionPrimary); } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerReplicaSetTreeItem.h ================================================ #pragma once #include "robomongo/core/events/MongoEvents.h" #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" namespace Robomongo { class EventBus; class MongoServerLoadingDatabasesEvent; class ExplorerReplicaSetTreeItem : public ExplorerTreeItem { Q_OBJECT public: using BaseClass = ExplorerTreeItem; /* ** Constructs ExplorerReplicaSetTreeItem */ ExplorerReplicaSetTreeItem(QTreeWidgetItem *parent, MongoServer *const server, const mongo::HostAndPort& repMemberHostAndPort, const bool isPrimary, const bool isUp); /** * @brief Updates this widget's text and icon * @param isUp: true if this set member is reachable, false otherwise * @param isPrimary: true if this set member is primary, false otherwise */ void updateTextAndIcon(bool isUp, bool isPrimary); // Getters ConnectionSettings* connectionSettings() { return _connSettings.get(); } bool isUp() const { return _isUp; } MongoServer* server() const { return _server; } private Q_SLOTS: void ui_openShell(); void ui_openDirectConnection(); void ui_serverHostInfo(); void ui_serverStatus(); void ui_serverVersion(); void ui_showLog(); private: mongo::HostAndPort _repMemberHostAndPort; bool _isPrimary; // true if this set member is primary, false otherwise bool _isUp; // true if this set member is reachable, false otherwise MongoServer *const _server; std::unique_ptr _connSettings; EventBus *_bus; // todo: remove? }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerServerTreeItem.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerServerTreeItem.h" #include #include #include #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/ReplicaSetSettings.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerReplicaSetFolderItem.h" #include "robomongo/gui/widgets/explorer/ExplorerReplicaSetTreeItem.h" #include "robomongo/gui/dialogs/CreateDatabaseDialog.h" #include "robomongo/gui/GuiRegistry.h" namespace { void openCurrentServerShell(Robomongo::MongoServer *const server, const QString &script, bool execute = true, const Robomongo::CursorPosition &cursor = Robomongo::CursorPosition()) { QString const connName = Robomongo::QtUtils::toQString(server->connectionRecord()->getReadableName()); Robomongo::AppRegistry::instance().app()->openShell(server, script, std::string(), execute, connName, cursor); } } namespace Robomongo { ExplorerServerTreeItem::ExplorerServerTreeItem(QTreeWidget *view, MongoServer *const server, ConnectionInfo connInfo) : BaseClass(view), _server(server), _bus(AppRegistry::instance().bus()), _replicaSetFolder(nullptr), _primaryWasUnreachable(false), _systemFolder(nullptr) { auto openShellAction = new QAction("Open Shell", this); #ifdef __APPLE__ openShellAction->setIcon(GuiRegistry::instance().mongodbIconForMAC()); #else openShellAction->setIcon(GuiRegistry::instance().mongodbIcon()); #endif VERIFY(connect(openShellAction, SIGNAL(triggered()), SLOT(ui_openShell()))); QAction *refreshServer = new QAction("Refresh", this); VERIFY(connect(refreshServer, SIGNAL(triggered()), SLOT(ui_refreshServer()))); QAction *createDatabase = new QAction("Create Database", this); VERIFY(connect(createDatabase, SIGNAL(triggered()), SLOT(ui_createDatabase()))); QAction *serverStatus = new QAction("Server Status", this); VERIFY(connect(serverStatus, SIGNAL(triggered()), SLOT(ui_serverStatus()))); QAction *serverVersion = new QAction("MongoDB Version", this); VERIFY(connect(serverVersion, SIGNAL(triggered()), SLOT(ui_serverVersion()))); QAction *serverHostInfo = new QAction("Host Info", this); VERIFY(connect(serverHostInfo, SIGNAL(triggered()), SLOT(ui_serverHostInfo()))); QAction *showLog = new QAction("Show Log", this); VERIFY(connect(showLog, SIGNAL(triggered()), SLOT(ui_showLog()))); QAction *disconnectAction = new QAction("Disconnect", this); disconnectAction->setIconText("Disconnect"); VERIFY(connect(disconnectAction, SIGNAL(triggered()), SLOT(ui_disconnectServer()))); BaseClass::_contextMenu->addAction(openShellAction); BaseClass::_contextMenu->addAction(refreshServer); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(createDatabase); BaseClass::_contextMenu->addAction(serverStatus); BaseClass::_contextMenu->addAction(serverHostInfo); BaseClass::_contextMenu->addAction(serverVersion); BaseClass::_contextMenu->addSeparator(); BaseClass::_contextMenu->addAction(showLog); BaseClass::_contextMenu->addAction(disconnectAction); _bus->subscribe(this, DatabaseListLoadedEvent::Type, _server); _bus->subscribe(this, MongoServerLoadingDatabasesEvent::Type, _server); _bus->subscribe(this, ReplicaSetFolderRefreshed::Type, _server); _bus->subscribe(this, ConnectionEstablishedEvent::Type, _server); _bus->subscribe(this, ConnectionFailedEvent::Type, _server); setText(0, buildServerName()); setIcon(0, _server->connectionRecord()->isReplicaSet() ? GuiRegistry::instance().replicaSetIcon() : GuiRegistry::instance().serverIcon()); setExpanded(true); setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); if (_server->connectionRecord()->isReplicaSet()) { buildReplicaSetFolder(false); buildDatabaseItems(); } } void ExplorerServerTreeItem::expand() { if (_server->connectionRecord()->isReplicaSet()) { // do nothing since replica set refresh/reload can take very long times. } else { // single server _server->loadDatabases(); } } void ExplorerServerTreeItem::disableSomeContextMenuActions(bool disable) { if (BaseClass::_contextMenu->actions().size() < 10 || !_server->connectionRecord()->isReplicaSet()) return; // [1]:Refresh and [9]:Disconnect are always enabled BaseClass::_contextMenu->actions().at(0)->setDisabled(disable); BaseClass::_contextMenu->actions().at(2)->setDisabled(disable); BaseClass::_contextMenu->actions().at(3)->setDisabled(disable); BaseClass::_contextMenu->actions().at(4)->setDisabled(disable); BaseClass::_contextMenu->actions().at(5)->setDisabled(disable); BaseClass::_contextMenu->actions().at(6)->setDisabled(disable); BaseClass::_contextMenu->actions().at(8)->setDisabled(disable); } void ExplorerServerTreeItem::databaseRefreshed(const QList &dbs) { int count = dbs.count(); setText(0, buildServerName(&count)); // Delete system folder and database items QtUtils::clearChildItems(this); // Add 'System' folder QIcon folderIcon = GuiRegistry::instance().folderIcon(); _systemFolder = new ExplorerTreeItem(this); _systemFolder->setIcon(0, folderIcon); _systemFolder->setText(0, "System"); addChild(_systemFolder); for (int i = 0; i < dbs.size(); i++) { MongoDatabase *database = dbs.at(i); if (database->isSystem()) { auto dbItem = new ExplorerDatabaseTreeItem(_systemFolder, database); _systemFolder->addChild(dbItem); continue; } auto dbItem = new ExplorerDatabaseTreeItem(this, database); addChild(dbItem); } // Show 'System' folder only if it has items _systemFolder->setHidden(_systemFolder->childCount() == 0); } void ExplorerServerTreeItem::handle(DatabaseListLoadedEvent *event) { if (event->isError()) { setText(0, buildServerName(0)); // Restore normal name (without "...") if (!_server->connectionRecord()->isReplicaSet()) setExpanded(false); // Collapse // We should clear all child items, but we are not // doing this, because of incorrect "senders" for messages // that are going to Worker thread. Yes, some guy specified UI objects // as senders. If we delete these UI objects, response will crash the app // (because QObject::thread() is used to get thread of the QObject) // QtUtils::clearChildItems(this); std::stringstream ss; ss << "Cannot load list of databases.\n\nError:\n" << event->error().errorMessage(); QMessageBox::information(NULL, "Error", QtUtils::toQString(ss.str())); return; } databaseRefreshed(event->list); } void ExplorerServerTreeItem::handle(MongoServerLoadingDatabasesEvent *event) { int count = -1; setText(0, buildServerName(&count)); } void ExplorerServerTreeItem::handle(ReplicaSetFolderRefreshed *event) { buildReplicaSetFolder(event->expanded); // Rebuild replica set folder in any case // ---Primary is unreachable if (event->isError()) { replicaSetPrimaryUnreachable(); disableSomeContextMenuActions(true); if (event->error().showErrorWindow()) { std::string const errorStr = "Replica set's primary is unreachable.\n\nReason:\n" "Connection failure. " + event->error().errorMessage(); QMessageBox::critical(nullptr, "Error", QString::fromStdString(errorStr)); } return; } // --- Primary is reachable if (_primaryWasUnreachable) // Rebuild db items only when primary was unreachable previously. _server->loadDatabases(); replicaSetPrimaryReachable(); } void ExplorerServerTreeItem::handle(ConnectionEstablishedEvent *event) { if (!_server->connectionRecord()->isReplicaSet() || ConnectionType::ConnectionRefresh != event->connectionType) return; if (_primaryWasUnreachable) replicaSetPrimaryReachable(); setIcon(0, GuiRegistry::instance().replicaSetIcon()); buildReplicaSetServerItem(); } void ExplorerServerTreeItem::handle(ConnectionFailedEvent *event) { if (!_server->connectionRecord()->isReplicaSet() || ConnectionType::ConnectionRefresh != event->connectionType) return; buildReplicaSetFolder(true); replicaSetPrimaryUnreachable(); QMessageBox::critical(nullptr, "Error", QString::fromStdString(event->message)); } QString ExplorerServerTreeItem::buildServerName(int *count /* = NULL */, bool online /* = true*/) { QString name = QtUtils::toQString(_server->connectionRecord()->getReadableName()); if (!count) return name; if (*count == -1) return name + " ..."; return online ? QString("%1 (%2)").arg(name).arg(*count) : QString("%1").arg(name) + " [Offline]"; } void ExplorerServerTreeItem::ui_serverHostInfo() { openCurrentServerShell(_server, "db.hostInfo()"); } void ExplorerServerTreeItem::ui_serverStatus() { openCurrentServerShell(_server, "db.serverStatus()"); } void ExplorerServerTreeItem::ui_serverVersion() { openCurrentServerShell(_server, "db.version()"); } void ExplorerServerTreeItem::ui_showLog() { openCurrentServerShell(_server, "show log"); } void ExplorerServerTreeItem::ui_openShell() { openCurrentServerShell(_server, ""); } void ExplorerServerTreeItem::ui_disconnectServer() { auto *view = dynamic_cast(BaseClass::treeWidget()); if (!view) return; int index = view->indexOfTopLevelItem(this); if (index == -1) return; QTreeWidgetItem *removedItem = view->takeTopLevelItem(index); if (!removedItem) return; AppRegistry::instance().app()->closeServer(_server); delete removedItem; } void ExplorerServerTreeItem::ui_refreshServer() { if (_server->connectionRecord()->isReplicaSet()) { int count = -1; setText(0, buildServerName(&count)); // Append "..." into root item text _server->tryRefreshReplicaSetConnection(); } else { // single server expand(); } } void ExplorerServerTreeItem::ui_createDatabase() { CreateDatabaseDialog dlg(QString::fromStdString(_server->connectionRecord()->getFullAddress()), QString(), QString(), treeWidget()); dlg.setOkButtonText("&Create"); dlg.setInputLabelText("Database Name:"); if (dlg.exec() == QDialog::Accepted) { if (_server->connectionRecord()->isReplicaSet()) { // Replica Set auto newDb = new MongoDatabase(_server, dlg.databaseName().toStdString()); _server->addDatabase(newDb); int dbCount = _server->databases().count(); setText(0, buildServerName(&dbCount)); auto dbItem = new ExplorerDatabaseTreeItem(this, newDb); addChild(dbItem); LOG_MSG("Database \'" + newDb->name() + "\' created.", mongo::logger::LogSeverity::Info()); } else { // single server _server->createDatabase(QtUtils::toStdStringSafe(dlg.databaseName())); } } } void ExplorerServerTreeItem::buildReplicaSetServerItem() { // Delete all children (replica set folder, system folder and database items) QtUtils::clearChildItems(this); _replicaSetFolder = nullptr; buildReplicaSetFolder(false); buildDatabaseItems(); } void ExplorerServerTreeItem::buildReplicaSetFolder(bool expanded) { if (_replicaSetFolder) { // delete and rebuild existing folder child items if (_replicaSetFolder->childCount() > 0) QtUtils::clearChildItems(_replicaSetFolder); _replicaSetFolder->updateText(); } else { // build folder from scratch _replicaSetFolder = new ExplorerReplicaSetFolderItem(this, _server); addChild(_replicaSetFolder); } // Add replica set members bool isPrimary; for (auto const& memberAndHealth : _server->replicaSetInfo()->membersAndHealths) { isPrimary = _server->replicaSetInfo()->primary.toString() == memberAndHealth.first; auto const& hostAndPort = mongo::HostAndPort(mongo::StringData(memberAndHealth.first)); _replicaSetFolder->addChild(new ExplorerReplicaSetTreeItem(_replicaSetFolder, _server, hostAndPort, isPrimary, memberAndHealth.second)); } _replicaSetFolder->setRefreshFlag(false); _replicaSetFolder->setExpanded(expanded); _replicaSetFolder->setRefreshFlag(true); } void ExplorerServerTreeItem::buildDatabaseItems() { int dbCount = _server->databases().count(); setText(0, buildServerName(&dbCount)); // Add 'System' folder _systemFolder = new ExplorerTreeItem(this); _systemFolder->setIcon(0, GuiRegistry::instance().folderIcon()); _systemFolder->setText(0, "System"); addChild(_systemFolder); for (auto const& database : _server->databases()) { if (database->isSystem()) { auto dbItem = new ExplorerDatabaseTreeItem(_systemFolder, database); _systemFolder->addChild(dbItem); continue; } auto dbItem = new ExplorerDatabaseTreeItem(this, database); addChild(dbItem); } // Show 'System' folder only if it has items _systemFolder->setHidden(_systemFolder->childCount() == 0); } void ExplorerServerTreeItem::replicaSetPrimaryReachable() { _primaryWasUnreachable = false; disableSomeContextMenuActions(false); _replicaSetFolder->disableSomeContextMenuActions(); } void ExplorerServerTreeItem::replicaSetPrimaryUnreachable() { _primaryWasUnreachable = true; disableSomeContextMenuActions(true); _replicaSetFolder->disableSomeContextMenuActions(); int dbCount = 0; setText(0, buildServerName(&dbCount, false)); setIcon(0, GuiRegistry::instance().replicaSetOfflineIcon()); // For system folder and database items - delete children then set disable QtUtils::clearChildItems(_systemFolder); _systemFolder->setDisabled(true); for (int i = 0; i < childCount(); ++i) { auto dbItem = dynamic_cast(child(i)); if (dbItem) { QtUtils::clearChildItems(dbItem); dbItem->setDisabled(true); } } } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerServerTreeItem.h ================================================ #pragma once #include "robomongo/core/events/MongoEvents.h" #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" namespace Robomongo { class EventBus; class MongoServerLoadingDatabasesEvent; class ExplorerReplicaSetFolderItem; class ExplorerTreeItem; class ExplorerServerTreeItem : public ExplorerTreeItem { Q_OBJECT public: typedef ExplorerTreeItem BaseClass; /* ** Constructs ExplorerServerTreeItem */ ExplorerServerTreeItem(QTreeWidget *view, MongoServer *const server, ConnectionInfo connInfo); /* ** Expand server tree item; */ void expand(); // Disable/enable menu items, except [1]:Refresh and [9]:Disconnect which are // always enabled, according to replica set status (online or offline). void disableSomeContextMenuActions(bool disable); public Q_SLOTS: void databaseRefreshed(const QList &dbs); void handle(DatabaseListLoadedEvent *event); void handle(MongoServerLoadingDatabasesEvent *event); void handle(ReplicaSetFolderRefreshed *event); // Special handle for server refresh events for replica set connections only void handle(ConnectionEstablishedEvent *event); // Special handle for server refresh events for replica set connections only void handle(ConnectionFailedEvent *event); private Q_SLOTS: void ui_showLog(); void ui_openShell(); void ui_disconnectServer(); void ui_refreshServer(); void ui_createDatabase(); void ui_serverHostInfo(); void ui_serverStatus(); void ui_serverVersion(); private: // Build all items for a root replica set server item, only used in refresh events // (not designed to be used in primary connection) void buildReplicaSetServerItem(); // Build only replica set folder and member items void buildReplicaSetFolder(bool expanded); // This function assumes there is no existing db items (system folder and other db tree items), // so existing db items should be deleted before calling this function. void buildDatabaseItems(); void replicaSetPrimaryReachable(); void replicaSetPrimaryUnreachable(); /** * @brief Builds server * @param count: Number of databases. * If NULL - name will be without count of databases. * If -1 - name will contain "..." at the end. */ QString buildServerName(int *count = NULL, bool isOnline = true); ExplorerReplicaSetFolderItem *_replicaSetFolder; ExplorerTreeItem *_systemFolder; MongoServer *const _server; EventBus *_bus; // Flag to track last replica set connection's primary status bool _primaryWasUnreachable = false; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerTreeItem.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" #include #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { ExplorerTreeItem::ExplorerTreeItem(QTreeWidgetItem *parent) :QObject(), BaseClass(parent), _contextMenu(new QMenu(treeWidget()) ) { } ExplorerTreeItem::ExplorerTreeItem(QTreeWidget *view) :QObject(view), BaseClass(view), _contextMenu(new QMenu(view) ) { } void ExplorerTreeItem::showContextMenuAtPos(const QPoint &pos) { _contextMenu->exec(pos); } ExplorerTreeItem::~ExplorerTreeItem() { _contextMenu->deleteLater(); QtUtils::clearChildItems(this); } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerTreeItem.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QMenu; QT_END_NAMESPACE namespace Robomongo { class ExplorerTreeItem :public QObject, public QTreeWidgetItem { Q_OBJECT public: typedef QTreeWidgetItem BaseClass; explicit ExplorerTreeItem(QTreeWidget *view); explicit ExplorerTreeItem(QTreeWidgetItem *parent); virtual void showContextMenuAtPos(const QPoint &pos); using BaseClass::parent; virtual ~ExplorerTreeItem(); protected: QMenu *const _contextMenu; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerTreeWidget.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerTreeWidget.h" #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerReplicaSetTreeItem.h" #include #include namespace Robomongo { ExplorerTreeWidget::ExplorerTreeWidget(QWidget *parent) : QTreeWidget(parent) { #if defined(Q_OS_MAC) setAttribute(Qt::WA_MacShowFocusRect, false); QPalette palet = palette(); palet.setColor(QPalette::Active, QPalette::Highlight, QColor(16, 108, 214)); setPalette(palet); #endif setContextMenuPolicy(Qt::DefaultContextMenu); setObjectName("explorerTree"); setIndentation(15); setHeaderHidden(true); setSelectionMode(QAbstractItemView::SingleSelection); setExpandsOnDoubleClick(false); } void ExplorerTreeWidget::contextMenuEvent(QContextMenuEvent *event) { QTreeWidgetItem *item = itemAt(event->pos()); // If the replica set item is not reachable, do not show context menu auto replicaSetItem = dynamic_cast(item); if (replicaSetItem && !replicaSetItem->isUp()) return; // If the database set item is disabled, do not show context menu auto dbItem = dynamic_cast(item); if (dbItem && dbItem->isDisabled()) return; if (item) { auto explorerItem = dynamic_cast(item); if (explorerItem) explorerItem->showContextMenuAtPos(mapToGlobal(event->pos())); } } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerTreeWidget.h ================================================ #pragma once #include namespace Robomongo { class ExplorerTreeWidget : public QTreeWidget { Q_OBJECT public: explicit ExplorerTreeWidget(QWidget *parent = 0); protected: virtual void contextMenuEvent(QContextMenuEvent *event); }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerUserTreeItem.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerUserTreeItem.h" #include #include #include "robomongo/gui/dialogs/CreateUserDialog.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/utils/DialogUtils.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { ExplorerUserTreeItem::ExplorerUserTreeItem( QTreeWidgetItem *parent, MongoDatabase *const database, const MongoUser &user) : BaseClass(parent), _user(user), _database(database) { auto const dropUser { new QAction("Drop User", this) }; VERIFY(connect(dropUser, SIGNAL(triggered()), SLOT(ui_dropUser()))); auto const viewUser { new QAction("View User", this) }; VERIFY(connect(viewUser, SIGNAL(triggered()), SLOT(ui_viewUser()))); BaseClass::_contextMenu->addAction(viewUser); BaseClass::_contextMenu->addAction(dropUser); setText(0, QtUtils::toQString(_user.name())); setIcon(0, GuiRegistry::instance().userIcon()); setExpanded(false); } void ExplorerUserTreeItem::ui_dropUser() { // Ask user int const answer { utils::questionDialog( treeWidget(), "Drop", "User", QtUtils::toQString(_user.name()) ) }; if (answer == QMessageBox::Yes) _database->dropUser(_user.name()); } void ExplorerUserTreeItem::ui_viewUser() { auto const& app { Robomongo::AppRegistry::instance().app() }; app->openShell( _database, QString::fromStdString("db.getUser(\"" + _user.name() + "\")"), true, QtUtils::toQString(_database->name()), CursorPosition() ); } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerUserTreeItem.h ================================================ #pragma once #include "robomongo/core/domain/MongoUser.h" #include "robomongo/gui/widgets/explorer/ExplorerTreeItem.h" namespace Robomongo { class MongoDatabase; class ExplorerUserTreeItem : public ExplorerTreeItem { Q_OBJECT public: typedef ExplorerTreeItem BaseClass; ExplorerUserTreeItem(QTreeWidgetItem *parent, MongoDatabase *const database, const MongoUser &user); public Q_SLOTS: void ui_dropUser(); void ui_viewUser(); private: const MongoUser _user; MongoDatabase *_database; }; } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerWidget.cpp ================================================ #include "robomongo/gui/widgets/explorer/ExplorerWidget.h" #include #include #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/MainWindow.h" #include "robomongo/gui/widgets/explorer/ExplorerTreeWidget.h" #include "robomongo/gui/widgets/explorer/ExplorerServerTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerCollectionIndexesDir.h" #include "robomongo/gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerReplicaSetTreeItem.h" #include "robomongo/gui/widgets/explorer/ExplorerReplicaSetFolderItem.h" #include "robomongo/gui/widgets/explorer/ExplorerUserTreeItem.h" #include "robomongo/utils/common.h" namespace Robomongo { ExplorerWidget::ExplorerWidget(MainWindow *parentMainWindow) : BaseClass(parentMainWindow), _progress(0) { _treeWidget = new ExplorerTreeWidget(this); QHBoxLayout *vlaout = new QHBoxLayout(); vlaout->setMargin(0); vlaout->addWidget(_treeWidget, Qt::AlignJustify); VERIFY(connect(_treeWidget, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(ui_itemExpanded(QTreeWidgetItem *)))); VERIFY(connect(_treeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(ui_itemDoubleClicked(QTreeWidgetItem *, int)))); setLayout(vlaout); QMovie *movie = new QMovie(":robomongo/icons/loading.gif", QByteArray(), this); _progressLabel = new QLabel(this); _progressLabel->setMovie(movie); _progressLabel->hide(); movie->start(); } ExplorerWidget::~ExplorerWidget() { saveSetting("ExplorerWidget/size", size()); } QTreeWidgetItem* ExplorerWidget::getSelectedTreeItem() const { return _treeWidget->currentItem(); } void ExplorerWidget::keyPressEvent(QKeyEvent *event) { if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Enter)) { QList items = _treeWidget->selectedItems(); if (items.count() != 1) { BaseClass::keyPressEvent(event); return; } QTreeWidgetItem *item = items[0]; if (!item) { BaseClass::keyPressEvent(event); return; } ui_itemDoubleClicked(item, 0); return; } BaseClass::keyPressEvent(event); } QSize ExplorerWidget::sizeHint() const { auto size { getSetting("ExplorerWidget/size").toSize() }; if(QSize(-1, -1) == size) size = QSize(180, -1); return(size); } void ExplorerWidget::increaseProgress() { ++_progress; _progressLabel->move(width() / 2 - 8, height() / 2 - 8); _progressLabel->show(); } void ExplorerWidget::decreaseProgress() { --_progress; if (_progress < 0) _progress = 0; if (!_progress) _progressLabel->hide(); } void ExplorerWidget::handle(ConnectingEvent *event) { increaseProgress(); } void ExplorerWidget::handle(ConnectionEstablishedEvent *event) { // Do not make UI changes for non PRIMARY connections if (event->connectionType != ConnectionPrimary) return; decreaseProgress(); auto item = new ExplorerServerTreeItem(_treeWidget, event->server, event->connInfo); _treeWidget->addTopLevelItem(item); _treeWidget->setCurrentItem(item); _treeWidget->setFocus(); } void ExplorerWidget::handle(ConnectionFailedEvent *event) { decreaseProgress(); } void ExplorerWidget::ui_itemExpanded(QTreeWidgetItem *item) { auto categoryItem = dynamic_cast(item); if (categoryItem) { categoryItem->expand(); return; } auto serverItem = dynamic_cast(item); if (serverItem) { serverItem->expand(); return; } auto replicaSetFolder = dynamic_cast(item); if (replicaSetFolder) { replicaSetFolder->expand(); return; } auto dirItem = dynamic_cast(item); if (dirItem) { dirItem->expand(); } } void ExplorerWidget::ui_itemDoubleClicked(QTreeWidgetItem *item, int column) { if (auto collectionItem = dynamic_cast(item)) { AppRegistry::instance().app()->openShell(collectionItem->collection()); return; } if (auto userItem = dynamic_cast(item)) { userItem->ui_viewUser(); return; } auto replicaMemberItem = dynamic_cast(item); if (replicaMemberItem && replicaMemberItem->isUp()) { AppRegistry::instance().app()->openShell(replicaMemberItem->server(), replicaMemberItem->connectionSettings(), ScriptInfo("", true)); return; } // Toggle expanded state item->setExpanded(!item->isExpanded()); } } ================================================ FILE: src/robomongo/gui/widgets/explorer/ExplorerWidget.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QTreeWidget; class QTreeWidgetItem; class QLabel; QT_END_NAMESPACE #include "robomongo/core/events/MongoEvents.h" namespace Robomongo { class MainWindow; /** * @brief Explorer widget (usually you'll see it at the left of main window) */ class ExplorerWidget : public QWidget { Q_OBJECT public: typedef QWidget BaseClass; ExplorerWidget(MainWindow *parent); ~ExplorerWidget(); QTreeWidgetItem* getSelectedTreeItem() const; protected Q_SLOTS: void handle(ConnectingEvent *event); void handle(ConnectionEstablishedEvent *event); void handle(ConnectionFailedEvent *event); private Q_SLOTS: void ui_itemExpanded(QTreeWidgetItem *item); void ui_itemDoubleClicked(QTreeWidgetItem *item, int column); protected: void keyPressEvent(QKeyEvent *event) override; private: QSize sizeHint() const override; int _progress; void increaseProgress(); void decreaseProgress(); QLabel *_progressLabel; QTreeWidget *_treeWidget; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/BsonTableModel.cpp ================================================ #include "robomongo/gui/widgets/workarea/BsonTableModel.h" #include #include #include "robomongo/gui/widgets/workarea/BsonTreeItem.h" #include "robomongo/gui/widgets/workarea/BsonTreeModel.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { BsonTableModelProxy::BsonTableModelProxy(QObject *parent) : BaseClass(parent) { } int BsonTableModelProxy::rowCount(const QModelIndex &parent) const { int count = sourceModel()->rowCount(parent); return count; } QModelIndex BsonTableModelProxy::parent( const QModelIndex& index ) const { QModelIndex sourceParent = sourceModel()->parent( mapToSource(index) ); return sourceParent; } int BsonTableModelProxy::columnCount(const QModelIndex &parent) const { return _columns.size(); } QModelIndex BsonTableModelProxy::mapFromSource( const QModelIndex & sourceIndex ) const { int row = sourceIndex.row(); int col = sourceIndex.column(); BsonTreeItem *node = QtUtils::item(sourceIndex); if (!node || _columns.size() <= col) return QModelIndex(); BsonTreeItem *child = node->childByKey(_columns[col]); return createIndex( row, col, child ); } QModelIndex BsonTableModelProxy::sibling(int row, int column, const QModelIndex &idx) const { return BaseClass::sibling(row, 0, idx); } QModelIndex BsonTableModelProxy::index( int row, int col, const QModelIndex& parent ) const { BsonTreeItem *node = QtUtils::item(sourceModel()->index(row, 0, parent)); if (!node || _columns.size() <= col) return QModelIndex(); BsonTreeItem *child = node->childByKey(_columns[col]); return createIndex( row, col, child ); } QModelIndex BsonTableModelProxy::mapToSource( const QModelIndex &proxyIndex ) const { if ( !proxyIndex.isValid() ) return QModelIndex(); Q_ASSERT( proxyIndex.model() == this ); QModelIndex sourceIndex; BsonTreeItem *child = static_cast(proxyIndex.internalPointer()); if (child) { QtUtils::HackQModelIndex* hack = reinterpret_cast(&sourceIndex); BsonTreeItem *parent = static_cast(child->parent()); hack->r = proxyIndex.row(); hack->c = proxyIndex.column(); hack->i = parent; hack->m = sourceModel(); } return sourceIndex; } void BsonTableModelProxy::setSourceModel( QAbstractItemModel* model ) { if (model) { BsonTreeItem *child = QtUtils::item(model->index(0, 0)); if (child) { _root = qobject_cast(child->parent()); if (_root) { int count = _root->childrenCount(); for (int i = 0; i < count; ++i) { BsonTreeItem *child = _root->child(i); int countc = child->childrenCount(); for (int j = 0; j < countc; ++j) { addColumn(child->child(j)->key()); } } } } } return BaseClass::setSourceModel(model); } QVariant BsonTableModelProxy::data(const QModelIndex &index, int role) const { QVariant result; if (!index.isValid()) return result; BsonTreeItem *node = QtUtils::item(index); if (!node) { if (role == Qt::BackgroundRole) { return QBrush("#f5f3f2"); } return result; } if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { bool isCut = node->type() == mongo::String || node->type() == mongo::Code || node->type() == mongo::CodeWScope; if (role == Qt::ToolTipRole) { result = isCut ? node->value() : node->value().left(500); } else{ result = isCut ? node->value() : node->value().simplified().left(300); } } else if (role == Qt::DecorationRole) { return BsonTreeModel::getIcon(node); } return result; } QVariant BsonTableModelProxy::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { return column(section); } else { return QString("%1").arg(section + 1); } } QString BsonTableModelProxy::column(int col) const { return _columns[col]; } size_t BsonTableModelProxy::findIndexColumn(const QString &col) const { for (int i = 0; i < _columns.size(); ++i) { if (_columns[i] == col) { return i; } } return _columns.size(); } size_t BsonTableModelProxy::addColumn(const QString &col) { size_t column = findIndexColumn(col); if (column == _columns.size()) { _columns.push_back(col); } return column; } } ================================================ FILE: src/robomongo/gui/widgets/workarea/BsonTableModel.h ================================================ #pragma once #include #include namespace Robomongo { class BsonTreeItem; class BsonTableModelProxy : public QAbstractProxyModel { Q_OBJECT public: typedef QAbstractProxyModel BaseClass; typedef std::vector ColumnsValuesType; explicit BsonTableModelProxy(QObject *parent = 0); QVariant data(const QModelIndex &index, int role) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QModelIndex index( int row, int col, const QModelIndex& index ) const; virtual QModelIndex mapFromSource( const QModelIndex & sourceIndex ) const; virtual QModelIndex mapToSource( const QModelIndex &proxyIndex ) const; virtual void setSourceModel( QAbstractItemModel* model ); virtual QModelIndex parent( const QModelIndex& index ) const; virtual QModelIndex sibling(int row, int column, const QModelIndex &idx) const; private: QString column(int col) const; size_t addColumn(const QString &col); size_t findIndexColumn(const QString &col) const; ColumnsValuesType _columns; BsonTreeItem *_root; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/BsonTableView.cpp ================================================ #include "robomongo/gui/widgets/workarea/BsonTableView.h" #include #include #include #include #include "robomongo/gui/widgets/workarea/BsonTreeItem.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { BsonTableView::BsonTableView(MongoShell *shell, const MongoQueryInfo &queryInfo, QWidget *parent) :BaseClass(parent), _notifier(this, shell, queryInfo) { #if defined(Q_OS_MAC) setAttribute(Qt::WA_MacShowFocusRect, false); #endif GuiRegistry::instance().setAlternatingColor(this); verticalHeader()->setDefaultAlignment(Qt::AlignLeft); horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); setStyleSheet("QTableView { border-left: 1px solid #c7c5c4; border-top: 1px solid #c7c5c4; gridline-color: #edebea;}"); setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionBehavior(QAbstractItemView::SelectItems); setContextMenuPolicy(Qt::CustomContextMenu); VERIFY(connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&)))); } void BsonTableView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Delete) { _notifier.onDeleteDocuments(); } return BaseClass::keyPressEvent(event); } QModelIndex BsonTableView::selectedIndex() const { QModelIndexList indexes = detail::uniqueRows(selectionModel()->selectedIndexes()); if (indexes.count() != 1) return QModelIndex(); return indexes[0]; } QModelIndexList BsonTableView::selectedIndexes() const { return detail::uniqueRows(selectionModel()->selectedIndexes()); } void BsonTableView::showContextMenu( const QPoint &point ) { QPoint menuPoint = mapToGlobal(point); menuPoint.setY(menuPoint.y() + horizontalHeader()->height()); menuPoint.setX(menuPoint.x() + verticalHeader()->width()); QModelIndexList indexes = selectedIndexes(); if (detail::isMultiSelection(indexes)) { QMenu menu(this); _notifier.initMultiSelectionMenu(&menu); menu.exec(menuPoint); } else{ QModelIndex selectedInd = selectedIndex(); BsonTreeItem *documentItem = QtUtils::item(selectedInd); QMenu menu(this); _notifier.initMenu(&menu, documentItem); menu.exec(menuPoint); } } } ================================================ FILE: src/robomongo/gui/widgets/workarea/BsonTableView.h ================================================ #pragma once #include #include "robomongo/core/domain/Notifier.h" namespace Robomongo { class BsonTableView : public QTableView , public INotifierObserver { Q_OBJECT public: typedef QTableView BaseClass; explicit BsonTableView(MongoShell *shell, const MongoQueryInfo &queryInfo, QWidget *parent = 0); virtual QModelIndex selectedIndex() const; virtual QModelIndexList selectedIndexes() const; public Q_SLOTS: void showContextMenu(const QPoint &point); protected: virtual void keyPressEvent(QKeyEvent *event); private: Notifier _notifier; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/BsonTreeItem.cpp ================================================ #include "robomongo/gui/widgets/workarea/BsonTreeItem.h" #include using namespace mongo; namespace { struct removeIfFound { removeIfFound(Robomongo::BsonTreeItem *item) :_whatSearch(item) {} bool operator()(const Robomongo::BsonTreeItem* item) const { if (item == _whatSearch) { delete _whatSearch; return true; } return false; } const Robomongo::BsonTreeItem *const _whatSearch; }; const Robomongo::BsonTreeItem *findSuperRoot(const Robomongo::BsonTreeItem *const item) { Robomongo::BsonTreeItem *parent = qobject_cast(item->parent()); if (parent) { Robomongo::BsonTreeItem *grParent = qobject_cast(parent->parent()); if (grParent) { return findSuperRoot(parent); } } return item; } } namespace Robomongo { BsonTreeItem::BsonTreeItem(QObject *parent) :BaseClass(parent) { } BsonTreeItem::BsonTreeItem(const mongo::BSONObj &bsonObjRoot, QObject *parent) :BaseClass(parent), _root(bsonObjRoot) { } unsigned BsonTreeItem::childrenCount() const { return _items.size(); } void BsonTreeItem::clear() { _items.clear(); } void BsonTreeItem::addChild(BsonTreeItem *item) { _items.push_back(item); } BsonTreeItem* BsonTreeItem::child(unsigned pos) const { return _items[pos]; } BsonTreeItem* BsonTreeItem::childSafe(unsigned pos) const { if (childrenCount() > pos) { return _items[pos]; } else { return NULL; } } BsonTreeItem* BsonTreeItem::childByKey(const QString &val) { for (unsigned i = 0; i < _items.size(); ++i) { if (_items[i]->key() == val) { return _items[i]; } } return NULL; } const BsonTreeItem *BsonTreeItem::superParent() const { return findSuperRoot(this); } mongo::BSONObj BsonTreeItem::superRoot() const { return superParent()->root(); } mongo::BSONObj BsonTreeItem::root() const { return _root; } int BsonTreeItem::indexOf(BsonTreeItem *item) const { for (unsigned i = 0; i < _items.size(); ++i) { if (item == _items[i]) { return i; } } return -1; } QString BsonTreeItem::key() const { return _fields._key; } QString BsonTreeItem::value() const { return _fields._value; } mongo::BSONType BsonTreeItem::type() const { return _fields._type; } void BsonTreeItem::setKey(const QString &key) { _fields._key = key; } void BsonTreeItem::setValue(const QString &value) { _fields._value = value; } void BsonTreeItem::setType(mongo::BSONType type) { _fields._type = type; } mongo::BinDataType BsonTreeItem::binType() const { return _fields._binType; } void BsonTreeItem::setBinType(mongo::BinDataType type) { _fields._binType = type; } void BsonTreeItem::removeChild(BsonTreeItem *item) { _items.erase(std::remove_if(_items.begin(), _items.end(), removeIfFound(item)), _items.end()); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/BsonTreeItem.h ================================================ #pragma once #include #include #include #include namespace Robomongo { /** * @brief BSON tree item (represents array or object) */ struct BsonItemFields { QString _key; QString _value; mongo::BSONType _type; mongo::BinDataType _binType; }; class BsonTreeItem : public QObject { Q_OBJECT public: enum eColumn { eKey = 0, eValue = 1, eType = 2, eCountColumns = 3 }; typedef QObject BaseClass; typedef std::vector ChildContainerType; explicit BsonTreeItem(QObject *parent = 0); explicit BsonTreeItem(const mongo::BSONObj &bsonObjRoot, QObject *parent = 0); unsigned childrenCount() const; void clear(); void addChild(BsonTreeItem *item); void removeChild(BsonTreeItem *item); BsonTreeItem* child(unsigned pos) const; BsonTreeItem* childSafe(unsigned pos) const; BsonTreeItem* childByKey(const QString &val); int indexOf(BsonTreeItem *item) const; const BsonTreeItem* superParent() const; mongo::BSONObj root() const; mongo::BSONObj superRoot() const; std::string fieldName() const { return _fieldName; }; void setFieldName(const std::string &fieldName) { _fieldName = fieldName; }; QString key() const; void setKey(const QString &key); QString value() const; void setValue(const QString &value); mongo::BSONType type() const; void setType(mongo::BSONType type); mongo::BinDataType binType() const; void setBinType(mongo::BinDataType type); protected: const mongo::BSONObj _root; ChildContainerType _items; BsonItemFields _fields; std::string _fieldName; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/BsonTreeModel.cpp ================================================ #include "robomongo/gui/widgets/workarea/BsonTreeModel.h" #include #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/utils/BsonUtils.h" #include "robomongo/core/domain/MongoDocument.h" #include "robomongo/gui/widgets/workarea/BsonTreeItem.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/GuiRegistry.h" namespace { using namespace Robomongo; QString arrayValue(int itemsCount) { QString elements = itemsCount == 1 ? "element" : "elements"; return QString("[ %1 %2 ]").arg(itemsCount).arg(elements); } QString objectValue(int itemsCount) { QString fields = itemsCount == 1 ? "field" : "fields"; return QString("{ %1 %2 }").arg(itemsCount).arg(fields); } void parseDocument(BsonTreeItem *root, const mongo::BSONObj &doc, bool isArray) { mongo::BSONObjIterator iterator(doc); while (iterator.more()) { mongo::BSONElement element = iterator.next(); BsonTreeItem *childItemInner = new BsonTreeItem(doc, root); std::string fieldName = std::string(element.fieldName()); childItemInner->setFieldName(fieldName); QString uiFieldName = QtUtils::toQString(fieldName); childItemInner->setKey(uiFieldName); if (isArray) { // When we iterate array, show field names in square brackets // In this case field names are numeric, starting from 0. childItemInner->setKey("[" + uiFieldName + "]"); } if (BsonUtils::isArray(element)) { int itemsCount = element.Array().size(); childItemInner->setValue(arrayValue(itemsCount)); } else if (BsonUtils::isDocument(element)) { int count = BsonUtils::elementsCount(element.Obj()); childItemInner->setValue(objectValue(count)); } else { std::string result; BsonUtils::buildJsonString(element, result, AppRegistry::instance().settingsManager()->uuidEncoding(), AppRegistry::instance().settingsManager()->timeZone()); childItemInner->setValue(QtUtils::toQString(result)); } childItemInner->setType(element.type()); if (element.type() == mongo::BinData) { childItemInner->setBinType(element.binDataType()); } root->addChild(childItemInner); //root->setValue(QString("{ %1 fields }").arg(root->childrenCount())); } } } namespace Robomongo { BsonTreeModel::BsonTreeModel(const std::vector &documents, QObject *parent) : BaseClass(parent), _root(new BsonTreeItem(this)) { for (int i = 0; i < documents.size(); ++i) { MongoDocumentPtr doc = documents[i]; BsonTreeItem *child = new BsonTreeItem(doc->bsonObj(), _root); parseDocument(child, doc->bsonObj(), doc->bsonObj().isArray()); QString idValue; BsonTreeItem *idItem = child->childByKey("_id"); if (idItem) { idValue = idItem->value(); } child->setKey(QString("(%1) %2").arg(i + 1).arg(idValue)); int count = BsonUtils::elementsCount(doc->bsonObj()); if (doc->bsonObj().isArray()) { child->setValue(arrayValue(count)); child->setType(mongo::Array); } else { child->setValue(objectValue(count)); child->setType(mongo::Object); } _root->addChild(child); } } void BsonTreeModel::fetchMore(const QModelIndex &parent) { BsonTreeItem *node = QtUtils::item(parent); if (node) { mongo::BSONElement elem = BsonUtils::indexOf(node->root(), parent.row()); if (!elem.isNull() && elem.isABSONObj()) { parseDocument(node, elem.Obj(), elem.type() == mongo::Array); } } return BaseClass::fetchMore(parent); } bool BsonTreeModel::canFetchMore(const QModelIndex &parent) const { BsonTreeItem *node = QtUtils::item(parent); if (node && !node->childrenCount()) { return BsonUtils::isDocument(node->type()); } return false; } bool BsonTreeModel::hasChildren(const QModelIndex &parent) const { BsonTreeItem *node = QtUtils::item(parent); if (node) { return BsonUtils::isDocument(node->type()); } return true; } const QIcon &BsonTreeModel::getIcon(BsonTreeItem *item) { switch(item->type()) { case mongo::NumberDouble: return GuiRegistry::instance().bsonDoubleIcon(); case mongo::NumberDecimal: return GuiRegistry::instance().bsonNumberDecimalIcon(); case mongo::String: return GuiRegistry::instance().bsonStringIcon(); case mongo::Object: return GuiRegistry::instance().bsonObjectIcon(); case mongo::Array: return GuiRegistry::instance().bsonArrayIcon(); case mongo::BinData: return GuiRegistry::instance().bsonBinaryIcon(); case mongo::Undefined: return GuiRegistry::instance().circleIcon(); case mongo::jstOID: return GuiRegistry::instance().circleIcon(); case mongo::Bool: return GuiRegistry::instance().bsonBooleanIcon(); case mongo::Date: return GuiRegistry::instance().bsonDateTimeIcon(); case mongo::jstNULL: return GuiRegistry::instance().bsonNullIcon(); case mongo::RegEx: return GuiRegistry::instance().circleIcon(); case mongo::DBRef: return GuiRegistry::instance().circleIcon(); case mongo::Code: case mongo::CodeWScope: return GuiRegistry::instance().circleIcon(); case mongo::NumberInt: return GuiRegistry::instance().bsonIntegerIcon(); case mongo::bsonTimestamp: return GuiRegistry::instance().bsonDateTimeIcon(); case mongo::NumberLong: return GuiRegistry::instance().bsonIntegerIcon(); default: return GuiRegistry::instance().circleIcon(); } } QVariant BsonTreeModel::data(const QModelIndex &index, int role) const { QVariant result; if (!index.isValid()) return result; BsonTreeItem *node = QtUtils::item(index); if (!node) return result; int col = index.column(); if (role == Qt::DecorationRole && col == BsonTreeItem::eKey ) { return getIcon(node); } if (role == Qt::TextColorRole && col == BsonTreeItem::eType) { return QColor(Qt::gray); } if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { if (col == BsonTreeItem::eKey) { if (role == Qt::DisplayRole) { result = node->key(); } } else if (col == BsonTreeItem::eValue) { bool isCut = node->type() == mongo::String || node->type() == mongo::Code || node->type() == mongo::CodeWScope; if (role == Qt::ToolTipRole) { result = isCut ? node->value().left(500) : node->value(); } else{ result = isCut ? node->value().simplified().left(300) : node->value(); } } else if (col == BsonTreeItem::eType) { result = BsonUtils::BSONTypeToString(node->type(), node->binType(), AppRegistry::instance().settingsManager()->uuidEncoding()); } } return result; } Qt::ItemFlags BsonTreeModel::flags(const QModelIndex &index) const { Qt::ItemFlags result = 0; if (index.isValid()) { result = Qt::ItemIsSelectable | Qt::ItemIsEnabled; } return result; } int BsonTreeModel::rowCount(const QModelIndex &parent) const { const BsonTreeItem *parentItem = NULL; if (parent.isValid()) parentItem = QtUtils::item(parent); else parentItem = _root; return parentItem->childrenCount(); } int BsonTreeModel::columnCount(const QModelIndex &parent) const { return BsonTreeItem::eCountColumns; } QVariant BsonTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == BsonTreeItem::eKey) { return "Key"; } else if (section == BsonTreeItem::eValue) { return "Value"; } else { return "Type"; } } return BaseClass::headerData(section, orientation, role); } QModelIndex BsonTreeModel::parent(const QModelIndex& index) const { QModelIndex result; if (index.isValid()) { BsonTreeItem *const childItem = QtUtils::item(index); BsonTreeItem *const parentItem = static_cast(childItem->parent()); if (parentItem && parentItem != _root) { BsonTreeItem *const grandParent = static_cast(parentItem->parent()); int row = grandParent->indexOf(parentItem); result = createIndex(row, 0, parentItem); } } return result; } QModelIndex BsonTreeModel::index(int row, int column, const QModelIndex &parent) const { QModelIndex index; if (hasIndex(row, column, parent)) { const BsonTreeItem * parentItem = NULL; if (!parent.isValid()) { parentItem = _root; } else { parentItem = QtUtils::item(parent); } BsonTreeItem *childItem = parentItem->child(row); if (childItem) { index = createIndex(row, column, childItem); } } return index; } void BsonTreeModel::insertItem(BsonTreeItem *parent, BsonTreeItem *children) { QModelIndex index = createIndex(0, 0, parent); unsigned child_count = parent->childrenCount(); beginInsertRows(index, child_count, child_count); parent->addChild(children); endInsertRows(); } void BsonTreeModel::removeitem(BsonTreeItem *children) { BsonTreeItem *parent = static_cast(children->parent()); if (parent) { QModelIndex index = createIndex(0, 0, parent); int row = parent->indexOf(children); beginRemoveRows(index, row, row); parent->removeChild(children); endRemoveRows(); } } } ================================================ FILE: src/robomongo/gui/widgets/workarea/BsonTreeModel.h ================================================ #pragma once #include #include #include "robomongo/core/Core.h" namespace Robomongo { class BsonTreeItem; class BsonTreeModel : public QAbstractItemModel { Q_OBJECT public: typedef QAbstractItemModel BaseClass; static const QIcon &getIcon(BsonTreeItem *item); explicit BsonTreeModel(const std::vector &documents, QObject *parent = 0); QVariant data(const QModelIndex &index, int role) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; Qt::ItemFlags flags(const QModelIndex &index) const; virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; virtual QModelIndex parent(const QModelIndex& index) const; void insertItem(BsonTreeItem *parent, BsonTreeItem *children); void removeitem(BsonTreeItem *children); virtual void fetchMore(const QModelIndex &parent); virtual bool canFetchMore(const QModelIndex &parent) const; virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const; protected: BsonTreeItem *const _root; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/BsonTreeView.cpp ================================================ #include "robomongo/gui/widgets/workarea/BsonTreeView.h" #include #include #include #include #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/widgets/workarea/BsonTreeItem.h" #include "robomongo/gui/widgets/workarea/OutputWidget.h" namespace Robomongo { BsonTreeView::BsonTreeView(MongoShell *shell, const MongoQueryInfo &queryInfo, QWidget *parent) : BaseClass(parent), _notifier(this, shell, queryInfo), _outputItemContentWidget(dynamic_cast(parent)) { #if defined(Q_OS_MAC) setAttribute(Qt::WA_MacShowFocusRect, false); #endif GuiRegistry::instance().setAlternatingColor(this); setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionBehavior(QAbstractItemView::SelectRows); setContextMenuPolicy(Qt::CustomContextMenu); VERIFY(connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&)))); _expandRecursive = new QAction("Expand Recursively", this); _expandRecursive->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Right)); VERIFY(connect(_expandRecursive, SIGNAL(triggered()), SLOT(onExpandRecursive()))); _collapseRecursive = new QAction(tr("Collapse Recursively"), this); _collapseRecursive->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Left)); VERIFY(connect(_collapseRecursive, SIGNAL(triggered()), SLOT(onCollapseRecursive()))); setStyleSheet("QTreeView { border-left: 1px solid #c7c5c4; border-top: 1px solid #c7c5c4; }"); #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) header()->setSectionResizeMode(QHeaderView::Interactive); #endif } void BsonTreeView::showContextMenu(const QPoint &point) { if (_outputItemContentWidget->outputWidget()->progressBarActive()) return; QPoint menuPoint = mapToGlobal(point); menuPoint.setY(menuPoint.y() + header()->height()); QModelIndexList indexes = selectedIndexes(); if (detail::isMultiSelection(indexes)) { QMenu menu(this); menu.addAction(_expandRecursive); menu.addAction(_collapseRecursive); menu.addSeparator(); _notifier.initMultiSelectionMenu(&menu); menu.exec(menuPoint); } else { QModelIndex selectedInd = selectedIndex(); BsonTreeItem *documentItem = QtUtils::item(selectedInd); QMenu menu(this); bool isSimple = false; if (documentItem) { isSimple = detail::isSimpleType(documentItem); if (detail::isDocumentType(documentItem)) { menu.addAction(_expandRecursive); menu.addAction(_collapseRecursive); menu.addSeparator(); } } _notifier.initMenu(&menu, documentItem); menu.exec(menuPoint); } } void BsonTreeView::resizeEvent(QResizeEvent *event) { BaseClass::resizeEvent(event); header()->resizeSections(QHeaderView::Stretch); } void BsonTreeView::keyPressEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_Delete: _notifier.handleDeleteCommand(); break; case Qt::Key_Backspace: // Cmd/Ctrl + Backspace if (event->modifiers() & Qt::ControlModifier) _notifier.handleDeleteCommand(); break; case Qt::Key_Right: if (event->modifiers() & Qt::AltModifier) this->onExpandRecursive(); break; case Qt::Key_Left: if (event->modifiers() & Qt::AltModifier) this->onCollapseRecursive(); break; } return BaseClass::keyPressEvent(event); } void BsonTreeView::expandNode(const QModelIndex &index) { if (index.isValid()) { BaseClass::expand(index); BsonTreeItem *item = QtUtils::item(index); for (unsigned i = 0; i < item->childrenCount(); ++i) { BsonTreeItem *tritem = item->child(i); if (tritem && detail::isDocumentType(tritem)) { expandNode(model()->index(i, 0, index)); } } } } void BsonTreeView::collapseNode(const QModelIndex &index) { if (index.isValid()) { BaseClass::collapse(index); BsonTreeItem *item = QtUtils::item(index); for (unsigned i = 0; i < item->childrenCount(); ++i) { BsonTreeItem *tritem = item->child(i); if (tritem && detail::isDocumentType(tritem)) { collapseNode(model()->index(i, 0, index)); } } } } void BsonTreeView::onExpandRecursive() { QModelIndexList indexes = selectedIndexes(); if (detail::isMultiSelection(indexes)) { for (int i = 0; iselectedRows()); int count = indexes.count(); if (indexes.count() != 1) return QModelIndex(); return indexes[0]; } QModelIndexList BsonTreeView::selectedIndexes() const { return detail::uniqueRows(selectionModel()->selectedRows(), true); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/BsonTreeView.h ================================================ #pragma once #include #include "robomongo/core/domain/Notifier.h" #include "robomongo/gui/widgets/workarea/OutputItemContentWidget.h" namespace Robomongo { class InsertDocumentResponse; class BsonTreeView : public QTreeView, public INotifierObserver { Q_OBJECT public: typedef QTreeView BaseClass; BsonTreeView(MongoShell *shell, const MongoQueryInfo &queryInfo, QWidget *parent = NULL); virtual QModelIndex selectedIndex() const; virtual QModelIndexList selectedIndexes() const; void expandNode(const QModelIndex &index); void collapseNode(const QModelIndex &index); private Q_SLOTS: void onExpandRecursive(); void onCollapseRecursive(); void showContextMenu(const QPoint &point); protected: virtual void resizeEvent(QResizeEvent *event); virtual void keyPressEvent(QKeyEvent *event); private: Notifier _notifier; QAction *_expandRecursive; QAction *_collapseRecursive; const OutputItemContentWidget* _outputItemContentWidget; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/CollectionStatsTreeItem.cpp ================================================ #include "robomongo/gui/widgets/workarea/CollectionStatsTreeItem.h" #include #include #include "robomongo/core/domain/MongoUtils.h" #include "robomongo/core/domain/MongoDocument.h" #include "robomongo/core/domain/MongoNamespace.h" #include "robomongo/core/utils/BsonUtils.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/core/utils/QtUtils.h" namespace { QString prepareValue(const QString &data) { return data + " "; // ugly yet simple way to extend size of columns } } namespace Robomongo { CollectionStatsTreeItem::CollectionStatsTreeItem(MongoDocumentPtr document) { mongo::BSONObj _obj = document->bsonObj(); MongoNamespace ns(BsonUtils::getField(_obj, "ns")); setText(0, prepareValue(QtUtils::toQString(ns.collectionName()))); setIcon(0, GuiRegistry::instance().collectionIcon()); setText(1, prepareValue(QString::number(BsonUtils::getField(_obj, "count")))); setText(2, prepareValue(MongoUtils::buildNiceSizeString(BsonUtils::getField(_obj, "size")))); setText(3, prepareValue(MongoUtils::buildNiceSizeString(BsonUtils::getField(_obj, "storageSize")))); setText(4, prepareValue(MongoUtils::buildNiceSizeString(BsonUtils::getField(_obj, "totalIndexSize")))); setText(5, prepareValue(MongoUtils::buildNiceSizeString(BsonUtils::getField(_obj, "avgObjSize")))); setText(6, prepareValue(QString::number(BsonUtils::getField(_obj, "paddingFactor")))); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/CollectionStatsTreeItem.h ================================================ #pragma once #include #include "robomongo/core/Core.h" namespace Robomongo { class CollectionStatsTreeItem : public QTreeWidgetItem { public: CollectionStatsTreeItem(MongoDocumentPtr document); }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/CollectionStatsTreeWidget.cpp ================================================ #include "robomongo/gui/widgets/workarea/CollectionStatsTreeWidget.h" #include #include "robomongo/gui/widgets/workarea/CollectionStatsTreeItem.h" namespace Robomongo { CollectionStatsTreeWidget::CollectionStatsTreeWidget(const std::vector &documents, QWidget *parent) : QTreeWidget(parent) { QStringList colums; colums << "Name" << "Count" << "Size" << "Storage" << "Index" << "Average Object" << "Padding"; setHeaderLabels(colums); setStyleSheet( "QTreeWidget { border-left: 1px solid #c7c5c4; border-top: 1px solid #c7c5c4; }" ); QList items; size_t documentsCount = documents.size(); for (int i = 0; i < documentsCount; i++) { MongoDocumentPtr document = documents[i]; CollectionStatsTreeItem *item = new CollectionStatsTreeItem(document); items.append(item); } addTopLevelItems(items); header()->resizeSections(QHeaderView::ResizeToContents); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/CollectionStatsTreeWidget.h ================================================ #pragma once #include #include "robomongo/core/Core.h" namespace Robomongo { class CollectionStatsTreeWidget : public QTreeWidget { Q_OBJECT public: CollectionStatsTreeWidget(const std::vector &documents, QWidget *parent = NULL); }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/IndicatorLabel.cpp ================================================ #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include #include #include namespace Robomongo { Indicator::Indicator(const QIcon &icon, const QString &text) { QLabel *iconLabel = createLabelWithIcon(icon); _label = new QLabel(); QHBoxLayout *layout = new QHBoxLayout(); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(iconLabel); layout->addSpacing(7); layout->addWidget(_label); layout->addSpacing(14); setLayout(layout); setText(text); } void Indicator::setText(const QString &text) { _label->setText(text); } QLabel *Indicator::createLabelWithIcon(const QIcon &icon) { QPixmap pixmap = icon.pixmap(16, 16); QLabel *label = new QLabel; label->setPixmap(pixmap); return label; } } ================================================ FILE: src/robomongo/gui/widgets/workarea/IndicatorLabel.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QIcon; class QLabel; QT_END_NAMESPACE namespace Robomongo { class Indicator : public QWidget { Q_OBJECT public: Indicator(const QIcon &icon, const QString &text = QString()); void setText(const QString &text); private: QLabel *createLabelWithIcon(const QIcon &icon); QLabel *_label; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/JsonPrepareThread.cpp ================================================ #include "robomongo/gui/widgets/workarea/JsonPrepareThread.h" #include #include "robomongo/core/domain/MongoDocument.h" #include "robomongo/core/utils/BsonUtils.h" #include "robomongo/core/utils/QtUtils.h" namespace Robomongo { JsonPrepareThread::JsonPrepareThread(const std::vector &bsonObjects, UUIDEncoding uuidEncoding, SupportedTimes timeZone) :_bsonObjects(bsonObjects), _uuidEncoding(uuidEncoding), _timeZone(timeZone), _stop(false) { } void JsonPrepareThread::stop() { _stop = true; } void JsonPrepareThread::run() { int position = 1; // 1-based numbering to match tree & table views for (std::vector::const_iterator it = _bsonObjects.begin(); it != _bsonObjects.end(); ++it) { MongoDocumentPtr doc = *it; mongo::StringBuilder sb; if (position == 1) sb << "/* 1 */\n"; else sb << "\n\n/* " << position << " */\n"; mongo::BSONObj obj = doc->bsonObj(); std::string stdJson = BsonUtils::jsonString(obj, mongo::TenGen, 1, _uuidEncoding, _timeZone); if (_stop) break; sb << stdJson; QString json = QtUtils::toQString(sb.str()); if (_stop) break; emit partReady(json); position++; } emit done(); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/JsonPrepareThread.h ================================================ #pragma once #include #include #include "robomongo/core/Core.h" #include "robomongo/core/Enums.h" namespace Robomongo { /* ** In this thread we are running task to prepare JSON string from list of BSON objects */ class JsonPrepareThread : public QThread { Q_OBJECT public: /* ** Constructor */ JsonPrepareThread(const std::vector &bsonObjects, UUIDEncoding uuidEncoding, SupportedTimes timeZone); void stop(); Q_SIGNALS: /** * @brief Signals when all parts prepared */ void done(); /** * @brief Signals when json part is ready */ void partReady(const QString &part); protected: /* ** Overload function */ virtual void run(); private: /* ** List of documents */ const std::vector _bsonObjects; const UUIDEncoding _uuidEncoding; const SupportedTimes _timeZone; volatile bool _stop; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/OutputItemContentWidget.cpp ================================================ #include "robomongo/gui/widgets/workarea/OutputItemContentWidget.h" #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/domain/MongoShell.h" #include "robomongo/core/domain/MongoAggregateInfo.h" #include "robomongo/gui/widgets/workarea/OutputWidget.h" #include "robomongo/gui/widgets/workarea/OutputItemHeaderWidget.h" #include "robomongo/gui/widgets/workarea/JsonPrepareThread.h" #include "robomongo/gui/widgets/workarea/BsonTreeView.h" #include "robomongo/gui/widgets/workarea/BsonTreeModel.h" #include "robomongo/gui/widgets/workarea/BsonTableView.h" #include "robomongo/gui/widgets/workarea/BsonTableModel.h" #include "robomongo/gui/editors/PlainJavaScriptEditor.h" #include "robomongo/gui/widgets/workarea/CollectionStatsTreeWidget.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/editors/JSLexer.h" #include "robomongo/gui/editors/FindFrame.h" namespace Robomongo { OutputItemContentWidget::OutputItemContentWidget(ViewMode viewMode, MongoShell *shell, const QString &text, double secs, bool multipleResults, bool tabbedResults, bool firstItem, bool lastItem, AggrInfo aggrInfo, QWidget *parent) : BaseClass(parent), _textView(NULL), _bsonTreeview(NULL), _thread(NULL), _bsonTable(NULL), _isTextModeSupported(true), _isTreeModeSupported(false), _isTableModeSupported(false), _isCustomModeSupported(false), _isTextModeInitialized(false), _isTreeModeInitialized(false), _isCustomModeInitialized(false), _isTableModeInitialized(false), _isFirstPartRendered(false), _text(text), _shell(shell), _outputWidget(dynamic_cast(parentWidget())), _initialSkip(0), _initialLimit(0), _mod(NULL), _viewMode(viewMode), _aggrInfo(aggrInfo) { setup(secs, multipleResults, tabbedResults, firstItem, lastItem); } OutputItemContentWidget::OutputItemContentWidget(ViewMode viewMode, MongoShell *shell, const QString &type, const std::vector &documents, const MongoQueryInfo &queryInfo, double secs, bool multipleResults, bool tabbedResults, bool firstItem, bool lastItem, AggrInfo aggrInfo, QWidget *parent) : BaseClass(parent), _textView(NULL), _bsonTreeview(NULL), _thread(NULL), _bsonTable(NULL), _isTextModeSupported(true), _isTreeModeSupported(true), _isTableModeSupported(true), _isCustomModeSupported(!type.isEmpty()), _isTextModeInitialized(false), _isTreeModeInitialized(false), _isCustomModeInitialized(false), _isTableModeInitialized(false), _isFirstPartRendered(false), _documents(documents), _queryInfo(queryInfo), _type(type), _shell(shell), _initialSkip(queryInfo._skip), _initialLimit(queryInfo._limit), _outputWidget(dynamic_cast(parentWidget())), _mod(NULL), _viewMode(viewMode), _aggrInfo(aggrInfo) { setup(secs, multipleResults, tabbedResults, firstItem, lastItem); } void OutputItemContentWidget::setup(double secs, bool multipleResults, bool tabbedResults, bool firstItem, bool lastItem) { setContentsMargins(0, 0, 0, 0); _header = new OutputItemHeaderWidget(this, multipleResults, tabbedResults, firstItem, lastItem); if (_queryInfo._info.isValid()) { _header->setCollection(QtUtils::toQString(_queryInfo._info._ns.collectionName())); _header->paging()->setBatchSize(_queryInfo._batchSize); _header->paging()->setSkip(_queryInfo._skip); if (!_queryInfo._limit) _queryInfo._limit = 50; } else if (_aggrInfo.isValid) { _initialLimit = 0; _initialSkip = 0; _header->setCollection(QtUtils::toQString(_aggrInfo.collectionName)); _header->paging()->setBatchSize(_aggrInfo.batchSize); _header->paging()->setSkip(_aggrInfo.skip); } _header->setTime(QString("%1 sec.").arg(secs, 0, 'g', 3)); QVBoxLayout *layout = new QVBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(_header); _stack = new QStackedWidget; layout->addWidget(_stack); setLayout(layout); configureModel(); VERIFY(connect(_header->paging(), SIGNAL(refreshed(int, int)), this, SLOT(refresh(int, int)))); VERIFY(connect(_header->paging(), SIGNAL(leftClicked(int, int)), this, SLOT(paging_leftClicked(int, int)))); VERIFY(connect(_header->paging(), SIGNAL(rightClicked(int, int)), this, SLOT(paging_rightClicked(int, int)))); VERIFY(connect(_header, SIGNAL(maximizedPart()), this, SIGNAL(maximizedPart()))); VERIFY(connect(_header, SIGNAL(restoredSize()), this, SIGNAL(restoredSize()))); refreshOutputItem(); } void OutputItemContentWidget::paging_leftClicked(int skip, int limit) { int s = skip - limit; if (s < 0) s = 0; refresh(s, limit); } void OutputItemContentWidget::refreshOutputItem() { switch(_viewMode) { case Text: showText(); break; case Tree: showTree(); break; case Table: showTable(); break; case Custom: showCustom(); break; default: showTree(); } } void OutputItemContentWidget::paging_rightClicked(int skip, int limit) { skip += limit; refresh(skip, limit); } void OutputItemContentWidget::refresh(int skip, int batchSize) { // Cannot set skip lower than in the text query if (skip < _initialSkip) { _header->paging()->setSkip(_initialSkip); skip = _initialSkip; } int skipDelta = skip - _initialSkip; int limit = batchSize; // If limit is set to 0 it means UNLIMITED number of documents (limited only by batch size) // This is according to MongoDB documentation. if (_initialLimit != 0) { limit = _initialLimit - skipDelta; if (limit <= 0) limit = -1; // It means that we do not need to load documents if (limit > batchSize) limit = batchSize; } MongoQueryInfo info(_queryInfo); info._limit = limit; info._skip = skip; info._batchSize = batchSize; _outputWidget->showProgress(); _shell->setScriptExecutable(true); if (_aggrInfo.isValid) { // Build original pipeline object, and append extra skip and limit for paging std::string pipelineModified = "["; for (int i = 0; ; i++) { auto const obj = mongo::tojson(_aggrInfo.pipeline.getObjectField(std::to_string(i))); if (obj.empty() || "{}" == obj) break; pipelineModified.append(obj + ","); } pipelineModified.append("{$skip:" + std::to_string(skip) + "}, " + "{$limit:" + std::to_string(batchSize) + "}" + "]"); std::string const query = "db.getCollection('" + _aggrInfo.collectionName + "').aggregate(" + pipelineModified + ", " + _aggrInfo.options.toString() + ")"; // Create aggr. info with new skip and batchsize AggrInfo const aggrInfo { _aggrInfo.collectionName, skip, batchSize, _aggrInfo.pipeline, _aggrInfo.options, _outputWidget->resultIndex(this) }; _shell->setAggrInfo(aggrInfo); _shell->execute(query); } else _shell->query(_outputWidget->resultIndex(this), info); } void OutputItemContentWidget::updateWithInfo(const MongoQueryInfo &inf, const std::vector &documents) { update(documents, inf._skip, inf._batchSize); } void OutputItemContentWidget::updateWithInfo(const AggrInfo &aggrInfo, const std::vector &documents) { update(documents, aggrInfo.skip, aggrInfo.batchSize); } void OutputItemContentWidget::update(const std::vector &documents, int skip, int batchSize) { _documents = documents; _header->paging()->setSkip(skip); _header->paging()->setBatchSize(batchSize); _text.clear(); _isFirstPartRendered = false; markUninitialized(); if (_bsonTable) { _stack->removeWidget(_bsonTable); delete _bsonTable; _bsonTable = NULL; } if (_bsonTreeview) { _stack->removeWidget(_bsonTreeview); delete _bsonTreeview; _bsonTreeview = NULL; } if (_textView) { _stack->removeWidget(_textView); delete _textView; _textView = NULL; } configureModel(); } void OutputItemContentWidget::showText() { _viewMode = Text; _header->showText(); if (!_isTextModeSupported) return; if (!_isTextModeInitialized) { _textView = configureLogText(); if (!_text.isEmpty()) { _textView->sciScintilla()->setText(_text); } else { if (_documents.size() > 0) { _textView->sciScintilla()->setText("Loading..."); _thread = new JsonPrepareThread(_documents, AppRegistry::instance().settingsManager()->uuidEncoding(), AppRegistry::instance().settingsManager()->timeZone()); VERIFY(connect(_thread, SIGNAL(partReady(const QString&)), this, SLOT(jsonPartReady(const QString&)))); VERIFY(connect(_thread, SIGNAL(finished()), _thread, SLOT(deleteLater()))); _thread->start(); } } _stack->addWidget(_textView); _isTextModeInitialized = true; } _stack->setCurrentWidget(_textView); } void OutputItemContentWidget::showTree() { _viewMode = Tree; _header->showTree(); if (!_isTreeModeSupported) { // try to downgrade to text mode showText(); _viewMode = Tree; return; } if (!_isTreeModeInitialized) { _bsonTreeview = new BsonTreeView(_shell, _queryInfo, this); _bsonTreeview->setModel(_mod); _stack->addWidget(_bsonTreeview); if (true == AppRegistry::instance().settingsManager()->autoExpand()) // Expanding only one level, because on large // documents it can take much time _bsonTreeview->expand(_mod->index(0, 0, QModelIndex())); _isTreeModeInitialized = true; } _stack->setCurrentWidget(_bsonTreeview); } void OutputItemContentWidget::showCustom() { _viewMode = Custom; _header->showCustom(); if (!_isCustomModeSupported) { // try to downgrade to tree mode showTree(); _viewMode = Custom; return; } if (!_isCustomModeInitialized) { if (_type == "collectionStats") { _collectionStats = new CollectionStatsTreeWidget(_documents, NULL); _stack->addWidget(_collectionStats); } _isCustomModeInitialized = true; } if (_collectionStats) _stack->setCurrentWidget(_collectionStats); } void OutputItemContentWidget::showTable() { _viewMode = Table; _header->showTable(); if (!_isTableModeSupported) { // try to downgrade to text mode showText(); _viewMode = Table; return; } if (!_isTableModeInitialized) { _bsonTable = new BsonTableView(_shell, _queryInfo); BsonTableModelProxy *modp = new BsonTableModelProxy(_bsonTable); modp->setSourceModel(_mod); _bsonTable->setModel(modp); _stack->addWidget(_bsonTable); _isTableModeInitialized = true; } _stack->setCurrentWidget(_bsonTable); } void OutputItemContentWidget::markUninitialized() { _isTextModeInitialized = false; _isTreeModeInitialized = false; _isCustomModeInitialized = false; _isTableModeInitialized = false; } void OutputItemContentWidget::applyDockUndockSettings(bool isDocking) const { _header->applyDockUndockSettings(isDocking); } void OutputItemContentWidget::toggleOrientation(Qt::Orientation orientation) const { _header->toggleOrientation(orientation); } void OutputItemContentWidget::jsonPartReady(const QString &json) { // check that this is our current thread JsonPrepareThread *thread = qobject_cast(sender()); if (thread && thread != _thread) { // close previous thread thread->stop(); thread->wait(); } else { if (_textView) { if (_isFirstPartRendered) _textView->sciScintilla()->append(json); else _textView->sciScintilla()->setText(json); _isFirstPartRendered = true; } } } BsonTreeModel *OutputItemContentWidget::configureModel() { delete _mod; _mod = new BsonTreeModel(_documents, this); return _mod; } FindFrame *Robomongo::OutputItemContentWidget::configureLogText() { const QFont &textFont = GuiRegistry::instance().font(); QsciLexerJavaScript *javaScriptLexer = new JSLexer(this); javaScriptLexer->setFont(textFont); FindFrame *_logText = new FindFrame(this); _logText->sciScintilla()->setLexer(javaScriptLexer); _logText->sciScintilla()->setTabWidth(4); _logText->sciScintilla()->setAppropriateBraceMatching(); _logText->sciScintilla()->setFont(textFont); _logText->sciScintilla()->setReadOnly(true); _logText->sciScintilla()->setWrapMode((QsciScintilla::WrapMode) QsciScintilla::SC_WRAP_NONE); _logText->sciScintilla()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); _logText->sciScintilla()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // Wrap mode turned off because it introduces huge performance problems // even for medium size documents. _logText->sciScintilla()->setStyleSheet("QFrame {background-color: rgb(73, 76, 78); border: 1px solid #c7c5c4; border-radius: 0px; margin: 0px; padding: 0px;}"); return _logText; } } ================================================ FILE: src/robomongo/gui/widgets/workarea/OutputItemContentWidget.h ================================================ #pragma once #include #include "robomongo/core/Core.h" #include "robomongo/core/domain/MongoQueryInfo.h" #include "robomongo/core/domain/MongoAggregateInfo.h" #include "robomongo/core/Enums.h" #include namespace Robomongo { class FindFrame; class BsonTreeView; class BsonTableView; class BsonTreeModel; class JsonPrepareThread; class CollectionStatsTreeWidget; class MongoShell; class OutputItemHeaderWidget; class OutputWidget; class OutputItemContentWidget : public QWidget { Q_OBJECT public: typedef QWidget BaseClass; OutputItemContentWidget(ViewMode viewMode, MongoShell *shell, const QString &text, double secs, bool multipleResults, bool tabbedResults, bool firstItem, bool lastItem, AggrInfo aggrInfo, QWidget *parent); OutputItemContentWidget(ViewMode viewMode, MongoShell *shell, const QString &type, const std::vector &documents, const MongoQueryInfo &queryInfo, double secs, bool multipleResults, bool tabbedResults, bool firstItem, bool lastItem, AggrInfo aggrInfo, QWidget *parent); int _initialSkip; int _initialLimit; void updateWithInfo(const MongoQueryInfo &inf, const std::vector &documents); void updateWithInfo(const AggrInfo &aggrInfo, const std::vector &documents); void update(const std::vector &documents, int skip, int batchSize); bool isTextModeSupported() const { return _isTextModeSupported; } bool isTreeModeSupported() const { return _isTreeModeSupported; } bool isCustomModeSupported() const { return _isCustomModeSupported; } bool isTableModeSupported() const { return _isTableModeSupported; } ViewMode viewMode() const { return _viewMode; } void refreshOutputItem(); void markUninitialized(); void applyDockUndockSettings(bool isDocking) const; void toggleOrientation(Qt::Orientation orientation) const; const OutputWidget* outputWidget() const { return _outputWidget; } Q_SIGNALS: void restoredSize(); void maximizedPart(); public Q_SLOTS: void showText(); void showTree(); void showTable(); void showCustom(); private Q_SLOTS: void jsonPartReady(const QString &json); void refresh(int skip, int batchSize); void paging_rightClicked(int skip, int batchSize); void paging_leftClicked(int skip, int limit); private: void setup(double secs, bool multipleResults, bool tabbedResults, bool firstItem, bool lastItem); FindFrame *configureLogText(); BsonTreeModel *configureModel(); FindFrame *_textView; BsonTreeView *_bsonTreeview; BsonTableView *_bsonTable; BsonTreeModel *_mod; CollectionStatsTreeWidget *_collectionStats; QString _text; QString _type; // type of request std::vector _documents; MongoQueryInfo _queryInfo; AggrInfo _aggrInfo; QStackedWidget *_stack; JsonPrepareThread *_thread; MongoShell *_shell; OutputItemHeaderWidget *_header; OutputWidget *_outputWidget; bool _isTextModeSupported; bool _isTreeModeSupported; bool _isTableModeSupported; bool _isCustomModeSupported; bool _isTextModeInitialized; bool _isTreeModeInitialized; bool _isTableModeInitialized; bool _isCustomModeInitialized; bool _isFirstPartRendered; ViewMode _viewMode; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/OutputItemHeaderWidget.cpp ================================================ #include "robomongo/gui/widgets/workarea/OutputItemHeaderWidget.h" #include #include #include #include #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/widgets/workarea/QueryWidget.h" #include "robomongo/gui/widgets/workarea/OutputWidget.h" #include "robomongo/gui/widgets/workarea/OutputItemContentWidget.h" #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/settings/SettingsManager.h" namespace { QFrame *createVerticalLine() { QFrame *vline = new QFrame(); vline->setFrameShape(QFrame::VLine); vline->setFrameShadow(QFrame::Sunken); vline->setFixedWidth(5); return vline; } } namespace Robomongo { OutputItemHeaderWidget::OutputItemHeaderWidget( OutputItemContentWidget *outputItemContentWidget, bool multipleResults, bool tabbedResults, bool firstItem, bool lastItem, QWidget *parent) : QFrame(parent), _maxButton(nullptr), _dockUndockButton(nullptr), _maximized(false), _multipleResults(multipleResults), _firstItem(firstItem), _lastItem(lastItem), _orientation(Qt::Vertical) { setContentsMargins(5, 0, 0, 0); auto const* outputWidget = qobject_cast(outputItemContentWidget->parentWidget()); _orientation = outputWidget->getOrientation(); // Text mode button _textButton = new QPushButton(this); _textButton->setIcon(GuiRegistry::instance().textIcon()); _textButton->setToolTip("View results in text mode"); _textButton->setFixedSize(24, 24); _textButton->setFlat(true); _textButton->setCheckable(true); // Tree mode button _treeButton = new QPushButton(this); _treeButton->hide(); _treeButton->setIcon(GuiRegistry::instance().treeIcon()); _treeButton->setToolTip("View results in tree mode"); _treeButton->setFixedSize(24, 24); _treeButton->setFlat(true); _treeButton->setCheckable(true); _treeButton->setChecked(true); // Table mode button _tableButton = new QPushButton(this); _tableButton->hide(); _tableButton->setIcon(GuiRegistry::instance().tableIcon()); _tableButton->setToolTip("View results in table mode"); _tableButton->setFixedSize(24, 24); _tableButton->setFlat(true); _tableButton->setCheckable(true); _tableButton->setChecked(true); // Custom mode button _customButton = new QPushButton(this); _customButton->hide(); _customButton->setIcon(GuiRegistry::instance().customIcon()); _customButton->setToolTip("View results in custom UI"); _customButton->setFixedSize(24, 24); _customButton->setFlat(true); _customButton->setCheckable(true); // Create maximize button only if there are multiple results if (_multipleResults && !tabbedResults) { _maxButton = new QPushButton; _maxButton->setIcon(GuiRegistry::instance().maximizeIcon()); _maxButton->setToolTip("Maximize this output result (double-click on result's header)"); _maxButton->setFixedSize(18, 18); _maxButton->setFlat(true); VERIFY(connect(_maxButton, SIGNAL(clicked()), this, SLOT(maximizeMinimizePart()))); } auto dockWidget = qobject_cast(outputItemContentWidget->parentWidget()->parentWidget()); auto queryWidget = dockWidget->getParentQueryWidget(); _dockUndockButton = new QPushButton; _dockUndockButton->setFixedSize(18, 18); _dockUndockButton->setFlat(true); _dockUndockButton->setHidden(true); applyDockUndockSettings(!dockWidget->isFloating()); VERIFY(connect(_dockUndockButton, SIGNAL(clicked()), queryWidget, SLOT(dockUndock()))); VERIFY(connect(_textButton, SIGNAL(clicked()), outputItemContentWidget, SLOT(showText()))); VERIFY(connect(_treeButton, SIGNAL(clicked()), outputItemContentWidget, SLOT(showTree()))); VERIFY(connect(_tableButton, SIGNAL(clicked()), outputItemContentWidget, SLOT(showTable()))); VERIFY(connect(_customButton, SIGNAL(clicked()), outputItemContentWidget, SLOT(showCustom()))); _collectionIndicator = new Indicator(GuiRegistry::instance().collectionIcon()); _timeIndicator = new Indicator(GuiRegistry::instance().timeIcon()); _paging = new PagingWidget(); _collectionIndicator->hide(); _timeIndicator->hide(); _paging->hide(); QHBoxLayout *layout = new QHBoxLayout(); #ifdef __APPLE__ layout->setContentsMargins(2, 8, 5, 1); #else layout->setContentsMargins(2, 0, 5, 1); #endif layout->setSpacing(0); layout->addWidget(_collectionIndicator); layout->addWidget(_timeIndicator); QSpacerItem *hSpacer = new QSpacerItem(2000, 24, QSizePolicy::Preferred, QSizePolicy::Minimum); layout->addSpacerItem(hSpacer); layout->addWidget(_paging); layout->addWidget(createVerticalLine()); layout->addSpacing(2); if (outputItemContentWidget->isCustomModeSupported()) { layout->addWidget(_customButton, 0, Qt::AlignRight); _customButton->show(); } if (outputItemContentWidget->isTreeModeSupported()) { layout->addWidget(_treeButton, 0, Qt::AlignRight); _treeButton->show(); } if (outputItemContentWidget->isTableModeSupported()) { layout->addWidget(_tableButton, 0, Qt::AlignRight); _tableButton->show(); } if (outputItemContentWidget->isTextModeSupported()) layout->addWidget(_textButton, 0, Qt::AlignRight); if (_multipleResults) layout->addWidget(_maxButton, 0, Qt::AlignRight); layout->addSpacing(3); _verticalLine = createVerticalLine(); _verticalLine->setHidden(true); layout->addWidget(_verticalLine); layout->addWidget(_dockUndockButton); setLayout(layout); // Update dock/undock button visibility if (_multipleResults) updateDockButtonOnToggleOrientation(); else { _verticalLine->setVisible(true); _dockUndockButton->setVisible(true); } if(tabbedResults) setStyleSheet("background-color: white"); } void OutputItemHeaderWidget::mouseDoubleClickEvent(QMouseEvent *) { maximizeMinimizePart(); } void OutputItemHeaderWidget::showText() { _textButton->setIcon(GuiRegistry::instance().textHighlightedIcon()); _textButton->setChecked(true); _treeButton->setIcon(GuiRegistry::instance().treeIcon()); _treeButton->setChecked(false); _tableButton->setIcon(GuiRegistry::instance().tableIcon()); _tableButton->setChecked(false); _customButton->setIcon(GuiRegistry::instance().customIcon()); _customButton->setChecked(false); } void OutputItemHeaderWidget::showTree() { _textButton->setIcon(GuiRegistry::instance().textIcon()); _textButton->setChecked(false); _treeButton->setIcon(GuiRegistry::instance().treeHighlightedIcon()); _treeButton->setChecked(true); _tableButton->setIcon(GuiRegistry::instance().tableIcon()); _tableButton->setChecked(false); _customButton->setIcon(GuiRegistry::instance().customIcon()); _customButton->setChecked(false); } void OutputItemHeaderWidget::showTable() { _textButton->setIcon(GuiRegistry::instance().textIcon()); _textButton->setChecked(false); _treeButton->setIcon(GuiRegistry::instance().treeIcon()); _treeButton->setChecked(false); _tableButton->setIcon(GuiRegistry::instance().tableHighlightedIcon()); _tableButton->setChecked(true); _customButton->setIcon(GuiRegistry::instance().customIcon()); _customButton->setChecked(false); } void OutputItemHeaderWidget::showCustom() { _textButton->setIcon(GuiRegistry::instance().textIcon()); _textButton->setChecked(false); _treeButton->setIcon(GuiRegistry::instance().treeIcon()); _treeButton->setChecked(false); _tableButton->setIcon(GuiRegistry::instance().tableIcon()); _tableButton->setChecked(false); _customButton->setIcon(GuiRegistry::instance().customHighlightedIcon()); _customButton->setChecked(true); } void OutputItemHeaderWidget::applyDockUndockSettings(bool isDocking) { if (isDocking) { _dockUndockButton->setIcon(GuiRegistry::instance().undockIcon()); _dockUndockButton->setToolTip("Undock into separate window"); } else { _dockUndockButton->setIcon(GuiRegistry::instance().dockIcon()); _dockUndockButton->setToolTip("Dock into main window"); } } void OutputItemHeaderWidget::toggleOrientation(Qt::Orientation orientation) { if (!_firstItem && !_lastItem) return; _orientation = orientation; updateDockButtonOnToggleOrientation(); } void OutputItemHeaderWidget::setTime(const QString &time) { _timeIndicator->setVisible(!time.isEmpty()); _timeIndicator->setText(time); } void OutputItemHeaderWidget::setCollection(const QString &collection) { _collectionIndicator->setVisible(!collection.isEmpty()); _collectionIndicator->setText(collection); } void OutputItemHeaderWidget::maximizeMinimizePart() { // No maximize/minimize behaviour if there is only one query result if (!_multipleResults) return; if (_maximized) { // restoring original size emit restoredSize(); _maxButton->setIcon(GuiRegistry::instance().maximizeIcon()); _maxButton->setToolTip("Maximize this output result (double-click on result header)"); updateDockButtonOnToggleOrientation(); } else { // maximizing emit maximizedPart(); _maxButton->setIcon(GuiRegistry::instance().minimizeIcon()); _maxButton->setToolTip("Restore back to original size (double-click on result header)"); _verticalLine->setVisible(true); _dockUndockButton->setVisible(true); } _maximized = !_maximized; } void OutputItemHeaderWidget::updateDockButtonOnToggleOrientation() const { if (!_multipleResults) return; if (_firstItem) { _verticalLine->setVisible(Qt::Vertical == _orientation); _dockUndockButton->setVisible(Qt::Vertical == _orientation); } else if (_lastItem) { _verticalLine->setVisible(Qt::Horizontal == _orientation); _dockUndockButton->setVisible(Qt::Horizontal == _orientation); } else { _verticalLine->setVisible(false); _dockUndockButton->setVisible(false); } } } ================================================ FILE: src/robomongo/gui/widgets/workarea/OutputItemHeaderWidget.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QPushButton; QT_END_NAMESPACE #include "robomongo/gui/editors/PlainJavaScriptEditor.h" #include "robomongo/core/domain/MongoShellResult.h" #include "robomongo/gui/widgets/workarea/PagingWidget.h" namespace Robomongo { class OutputItemContentWidget; class Indicator; class OutputItemHeaderWidget : public QFrame { Q_OBJECT public: OutputItemHeaderWidget(OutputItemContentWidget *output, bool multipleResults, bool tabbedResults, bool firstItem, bool lastItem, QWidget *parent = nullptr); PagingWidget *paging() const { return _paging; } void showText(); void showTree(); void showTable(); void showCustom(); void applyDockUndockSettings(bool docking); void toggleOrientation(Qt::Orientation orientation); protected: virtual void mouseDoubleClickEvent(QMouseEvent *); Q_SIGNALS: void restoredSize(); void maximizedPart(); public Q_SLOTS: void setTime(const QString &time); void setCollection(const QString &collection); void maximizeMinimizePart(); private: void updateDockButtonOnToggleOrientation() const; QPushButton *_textButton; QPushButton *_treeButton; QPushButton *_tableButton; QPushButton *_customButton; QPushButton *_maxButton; QFrame *_verticalLine; QPushButton *_dockUndockButton; Indicator *_collectionIndicator; Indicator *_timeIndicator; PagingWidget *_paging; bool _maximized; bool _multipleResults; bool _firstItem; bool _lastItem; Qt::Orientation _orientation; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/OutputWidget.cpp ================================================ #include "robomongo/gui/widgets/workarea/OutputWidget.h" #include #include #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/domain/MongoShell.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/widgets/workarea/OutputItemContentWidget.h" #include "robomongo/gui/widgets/workarea/ProgressBarPopup.h" #include "robomongo/gui/widgets/workarea/WorkAreaTabBar.h" namespace Robomongo { OutputWidget::OutputWidget(QWidget *parent) : QTabWidget(parent), _splitter(new QSplitter), _tabbedResults(false) { _splitter->setOrientation(Qt::Vertical); _splitter->setHandleWidth(1); _splitter->setContentsMargins(0, 0, 0, 0); QVBoxLayout *layout = new QVBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(_splitter); setLayout(layout); setTabsClosable(true); setElideMode(Qt::ElideRight); setMovable(true); #ifdef __APPLE__ setDocumentMode(false); #else setDocumentMode(true); #endif setStyleSheet(buildStyleSheet()); VERIFY(connect(this, SIGNAL(tabCloseRequested(int)), SLOT(tabCloseRequested(int)))); _progressBarPopup = new ProgressBarPopup(this); } void OutputWidget::present(MongoShell *shell, const std::vector &results) { if (_prevResultsCount > 0) clearAllParts(); int const RESULTS_SIZE = _prevResultsCount = results.size(); bool const multipleResults = (RESULTS_SIZE > 1); _tabbedResults = (RESULTS_SIZE > 2); _splitter->setHidden(_tabbedResults ? true : false); _outputItemContentWidgets.clear(); while (count() > 0) removeTab(count()-1); for (int i = 0; i < RESULTS_SIZE; ++i) { MongoShellResult shellResult = results[i]; double secs = shellResult.elapsedMs() / 1000.f; ViewMode viewMode = AppRegistry::instance().settingsManager()->viewMode(); if (_prevViewModes.size()) { viewMode = _prevViewModes.back(); _prevViewModes.pop_back(); } bool const firstItem = (0 == i); bool const lastItem = (RESULTS_SIZE-1 == i); OutputItemContentWidget* item = nullptr; if (shellResult.documents().size() > 0) { item = new OutputItemContentWidget(viewMode, shell, QtUtils::toQString(shellResult.type()), shellResult.documents(), shellResult.queryInfo(), secs, multipleResults, _tabbedResults, firstItem, lastItem, shellResult.aggrInfo(), this); } else { item = new OutputItemContentWidget(viewMode, shell, QtUtils::toQString(shellResult.response()), secs, multipleResults, _tabbedResults, firstItem, lastItem, shellResult.aggrInfo(), this); } VERIFY(connect(item, SIGNAL(maximizedPart()), this, SLOT(maximizePart()))); VERIFY(connect(item, SIGNAL(restoredSize()), this, SLOT(restoreSize()))); if (_tabbedResults) { addTab(item, QString::fromStdString(shellResult.statementShort())); setTabToolTip(i, QString::fromStdString(shellResult.statement())); } else _splitter->addWidget(item); _outputItemContentWidgets.push_back(item); } tryToMakeAllPartsEqualInSize(); } void OutputWidget::updatePart(int partIndex, const MongoQueryInfo &queryInfo, const std::vector &documents) { if (!_tabbedResults && partIndex >= _splitter->count()) return; OutputItemContentWidget* outputItemContentWidget = nullptr; if(_tabbedResults) outputItemContentWidget = qobject_cast(currentWidget()); else outputItemContentWidget = qobject_cast(_splitter->widget(partIndex)); outputItemContentWidget->updateWithInfo(queryInfo, documents); outputItemContentWidget->refreshOutputItem(); } void OutputWidget::updatePart(int partIndex, const AggrInfo &agrrInfo, const std::vector &documents) { if (partIndex >= _splitter->count()) return; auto outputItemContentWidget = qobject_cast(_splitter->widget(partIndex)); outputItemContentWidget->updateWithInfo(agrrInfo, documents); outputItemContentWidget->refreshOutputItem(); } void OutputWidget::toggleOrientation() { bool const horizontal = _splitter->orientation() == Qt::Horizontal; _splitter->setOrientation(horizontal ? Qt::Vertical : Qt::Horizontal); int const COUNT = _splitter->count(); if (COUNT > 1) { auto const* firstItem = qobject_cast(_splitter->widget(0)); auto const* lastItem = qobject_cast(_splitter->widget(COUNT-1)); firstItem->toggleOrientation(_splitter->orientation()); lastItem->toggleOrientation(_splitter->orientation()); } } void OutputWidget::switchMode( std::function modeFunc ) { if (_tabbedResults) { QWidget* currentTab { widget(currentIndex()) }; modeFunc(qobject_cast(currentTab)); } else { for (int i = 0; i < _splitter->count(); i++) { QWidget* widget { _splitter->widget(i) }; modeFunc(qobject_cast(widget)); } } } void OutputWidget::enterTreeMode() { switchMode(&OutputItemContentWidget::showTree); } void OutputWidget::enterTextMode() { switchMode(&OutputItemContentWidget::showText); } void OutputWidget::enterTableMode() { switchMode(&OutputItemContentWidget::showTable); } void OutputWidget::enterCustomMode() { switchMode(&OutputItemContentWidget::showCustom); } void OutputWidget::maximizePart() { OutputItemContentWidget *result = qobject_cast(sender()); int count = _splitter->count(); for (int i = 0; i < count; i++) { OutputItemContentWidget *widget = (OutputItemContentWidget *) _splitter->widget(i); if (widget != result) widget->hide(); } } void OutputWidget::tabCloseRequested(int index) { removeTab(index); } void OutputWidget::restoreSize() { int count = _splitter->count(); for (int i = 0; i < count; i++) { OutputItemContentWidget *widget = (OutputItemContentWidget *) _splitter->widget(i); widget->show(); } } int OutputWidget::resultIndex(OutputItemContentWidget *result) { return _splitter->indexOf(result); } void OutputWidget::showProgress() { QSize siz = size(); QPoint point(siz.width() / 2 - ProgressBarPopup::width/2, siz.height() / 2 - ProgressBarPopup::height/2); _progressBarPopup->move(point); _progressBarPopup->show(); } void OutputWidget::hideProgress() { _progressBarPopup->hide(); } bool OutputWidget::progressBarActive() const { return _progressBarPopup->isVisible(); } void OutputWidget::applyDockUndockSettings(bool isDocking) const { for (auto const& item : _outputItemContentWidgets) { item->applyDockUndockSettings(isDocking); } } Qt::Orientation OutputWidget::getOrientation() const { return _splitter->orientation(); } void OutputWidget::mouseReleaseEvent(QMouseEvent * event) { if (event->button() != Qt::MidButton) return; int const tabIndex = tabBar()->tabAt(event->pos()); removeTab(tabIndex); QTabWidget::mouseReleaseEvent(event); } void OutputWidget::clearAllParts() { _prevViewModes.clear(); while (_splitter->count() > 0) { OutputItemContentWidget *widget = (OutputItemContentWidget *)_splitter->widget(_splitter->count()-1); _prevViewModes.push_back(widget->viewMode()); widget->hide(); delete widget; } } QString OutputWidget::buildStyleSheet() { QColor background = palette().window().color(); QColor gradientZero = QColor("#ffffff"); //Qt::white;//.lighter(103); QColor gradientOne = background.lighter(104); //Qt::white;//.lighter(103); QColor gradientTwo = background.lighter(108); //.lighter(103); QColor selectedBorder = background.darker(103); QString aga1 = gradientOne.name(); QString aga2 = gradientTwo.name(); QString aga3 = background.name(); #ifdef __APPLE__ QString styles = QString( "QTabWidget::pane { background-color: white; }" // This style disables default styling under Mac "QTabWidget::tab-bar {" "alignment: left;" "border-top-left-radius: 2px;" "border-top-right-radius: 2px;" "}" "QTabBar::close-button { " "margin-top: 2px;" "image: url(:/robomongo/icons/close_2_Mac_16x16.png);" "width: 10px;" "height: 10px;" "}" "QTabBar::close-button:hover { " "image: url(:/robomongo/icons/close_hover_16x16.png);" "width: 15px;" "height: 15px;" "}" "QTabBar::tab:selected { " "background: white; /*#E1E1E1*/; " "color: #282828;" "} " "QTabBar::tab {" "color: #505050;" "font-size: 11px;" "background: %1;" "border-top-left-radius: 2px;" "border-top-right-radius: 2px;" "border-right: 1px solid #aaaaaa;" "padding: 8px 0px 5px 0px;" // top r b l "}" ).arg(QWidget::palette().color(QWidget::backgroundRole()).darker(114).name()); #else // Wind and Linux QString styles = QString( "QTabBar::close-button { " "image: url(:/robomongo/icons/close_2_16x16.png);" "width: 10px;" "height: 10px;" "}" "QTabBar::close-button:hover { " "image: url(:/robomongo/icons/close_hover_16x16.png);" "width: 15px;" "height: 15px;" "}" "QTabBar::tab {" "background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1," "stop: 0 #F0F0F0, stop: 0.4 #DEDEDE," "stop: 0.5 #E6E6E6, stop: 1.0 #E1E1E1);" "border: 1px solid #C4C4C3;" "border-bottom-color: #B8B7B6;" // #C2C7CB same as the pane color "border-top-left-radius: 6px;" "border-top-right-radius: 6px;" "padding: 4px 4px 5px 8px;" "max-width: 200px;" "margin: 0px;" "margin-left: 1px;" "margin-right: -3px;" // it should be -(tab:first:margin-left + tab:last:margin-left) to fix incorrect text elidement "}" "QTabBar::tab:selected, QTabBar::tab:hover {" "/* background: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0," "stop: 0 %1, stop: 0.3 %2," //#fafafa, #f4f4f4 "stop: 0.6 %3, stop: 1.0 %4); */" //#e7e7e7, #fafafa "background-color: white;" "}" "QTabBar::tab:selected {" "margin-top: 1px;" "border-color: #9B9B9B;" // "border-bottom-color: %4;" //#fafafa "}" "QTabBar::tab:!selected {" "margin-top: 2px;" // make non-selected tabs look smaller "}" ).arg(gradientZero.name(), gradientOne.name(), gradientTwo.name(), "#ffffff"); #endif return styles; } void OutputWidget::tryToMakeAllPartsEqualInSize() { int resultsCount = _splitter->count(); if (resultsCount <= 1) return; int dimension = _splitter->orientation() == Qt::Vertical ? _splitter->height() : _splitter->width(); int step = dimension / resultsCount; QList partSizes; for (int i = 0; i < resultsCount; ++i) { partSizes << step; } _splitter->setSizes(partSizes); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/OutputWidget.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QSplitter; QT_END_NAMESPACE #include "robomongo/core/domain/MongoShellResult.h" #include "robomongo/core/Enums.h" namespace Robomongo { class OutputItemContentWidget; class ProgressBarPopup; class MongoShell; class OutputWidget : public QTabWidget { Q_OBJECT public: explicit OutputWidget(QWidget *parent); void present(MongoShell *shell, const std::vector &documents); void updatePart(int partIndex, const MongoQueryInfo &queryInfo, const std::vector &documents); void updatePart(int partIndex, const AggrInfo &agrrInfo, const std::vector &documents); void toggleOrientation(); void switchMode(std::function modeFunc); void enterTreeMode(); void enterTextMode(); void enterTableMode(); void enterCustomMode(); int resultIndex(OutputItemContentWidget *result); void showProgress(); void hideProgress(); bool progressBarActive() const; void applyDockUndockSettings(bool isDocking) const; Qt::Orientation getOrientation() const; private Q_SLOTS: void restoreSize(); void maximizePart(); void tabCloseRequested(int); private: void mouseReleaseEvent(QMouseEvent *event); void clearAllParts(); QString buildStyleSheet(); void tryToMakeAllPartsEqualInSize(); bool _tabbedResults; std::vector _prevViewModes; int _prevResultsCount; QSplitter *_splitter; ProgressBarPopup *_progressBarPopup; std::vector _outputItemContentWidgets; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/PagingWidget.cpp ================================================ #include "robomongo/gui/widgets/workarea/PagingWidget.h" #include #include #include #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/AppRegistry.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/gui/GuiRegistry.h" namespace { QPushButton *createButtonWithIcon(const QIcon &icon) { QPushButton *button = new QPushButton; button->setIcon(icon); button->setFixedSize(24, 24); button->setFlat(true); return button; } } namespace Robomongo { PagingWidget::PagingWidget(QWidget *parent) :BaseClass(parent) { _skipEdit = new QLineEdit; _batchSizeEdit = new QLineEdit; _skipEdit->setAlignment(Qt::AlignHCenter); _skipEdit->setToolTip("Skip"); _batchSizeEdit->setAlignment(Qt::AlignHCenter); _batchSizeEdit->setToolTip("Batch Size (number of documents shown at once)"); QFontMetrics metrics = _skipEdit->fontMetrics(); int width = metrics.boundingRect("00000000").width(); QRegExp rx("\\d+"); _skipEdit->setValidator(new QRegExpValidator(rx, this)); _batchSizeEdit->setValidator(new QRegExpValidator(rx, this)); _skipEdit->setFixedWidth(width); _batchSizeEdit->setFixedWidth(width); QPushButton *leftButton = createButtonWithIcon(GuiRegistry::instance().leftIcon()); QPushButton *rightButton = createButtonWithIcon(GuiRegistry::instance().rightIcon()); VERIFY(connect(leftButton, SIGNAL(clicked()), this, SLOT(leftButton_clicked()))); VERIFY(connect(rightButton, SIGNAL(clicked()), this, SLOT(rightButton_clicked()))); VERIFY(connect(_batchSizeEdit, SIGNAL(returnPressed()), this, SLOT(refresh()))); VERIFY(connect(_skipEdit, SIGNAL(returnPressed()), this, SLOT(refresh()))); QHBoxLayout *layout = new QHBoxLayout(); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(leftButton); layout->addSpacing(0); layout->addWidget(_skipEdit); layout->addSpacing(1); layout->addWidget(_batchSizeEdit); layout->addSpacing(0); layout->addWidget(rightButton); setLayout(layout); } void PagingWidget::setSkip(int skip) { _skipEdit->setText(QString::number(skip)); show(); } void PagingWidget::setBatchSize(int batchSize) { if (batchSize <= 0) batchSize = AppRegistry::instance().settingsManager()->batchSize(); _batchSizeEdit->setText(QString::number(batchSize)); show(); } void PagingWidget::refresh() { int limit = _batchSizeEdit->text().toInt(); int skip = _skipEdit->text().toInt(); emit refreshed(skip, limit); } void PagingWidget::leftButton_clicked() { int limit = _batchSizeEdit->text().toInt(); int skip = _skipEdit->text().toInt(); emit leftClicked(skip, limit); } void PagingWidget::rightButton_clicked() { int limit = _batchSizeEdit->text().toInt(); int skip = _skipEdit->text().toInt(); emit rightClicked(skip, limit); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/PagingWidget.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QLineEdit; QT_END_NAMESPACE namespace Robomongo { class PagingWidget : public QWidget { Q_OBJECT public: typedef QWidget BaseClass; enum {pageLimit = 50}; PagingWidget(QWidget *parent = NULL); void setSkip(int skip); void setBatchSize(int limit); Q_SIGNALS: void leftClicked(int skip, int limit); void rightClicked(int skip, int limit); void refreshed(int skip, int limit); private Q_SLOTS: void leftButton_clicked(); void rightButton_clicked(); void refresh(); private: QLineEdit *_skipEdit; QLineEdit *_batchSizeEdit; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/ProgressBarPopup.cpp ================================================ #include "ProgressBarPopup.h" #include #include #include namespace Robomongo { ProgressBarPopup::ProgressBarPopup(QWidget *parent) : QFrame(parent) { setStyleSheet("QFrame {background-color: #e1e1e1; border: 0px solid #c7c5c4; border-radius: 6px;}"); QMovie *movie = new QMovie(":robomongo/icons/progress_bar.gif", QByteArray(), this); _progressLabel = new QLabel(); _progressLabel->setMovie(movie); _progressLabel->setFixedWidth(widthProgress); _progressLabel->setFixedHeight(heightProgress); movie->start(); setFixedSize(width, height); QVBoxLayout *layout = new QVBoxLayout(); layout->setContentsMargins((width-widthProgress)/2, (height-heightProgress)/2, (height-heightProgress)/2, (width-widthProgress)/2); layout->setSpacing(0); layout->addWidget(_progressLabel); setLayout(layout); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/ProgressBarPopup.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QLabel; QT_END_NAMESPACE namespace Robomongo { class ProgressBarPopup : public QFrame { Q_OBJECT public: ProgressBarPopup(QWidget *parent = NULL); enum {heightProgress = 16, widthProgress = 164, height = heightProgress+20, width = widthProgress+20 }; private: QLabel *_progressLabel; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/QueryWidget.cpp ================================================ #include "robomongo/gui/widgets/workarea/QueryWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/domain/MongoCollection.h" #include "robomongo/core/domain/MongoDatabase.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/domain/MongoShell.h" #include "robomongo/core/domain/MongoAggregateInfo.h" #include "robomongo/core/events/MongoEvents.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/widgets/workarea/OutputWidget.h" #include "robomongo/gui/widgets/workarea/ScriptWidget.h" #include "robomongo/gui/widgets/workarea/OutputItemContentWidget.h" #include "robomongo/gui/widgets/workarea/OutputItemHeaderWidget.h" #include "robomongo/gui/editors/PlainJavaScriptEditor.h" #include "robomongo/gui/editors/JSLexer.h" #include "robomongo/gui/dialogs/ChangeShellTimeoutDialog.h" using namespace mongo; namespace Robomongo { QueryWidget::QueryWidget(MongoShell *shell, QWidget *parent) : QWidget(parent), _shell(shell), _viewer(nullptr), _dock(nullptr), _isTextChanged(false) { AppRegistry::instance().bus()->subscribe(this, DocumentListLoadedEvent::Type, shell); AppRegistry::instance().bus()->subscribe(this, ScriptExecutedEvent::Type, shell); AppRegistry::instance().bus()->subscribe(this, AutocompleteResponse::Type, shell); // Make QMessageBox text selectable // setStyleSheet("QMessageBox { messagebox-text-interaction-flags: 5; }"); _scriptWidget = new ScriptWidget(_shell, this); VERIFY(connect(_scriptWidget, SIGNAL(textChanged()), this, SLOT(textChange()))); // Need to use QMainWindow in order to make use of all features of docking. // (Note: Qt full support for dock windows implemented only for QMainWindow) _viewer = new OutputWidget(this); _outputWindow = new QMainWindow; _dock = new CustomDockWidget(this); _dock->setAllowedAreas(Qt::NoDockWidgetArea); _dock->setFeatures(QDockWidget::DockWidgetFloatable); _dock->setWidget(_viewer); _dock->setTitleBarWidget(new QWidget); VERIFY(connect(_dock, SIGNAL(topLevelChanged(bool)), this, SLOT(on_dock_undock()))); _outputWindow->addDockWidget(Qt::BottomDockWidgetArea, _dock); _outputLabel = new QLabel(this); _outputLabel->setContentsMargins(0, 5, 0, 0); _outputLabel->setVisible(false); _line = new QFrame(this); _line->setFrameShape(QFrame::HLine); _line->setFrameShadow(QFrame::Raised); _mainLayout = new QVBoxLayout; _mainLayout->setSpacing(0); _mainLayout->setContentsMargins(0, 0, 0, 0); _mainLayout->addWidget(_scriptWidget); _mainLayout->addWidget(_line); _mainLayout->addWidget(_outputLabel, 0, Qt::AlignTop); _mainLayout->addWidget(_outputWindow, 1); setLayout(_mainLayout); } void QueryWidget::setScriptFocus() { _scriptWidget->setScriptFocus(); } void QueryWidget::showAutocompletion() { _scriptWidget->showAutocompletion(); } void QueryWidget::hideAutocompletion() { _scriptWidget->hideAutocompletion(); } void QueryWidget::setCurrentDatabase(const std::string & dbname) { _scriptWidget->setCurrentDatabase(dbname); } void QueryWidget::bringDockToFront() { _dock->raise(); // required for MAC only; possible Qt bug _dock->activateWindow(); } bool QueryWidget::outputWindowDocked() const { if (_dock) { return !_dock->isFloating(); } else { // _dock is not initialized yet, but it will be docked when initialized return true; } } void QueryWidget::execute() { QString query = _scriptWidget->selectedText(); if (query.isEmpty()) query = _scriptWidget->text(); showProgress(); _shell->open(QtUtils::toStdString(query)); } void QueryWidget::stop() { _shell->stop(); } void QueryWidget::toggleOrientation() { _viewer->toggleOrientation(); } void QueryWidget::openNewTab() { if (_shell) { MongoServer *server = _shell->server(); QString query = _scriptWidget->selectedText(); AppRegistry::instance().app()->openShell(server, query, _currentResult.currentDatabase(), AppRegistry::instance().settingsManager()->autoExec()); } } void QueryWidget::saveToFile() { if (_shell) { _shell->setScript(_scriptWidget->text()); if (_shell->saveToFile()) { _isTextChanged = false; updateCurrentTab(); } } } void QueryWidget::savebToFileAs() { if (_shell) { _shell->setScript(_scriptWidget->text()); if (_shell->saveToFileAs()) { _isTextChanged = false; updateCurrentTab(); } } } void QueryWidget::openFile() { if (_shell && _shell->loadFromFile()) { _scriptWidget->setText(QtUtils::toQString(_shell->query())); _isTextChanged = false; updateCurrentTab(); } } void QueryWidget::textChange() { _isTextChanged = true; updateCurrentTab(); } QueryWidget::~QueryWidget() { AppRegistry::instance().app()->closeShell(_shell); } void QueryWidget::reload() { execute(); } void QueryWidget::duplicate() { _scriptWidget->selectAll(); openNewTab(); } void QueryWidget::enterTreeMode() { _viewer->enterTreeMode(); } void QueryWidget::enterTextMode() { _viewer->enterTextMode(); } void QueryWidget::enterTableMode() { _viewer->enterTableMode(); } void QueryWidget::enterCustomMode() { _viewer->enterCustomMode(); } void QueryWidget::showProgress() { _viewer->showProgress(); } void QueryWidget::dockUndock() { // Toggle between dock/undock _dock->setFloating(!_dock->isFloating()); }; void QueryWidget::changeShellTimeout() { changeShellTimeoutDialog(); } void QueryWidget::hideProgress() { _viewer->hideProgress(); } void QueryWidget::handle(DocumentListLoadedEvent *event) { hideProgress(); if (event->isError()) { QString message = QString("Failed to load documents.\n\nError:\n%1") .arg(QtUtils::toQString(event->error().errorMessage())); QMessageBox::information(this, "Error", message); return; } // this should be in viewer, subscribed to ScriptExecutedEvent _viewer->updatePart(event->resultIndex(), event->queryInfo(), event->documents()); } void QueryWidget::handle(ScriptExecutedEvent *event) { hideProgress(); _currentResult = event->result(); if (_currentResult.results().size() == 1) { MongoShellResult const& result = _currentResult.results().front(); AggrInfo const& aggrInfo = result.aggrInfo(); if (aggrInfo.isValid && aggrInfo.resultIndex > -1) { _viewer->updatePart(aggrInfo.resultIndex, aggrInfo, _currentResult.results().front().documents()); return; } } updateCurrentTab(); displayData(event->result().results(), event->empty()); // this should be in ScriptWidget, which is subscribed to ScriptExecutedEvent _scriptWidget->setup(event->result()); activateTabContent(); if (event->isError()) { // For some cases, event error message already contains string "Error:" QString const& subStr = QString::fromStdString(event->error().errorMessage()).startsWith("Error", Qt::CaseInsensitive) ? "" : "Error:\n"; QString const& message = "Failed to execute script.\n\n" + subStr + QString::fromStdString((event->error().errorMessage())); QMessageBox::critical(this, "Error", message); } if (event->timeoutReached()) { auto const shellTimeoutSec = AppRegistry::instance().settingsManager()->shellTimeoutSec(); QString const subStr = _currentResult.results().size() > 1 ? "At least one of the scripts has reached shell timeout" : "The script has reached shell timeout"; QString const secondStr = (shellTimeoutSec > 1) ? " seconds)" : " second)"; QString messageShort = "Failed to execute all of the script. " + subStr + " (" + QString::number(shellTimeoutSec) + secondStr + " limit. "; QString messageLong = messageShort + "\n\nPlease increase the value of shell timeout using button below " "or from the main window menu \"Options->Change Shell Timeout\"."; LOG_MSG(messageShort, mongo::logger::LogSeverity::Error()); auto errorDia = new QMessageBox(QMessageBox::Icon::Critical, "Error", messageLong); auto but = new QPushButton("Change Shell Timeout"); VERIFY(connect(but, SIGNAL(clicked()), this, SLOT(changeShellTimeout()))); errorDia->addButton(but, QMessageBox::NoRole); errorDia->exec(); } } void QueryWidget::activateTabContent() { AppRegistry::instance().bus()->publish(new QueryWidgetUpdatedEvent(this, _currentResult.results().size())); _scriptWidget->setScriptFocus(); } void QueryWidget::handle(AutocompleteResponse *event) { if (event->isError()) { // Do not show error message (error should be already logged) return; } _scriptWidget->showAutocompletion(event->list, QtUtils::toQString(event->prefix) ); } void QueryWidget::on_dock_undock() { if (!_dock->isFloating()) { // If output window docked // Settings to revert to docked mode _scriptWidget->ui_queryLinesCountChanged(); _mainLayout->addWidget(_scriptWidget); _mainLayout->addWidget(_line); _mainLayout->addWidget(_outputWindow, 1); _dock->setFeatures(QDockWidget::DockWidgetFloatable); _dock->setTitleBarWidget(new QWidget); _viewer->applyDockUndockSettings(true); } else { // output window undocked(floating) // Settings for query window in order to use maximum space _scriptWidget->disableFixedHeight(); _mainLayout->addWidget(_scriptWidget, 1); _mainLayout->addWidget(_line); _mainLayout->addWidget(_outputWindow); _dock->setFeatures(QDockWidget::DockWidgetClosable); _dock->setTitleBarWidget(nullptr); _viewer->applyDockUndockSettings(false); } } void QueryWidget::updateCurrentTab() { const QString &shellQuery = QtUtils::toQString(_shell->query()); QString toolTipQuery = shellQuery.left(700); QString tabTitle, toolTipText; if (_shell) { QFileInfo fileInfo(_shell->filePath()); if (fileInfo.isFile()) { tabTitle = fileInfo.fileName(); toolTipText = fileInfo.filePath(); } } if (tabTitle.isEmpty() && shellQuery.isEmpty()) { tabTitle = "New Shell"; } else { if (tabTitle.isEmpty()) { tabTitle = shellQuery.left(41).replace(QRegExp("[\n\r\t]"), " "); toolTipText = QString("
%1
").arg(toolTipQuery); } else { //tabTitle = QString("%1 %2").arg(tabTitle).arg(shellQuery); toolTipText = QString("%1
%2
").arg(toolTipText).arg(toolTipQuery); } } if (_isTextChanged) { tabTitle = "* " + tabTitle; } emit titleChanged(tabTitle); emit toolTipChanged(toolTipText); } void QueryWidget::displayData(const std::vector &results, bool empty) { if (!empty) { bool isOutVisible = results.size() == 0 && !_scriptWidget->text().isEmpty(); if (isOutVisible) { _outputLabel->setText(" Script executed successfully, but there are no results to show."); } _outputLabel->setVisible(isOutVisible); } _viewer->present(_shell, results); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/QueryWidget.h ================================================ #pragma once #include #include #include QT_BEGIN_NAMESPACE class QLabel; class QVBoxLayout; class QMainWindow; class QPushButton; class QFrame; QT_END_NAMESPACE #include "robomongo/core/Core.h" #include "robomongo/core/domain/MongoShellResult.h" namespace Robomongo { class BsonWidget; class DocumentListLoadedEvent; class ScriptExecutedEvent; class AutocompleteResponse; class OutputWidget; class ScriptWidget; class MongoShell; class QueryWidget : public QWidget { Q_OBJECT public: class CustomDockWidget; typedef QWidget BaseClass; QueryWidget(MongoShell *shell, QWidget *parent = NULL); ~QueryWidget(); void toggleOrientation(); void activateTabContent(); void openNewTab(); void reload(); void duplicate(); void enterTreeMode(); void enterTextMode(); void enterTableMode(); void enterCustomMode(); void setScriptFocus(); void showAutocompletion(); void hideAutocompletion(); void setCurrentDatabase(const std::string &dbname); // Bring active tab's dock into front void bringDockToFront(); // Get output window's dock status bool outputWindowDocked() const; Q_SIGNALS: void titleChanged(const QString &text); void toolTipChanged(const QString &text); public Q_SLOTS: void execute(); void stop(); void saveToFile(); void savebToFileAs(); void openFile(); void textChange(); void showProgress(); void hideProgress(); void handle(DocumentListLoadedEvent *event); void handle(ScriptExecutedEvent *event); void handle(AutocompleteResponse *event); private Q_SLOTS: // Make adjustments between output window dock/undock events void on_dock_undock(); // Toggle output window between dock/undock status void dockUndock(); void changeShellTimeout(); private: void updateCurrentTab(); void displayData(const std::vector &results, bool empty); MongoShell *_shell; OutputWidget *_viewer; ScriptWidget *_scriptWidget; QLabel *_outputLabel; QDockWidget *_dock; QMainWindow *_outputWindow; QFrame *_line; QVBoxLayout *_mainLayout; MongoShellExecResult _currentResult; bool _isTextChanged; }; /* ------- class CustomDockWidget -------- */ /* Custom dock widget for output window */ class QueryWidget::CustomDockWidget : public QDockWidget { Q_OBJECT public: CustomDockWidget(QueryWidget* parent) : _parent(parent) {} QueryWidget* getParentQueryWidget() const { return _parent; } protected: // Dock instead of close void closeEvent(QCloseEvent *event) override { event->ignore(); setFloating(false); } private: QueryWidget* _parent; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/ScriptWidget.cpp ================================================ #include "robomongo/gui/widgets/workarea/ScriptWidget.h" #include #include #include #include #include #include #include "robomongo/core/domain/MongoShell.h" #include "robomongo/core/domain/MongoServer.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/gui/widgets/workarea/IndicatorLabel.h" #include "robomongo/gui/widgets/workarea/QueryWidget.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/editors/JSLexer.h" #include "robomongo/gui/editors/FindFrame.h" #include "robomongo/gui/editors/PlainJavaScriptEditor.h" namespace { bool isStopChar(const QChar &ch, bool direction) { if (ch == '=' || ch == ';' || ch == '(' || ch == ')' || ch == '{' || ch == '}' || ch == '-' || ch == '/' || ch == '+' || ch == '*' || ch == '\r' || ch == '\n' || ch == ' ' ) { return true; } if (direction) { // right direction if (ch == '.') return true; } return false; } bool isForbiddenChar(const QChar &ch) { return ch == '\"' || ch == '\''; } } namespace Robomongo { ScriptWidget::ScriptWidget(MongoShell *shell, QueryWidget *parent) : _shell(shell), _parent(parent), _textChanged(false), _disableTextAndCursorNotifications(false) { setStyleSheet("QFrame {background-color: rgb(255, 255, 255); border: 0px solid #c7c5c4;" "border-radius: 0px; margin: 0px; padding: 0px;}"); _queryText = new FindFrame(this); _topStatusBar = new TopStatusBar(_shell->server()->connectionRecord()->connectionName(), _shell->server()->connectionRecord()->getFullAddress(), "loading..."); QVBoxLayout *layout = new QVBoxLayout; layout->setSpacing(0); layout->setContentsMargins(5, 1, 5, 5); layout->addWidget(_topStatusBar, 0, Qt::AlignTop); layout->addWidget(_queryText); setLayout(layout); // Query text widget configureQueryText(); _queryText->sciScintilla()->setFocus(); _queryText->sciScintilla()->installEventFilter(this); _completer = new QCompleter(this); _completer->setWidget(_queryText->sciScintilla()); _completer->setCompletionMode(QCompleter::PopupCompletion); _completer->setCaseSensitivity(Qt::CaseInsensitive); _completer->setMaxVisibleItems(20); _completer->setWrapAround(false); _completer->popup()->setFont(GuiRegistry::instance().font()); VERIFY(connect(_completer, SIGNAL(activated(const QString &)), this, SLOT(onCompletionActivated(const QString&)))); QStringListModel *model = new QStringListModel(_completer); _completer->setModel(model); setText(QtUtils::toQString(shell->query())); setTextCursor(shell->cursor()); } bool ScriptWidget::eventFilter(QObject *obj, QEvent *event) { if (obj == _queryText->sciScintilla()) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Tab) { hideAutocompletion(); return false; } } } return QFrame::eventFilter(obj, event); } void ScriptWidget::setup(const MongoShellExecResult &execResult) { setCurrentDatabase(execResult.currentDatabase(), execResult.isCurrentDatabaseValid()); setCurrentServer(execResult.currentServer(), execResult.isCurrentServerValid()); } void ScriptWidget::setText(const QString &text) { _queryText->sciScintilla()->setText(text); } void ScriptWidget::setTextCursor(const CursorPosition &cursor) { if (cursor.isNull()) { _queryText->sciScintilla()->setCursorPosition(15, 1000); return; } int column = cursor.column(); if (column < 0) { column = _queryText->sciScintilla()->text(cursor.line()).length() + column; } _queryText->sciScintilla()->setCursorPosition(cursor.line(), column); } QString ScriptWidget::text() const { return _queryText->sciScintilla()->text(); } QString ScriptWidget::selectedText() const { return _queryText->sciScintilla()->selectedText(); } void ScriptWidget::selectAll() { _queryText->sciScintilla()->selectAll(); } void ScriptWidget::setScriptFocus() { _queryText->sciScintilla()->setFocus(); } void ScriptWidget::setCurrentDatabase(const std::string &database, bool isValid) { _topStatusBar->setCurrentDatabase(database, isValid); } void ScriptWidget::setCurrentServer(const std::string &address, bool isValid) { _topStatusBar->setCurrentServer(address, isValid); } void ScriptWidget::showAutocompletion(const QStringList &list, const QString &prefix) { // do not show single autocompletion which is identical to existing prefix // or if it identical to prefix + '('. if (list.count() == 1) { if (list.at(0) == prefix || list.at(0) == (prefix + "(")) { return; } } // update list of completions QStringListModel * model = static_cast(_completer->model()); model->setStringList(list); int currentLine = 0; int currentIndex = 0; _queryText->sciScintilla()->getCursorPosition(¤tLine, ¤tIndex); int physicalLine = currentLine - _queryText->sciScintilla()->firstVisibleLine(); // "physical" line number in text editor (not logical) int lineIndexLeft = _currentAutoCompletionInfo.lineIndexLeft(); QRect rect = _queryText->sciScintilla()->rect(); rect.setWidth(550); rect.setHeight(editorHeight(physicalLine + 1)); rect.moveLeft(charWidth() * lineIndexLeft + autocompletionBoxLeftPosition() + _queryText->sciScintilla()->lineNumberMarginWidth()); _completer->complete(rect); _completer->popup()->setCurrentIndex(_completer->completionModel()->index(0, 0)); RoboScintilla* scin = static_cast(_queryText->sciScintilla()); scin->setIgnoreEnterKey(true); scin->setIgnoreTabKey(true); } void ScriptWidget::showAutocompletion() { _currentAutoCompletionInfo = sanitizeForAutocompletion(); if (_currentAutoCompletionInfo.isEmpty()) { hideAutocompletion(); return; } _shell->autocomplete(QtUtils::toStdString(_currentAutoCompletionInfo.text())); } void ScriptWidget::hideAutocompletion() { _completer->popup()->hide(); RoboScintilla *scin = static_cast(_queryText->sciScintilla()); scin->setIgnoreEnterKey(false); scin->setIgnoreTabKey(false); } void ScriptWidget::disableFixedHeight() const { _queryText->setMinimumSize(0, 0); _queryText->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); _queryText->sciScintilla()->setMinimumSize(0, 0); _queryText->sciScintilla()->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); _queryText->sciScintilla()->setFocus(); } void ScriptWidget::ui_queryLinesCountChanged() { // Set fixed size only if output widget is docked if (_parent->outputWindowDocked()) { int lines = _queryText->sciScintilla()->lines(); int editorTotalHeight = editorHeight(lines); int maxHeight = editorHeight(18); if (editorTotalHeight > maxHeight) { editorTotalHeight = maxHeight; } // Hide & Show solves problem of UI blinking _queryText->hide(); _queryText->setFixedHeight(editorTotalHeight); _queryText->sciScintilla()->setFixedHeight(editorTotalHeight); _queryText->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); _queryText->setMaximumHeight(editorTotalHeight + FindFrame::HeightFindPanel); _queryText->sciScintilla()->setFocus(); _queryText->show(); } } void ScriptWidget::onTextChanged() { emit textChanged(); if (!_disableTextAndCursorNotifications) _textChanged = true; } void ScriptWidget::onCursorPositionChanged(int line, int index) { if (!_disableTextAndCursorNotifications && _textChanged) { showAutocompletion(); _textChanged = false; } } void ScriptWidget::onCompletionActivated(const QString &text) { int row = _currentAutoCompletionInfo.line(); int colLeft = _currentAutoCompletionInfo.lineIndexLeft(); int colRight = _currentAutoCompletionInfo.lineIndexRight(); QString line = _queryText->sciScintilla()->text(row); int selectionIndexRight = colRight + 1; // overwrite open parenthesis, if it already exists in text if (text.endsWith('(')) { if (line.length() > colRight + 1) { if (line.at(colRight + 1) == '(') { ++selectionIndexRight; } } } _disableTextAndCursorNotifications = true; _queryText->sciScintilla()->setSelection(row, colLeft, row, selectionIndexRight); _queryText->sciScintilla()->replaceSelectedText(text); _disableTextAndCursorNotifications = false; } /* ** Configure QsciScintilla query widget */ void ScriptWidget::configureQueryText() { QsciLexerJavaScript *javaScriptLexer = new JSLexer(this); javaScriptLexer->setFont(GuiRegistry::instance().font()); int height = editorHeight(1); _queryText->sciScintilla()->setMinimumHeight(height); _queryText->sciScintilla()->setFixedHeight(height); _queryText->sciScintilla()->setAppropriateBraceMatching(); _queryText->sciScintilla()->setFont(GuiRegistry::instance().font()); _queryText->sciScintilla()->setPaper(QColor(255, 0, 0, 127)); _queryText->sciScintilla()->setLexer(javaScriptLexer); _queryText->sciScintilla()->setStyleSheet("QFrame { background-color: rgb(73, 76, 78); border: 1px solid #c7c5c4; border-radius: 4px; margin: 0px; padding: 0px;}"); VERIFY(connect(_queryText->sciScintilla(), SIGNAL(linesChanged()), SLOT(ui_queryLinesCountChanged()))); VERIFY(connect(_queryText->sciScintilla(), SIGNAL(textChanged()), SLOT(onTextChanged()))); VERIFY(connect(_queryText->sciScintilla(), SIGNAL(cursorPositionChanged(int, int)), SLOT(onCursorPositionChanged(int, int)))); } /** * @brief Calculates line height of text editor */ int ScriptWidget::lineHeight() const { return _queryText->sciScintilla()->textHeight(-1); } /** * @brief Calculates char width of text editor */ int ScriptWidget::charWidth() { QFontMetrics m(_queryText->sciScintilla()->font()); return m.averageCharWidth(); } int ScriptWidget::autocompletionBoxLeftPosition() { #if defined(Q_OS_MAC) return -1; #endif // for Linux and Windows it is the same for now return 1; } /** * @brief Calculates preferable editor height for specified number of lines */ int ScriptWidget::editorHeight(int lines) const { return lines * lineHeight() + 8; } AutoCompletionInfo ScriptWidget::sanitizeForAutocompletion() { int row = 0; int col = 0; _queryText->sciScintilla()->getCursorPosition(&row, &col); QString line = _queryText->sciScintilla()->text(row); int leftStop = -1; for (int i = col - 1; i >= 0; --i) { const QChar ch = line.at(i); if (isForbiddenChar(ch)) return AutoCompletionInfo(); if (isStopChar(ch, false)) { leftStop = i; break; } } int rightStop = line.length() + 1; for (int i = col; i < line.length(); ++i) { const QChar ch = line.at(i); if (isForbiddenChar(ch)) return AutoCompletionInfo(); if (isStopChar(ch, true)) { rightStop = i; break; } } leftStop = leftStop + 1; rightStop = rightStop - 1; //int len = ondemand ? col - leftStop : rightStop - leftStop + 1; int len = col - leftStop; QString final = line.mid(leftStop, len); return AutoCompletionInfo(final, row, leftStop, rightStop); } TopStatusBar::TopStatusBar(const std::string &connectionName, const std::string &serverName, const std::string &dbName) { setContentsMargins(0, 0, 0, 0); _textColor = palette().text().color().lighter(200); _currentConnectionLabel = new Indicator(GuiRegistry::instance().connectIcon(), QString("%2").arg(_textColor.name()).arg(connectionName.c_str())); _currentConnectionLabel->setDisabled(true); _currentServerLabel = new Indicator(GuiRegistry::instance().serverIcon(), QString("%2").arg(_textColor.name()).arg(serverName.c_str())); _currentServerLabel->setDisabled(true); _currentDatabaseLabel = new Indicator(GuiRegistry::instance().databaseIcon(), QString("%2").arg(_textColor.name()).arg(dbName.c_str())); _currentDatabaseLabel->setDisabled(true); QHBoxLayout *topLayout = new QHBoxLayout; topLayout->setSpacing(0); #if defined(Q_OS_MAC) topLayout->setContentsMargins(2, 3, 2, 3); #else topLayout->setContentsMargins(2, 7, 2, 3); #endif topLayout->addWidget(_currentConnectionLabel, 0, Qt::AlignLeft); topLayout->addWidget(_currentServerLabel, 0, Qt::AlignLeft); topLayout->addWidget(_currentDatabaseLabel, 0, Qt::AlignLeft); topLayout->addStretch(1); setLayout(topLayout); } void TopStatusBar::setCurrentDatabase(const std::string &database, bool isValid) { QString color = isValid ? _textColor.name() : "red"; QString text = QString("%2") .arg(color) .arg(database.c_str()); _currentDatabaseLabel->setText(text); } void TopStatusBar::setCurrentServer(const std::string &address, bool isValid) { QString color = isValid ? _textColor.name() : "red"; QString text = QString("%2") .arg(color) .arg(detail::prepareServerAddress(address).c_str()); _currentServerLabel->setText(text); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/ScriptWidget.h ================================================ #pragma once #include QT_BEGIN_NAMESPACE class QLabel; class QCompleter; QT_END_NAMESPACE #include "robomongo/core/domain/MongoShellResult.h" #include "robomongo/core/domain/CursorPosition.h" namespace Robomongo { class FindFrame; class TopStatusBar; class MongoShell; class Indicator; class QueryWidget; class AutoCompletionInfo { public: AutoCompletionInfo() : _text(""), _line(0), _lineIndexLeft(0), _lineIndexRight(0) {} AutoCompletionInfo(const QString &text, int line, int lineIndexLeft, int lineIndexRight = 0) : _text(text), _line(line), _lineIndexLeft(lineIndexLeft), _lineIndexRight(lineIndexRight) {} QString text() const { return _text; } int line() const { return _line; } int lineIndexLeft() const { return _lineIndexLeft; } int lineIndexRight() const { return _lineIndexRight; } bool isEmpty() const { return _text.isEmpty(); } private: QString _text; // text, for which we are trying to find completions int _line; // line number in editor, where 'text' is located int _lineIndexLeft; // index of first char in the line, where 'text' is started int _lineIndexRight;// index of last char in the line, where 'text' is ended }; class ScriptWidget : public QFrame { Q_OBJECT public: ScriptWidget(MongoShell *shell, QueryWidget* parent); /** * @reimp */ bool eventFilter(QObject *obj, QEvent *e); void setup(const MongoShellExecResult & execResult); void setTextCursor(const CursorPosition &cursor = CursorPosition()); QString text() const; QString selectedText() const; void selectAll(); void setScriptFocus(); void setCurrentDatabase(const std::string &database, bool isValid = true); void setCurrentServer(const std::string &address, bool isValid = true); void showAutocompletion(const QStringList &list, const QString &prefix); void showAutocompletion(); void hideAutocompletion(); bool getDisableTextAndCursorNotifications() { return _disableTextAndCursorNotifications; } void setDisableTextAndCursorNotifications(const bool value) { _disableTextAndCursorNotifications = value; } void disableFixedHeight() const; Q_SIGNALS: void textChanged(); public Q_SLOTS: void setText(const QString &text); void ui_queryLinesCountChanged(); private Q_SLOTS: void onTextChanged(); void onCursorPositionChanged(int line, int index); void onCompletionActivated(const QString&); private: void configureQueryText(); /** * @brief Calculates line height of text editor */ int lineHeight() const; /** * @brief Calculates char width of text editor */ int charWidth(); /** * @brief Because of different fonts, differents OSes etc. we didn't find * a better way to find required position for autocompletion box. * We just hardcoded it. */ int autocompletionBoxLeftPosition(); /** * @brief Calculates preferable editor height for specified number of lines */ int editorHeight(int lines) const; AutoCompletionInfo sanitizeForAutocompletion(); FindFrame *_queryText; TopStatusBar *_topStatusBar; QCompleter *_completer; MongoShell *_shell; AutoCompletionInfo _currentAutoCompletionInfo; QueryWidget *_parent; bool _textChanged; bool _disableTextAndCursorNotifications; }; class TopStatusBar : public QFrame { Q_OBJECT public: TopStatusBar(const std::string &connectionName, const std::string &serverName, const std::string &dbName); void setCurrentDatabase(const std::string &database, bool isValid = true); void setCurrentServer(const std::string &address, bool isValid = true); void showProgress(); void hideProgress(); private: Indicator *_currentDatabaseLabel; Indicator *_currentServerLabel; Indicator *_currentConnectionLabel; QColor _textColor; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/WelcomeTab.cpp ================================================ #include "robomongo/gui/widgets/workarea/WelcomeTab.h" #ifndef __linux__ // --------------------- Windows, macOS impl --------------------------// #include #include namespace Robomongo { // ------------------ WelcomeTab WelcomeTab::WelcomeTab(QScrollArea *parent) : QWidget(parent), _parent(parent) { auto webView = new QWebEngineView(this); QUrl const URL { "http://files.studio3t.com/rm-feed_3t_io/1.4.3/index.html" }; webView->setPage(new MyWebPage(this)); webView->page()->setUrl(URL); webView->setContextMenuPolicy(Qt::NoContextMenu); webView->page()->triggerAction(QWebEnginePage::WebAction::ReloadAndBypassCache); webView->page()->profile()->setHttpCacheType(QWebEngineProfile::HttpCacheType::NoCache); auto mainLayout = new QHBoxLayout; mainLayout->setContentsMargins(-10, -10, -1, -1); mainLayout->setSizeConstraint(QLayout::SetMinimumSize); mainLayout->addWidget(webView); setLayout(mainLayout); } // ------------------ MyWebPage bool MyWebPage::acceptNavigationRequest( QUrl const& url, NavigationType type, bool /*isMainFrame*/) { if (NavigationTypeLinkClicked == type) { QDesktopServices::openUrl(url); return false; } return true; } } #else // -------------------------------- Linux impl. ------------------------------------// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/gui/GuiRegistry.h" #include "robomongo/gui/MainWindow.h" #include "robomongo/core/domain/App.h" #include "robomongo/core/EventBus.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/core/settings/ConnectionSettings.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/utils/common.h" namespace Robomongo { bool deleteOldCacheFile(QString const& absFilePath); bool saveIntoCache(QString const& fileName, QString const& fileData, QString const& lastModifiedKey, QString const& lastModifiedDate); bool saveIntoCache(QString const& fileName, QPixmap const& pixMap, QString const& lastModifiedKey, QString const& lastModifiedDate); bool saveIntoCache(QString const& fileName, QByteArray* data, QString const& lastModifiedKey, QString const& lastModifiedDate); /** * @brief Container structure to hold data of a blog link */ struct BlogInfo { BlogInfo(QString const& title, QString const& link, QString const& publishDate) : title(title), link(link), publishDate(publishDate) {} QString const title; QString const link; QString const publishDate; }; /** * @brief Custom label for identifying a blog link label */ struct BlogLinkLabel : public QLabel { BlogLinkLabel(QString const& args) : QLabel(args) {} }; QString const WhatsNew = "

%1

"; QString const BlogsHeader = "

Blog Posts

"; QString const BlogLinkTemplate = "%2"; // For info only. Starting from 1.2.1, PROJECT_VERSION is used. // URL Folder number for Pic1 and Text1 enum { URL_FOLDER_1_0_0 = 1, URL_FOLDER_1_1_0_BETA = 2, URL_FOLDER_1_1_1 = 3, URL_FOLDER_1_2_0_BETA = 4, // Starting from 1.2.1, PROJECT_VERSION is used. }; QString const IMAGE_PATH = QString(PROJECT_VERSION) + "/image.png"; QString const CONTENTS_PATH = QString(PROJECT_VERSION) + "/contents.txt"; QString const RssFileName = "rss.xml"; QString const Text1_LastModifiedDateKey("wtText1LastModifiedDate"); QString const Image1_LastModifiedDateKey("wtImage1LastModifiedDate"); QString const Rss_LastModifiedDateKey("wtRssLastModifiedDate"); auto const TEXT_TO_TAB_RATIO = 0.6; auto const IMAGE_TO_TAB_RATIO = 0.25; auto const BLOG_TO_TAB_RATIO = 0.28; /* ------------------------------------- Welcome Tab --------------------------------------- */ WelcomeTab::WelcomeTab(QScrollArea *parent) : QWidget(parent), _parent(parent) { _pic1_URL = "https://files.studio3t.com/rm-feed_3t_io/" + IMAGE_PATH; _text1_URL = "https://files.studio3t.com/rm-feed_3t_io/" + CONTENTS_PATH; _rss_URL = "https://blog.robomongo.org/rss/"; #ifdef __APPLE__ constexpr int HEADER_POINT_SIZE = 10; #else constexpr int HEADER_POINT_SIZE = 7; #endif //// What's new section _whatsNewHeader = new QLabel; _whatsNewHeader->setHidden(true); QFont headerFont { _whatsNewHeader->font() }; headerFont.setPointSize(HEADER_POINT_SIZE); _whatsNewHeader->setFont(headerFont); // _whatsNewHeader->setFont _whatsNewText = new QLabel; _whatsNewText->setTextInteractionFlags(Qt::TextSelectableByMouse); _whatsNewText->setTextFormat(Qt::RichText); _whatsNewText->setTextInteractionFlags(Qt::TextBrowserInteraction); _whatsNewText->setOpenExternalLinks(true); _whatsNewText->setWordWrap(true); _whatsNewText->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); _pic1 = new QLabel; _pic1->setTextInteractionFlags(Qt::TextSelectableByMouse); _pic1->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); _pic1->setScaledContents(true); _blogsHeader = new QLabel(BlogsHeader); _blogsHeader->setHidden(true); _blogsHeader->setFont(headerFont); //// --- Network Access Managers if (!AppRegistry::instance().settingsManager()->disableHttpsFeatures()) { auto text1Downloader = new QNetworkAccessManager; VERIFY(connect(text1Downloader, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_downloadTextReply(QNetworkReply*)))); text1Downloader->head(QNetworkRequest(_text1_URL)); auto pic1Downloader = new QNetworkAccessManager; VERIFY(connect(pic1Downloader, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_downloadPictureReply(QNetworkReply*)))); pic1Downloader->head(QNetworkRequest(_pic1_URL)); auto rssDownloader = new QNetworkAccessManager; VERIFY(connect(rssDownloader, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_downloadRssReply(QNetworkReply*)))); rssDownloader->get(QNetworkRequest(_rss_URL)); } //// --- Layouts _allBlogsButton = new QPushButton("All Blog Posts"); _allBlogsButton->setHidden(true); _allBlogsButton->setStyleSheet("color: #106CD6"); VERIFY(connect(_allBlogsButton, SIGNAL(clicked()), this, SLOT(on_allBlogsButton_clicked()))); _allBlogsButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); _blogLinksLay = new QVBoxLayout; _blogLinksLay->setAlignment(Qt::AlignLeft); auto rightLayout = new QVBoxLayout; rightLayout->setContentsMargins(20, -1, -1, -1); rightLayout->addWidget(_blogsHeader, 0, Qt::AlignTop); rightLayout->addLayout(_blogLinksLay); rightLayout->addSpacing(15); rightLayout->addWidget(_allBlogsButton, 0, Qt::AlignLeft); rightLayout->addStretch(); auto leftLayout = new QVBoxLayout; leftLayout->addWidget(_whatsNewHeader, 0, Qt::AlignTop); leftLayout->addWidget(_pic1, 0, Qt::AlignTop); leftLayout->addWidget(_whatsNewText, 0, Qt::AlignTop); leftLayout->addStretch(); leftLayout->setSizeConstraint(QLayout::SetMinimumSize); auto mainLayout = new QHBoxLayout; mainLayout->setContentsMargins(20, 20, -1, -1); mainLayout->addLayout(leftLayout); mainLayout->addSpacing(20); mainLayout->addLayout(rightLayout); mainLayout->setSizeConstraint(QLayout::SetMinimumSize); setLayout(mainLayout); } WelcomeTab::~WelcomeTab() { } void WelcomeTab::on_downloadTextReply(QNetworkReply* reply) { auto hideOrShowWhatsNewHeader = [this]() { if ((!_pic1->pixmap() || _pic1->pixmap()->isNull()) && _whatsNewText->text().isEmpty()) _whatsNewHeader->setHidden(true); else _whatsNewHeader->setVisible(true); }; if (reply->operation() == QNetworkAccessManager::HeadOperation) { if (reply->error() == QNetworkReply::NoError) { // No network error QString const& createDate = reply->header(QNetworkRequest::LastModifiedHeader).toString(); if (createDate == AppRegistry::instance().settingsManager()->cacheData(Text1_LastModifiedDateKey) && fileExists(CacheDir + _text1_URL.fileName())) { // Load from cache QFile file(CacheDir + _text1_URL.fileName()); if (!file.open(QFile::ReadOnly | QFile::Text)) return; QTextStream in(&file); QString str(in.readAll()); setWhatsNewHeaderAndText(str); hideOrShowWhatsNewHeader(); return; } else { // Get from internet reply->manager()->get(QNetworkRequest(_text1_URL)); } } else { // There is a network error // Load from cache QFile file(CacheDir + _text1_URL.fileName()); if (!file.open(QFile::ReadOnly | QFile::Text)) return; QTextStream in(&file); QString str(in.readAll()); setWhatsNewHeaderAndText(str); hideOrShowWhatsNewHeader(); return; } } else if (reply->operation() == QNetworkAccessManager::GetOperation) { // todo: handle get operation fails QString str(QUrl::fromPercentEncoding(reply->readAll())); if (str.isEmpty()) { LOG_MSG("WelcomeTab: Failed to download text file from URL. Reason: " + reply->errorString(), mongo::logger::LogSeverity::Warning()); hideOrShowWhatsNewHeader(); return; } setWhatsNewHeaderAndText(str); hideOrShowWhatsNewHeader(); saveIntoCache(_text1_URL.fileName(), str, Text1_LastModifiedDateKey, reply->header(QNetworkRequest::LastModifiedHeader).toString()); } } void WelcomeTab::on_downloadPictureReply(QNetworkReply* reply) { auto const FIFTY_PERCENT_OF_TAB = _parent->width() * IMAGE_TO_TAB_RATIO; QPixmap image; auto hideOrShowWhatsNewHeader = [this]() { if ((!_pic1->pixmap() || _pic1->pixmap()->isNull()) && _whatsNewText->text().isEmpty()) _whatsNewHeader->setHidden(true); else _whatsNewHeader->setVisible(true); }; // Network error, load from cache if (reply->error() != QNetworkReply::NoError) { image = QPixmap(CacheDir + _pic1_URL.fileName()); if (image.isNull()) return; _image = image; _pic1->setPixmap(_image); if (0 == _image.size().width()) return; _pic1->setFixedSize(FIFTY_PERCENT_OF_TAB, (FIFTY_PERCENT_OF_TAB / _image.size().width()) * _image.size().height()); adjustSize(); hideOrShowWhatsNewHeader(); return; } // No network error if (reply->operation() == QNetworkAccessManager::HeadOperation) { QString const& createDate = reply->header(QNetworkRequest::LastModifiedHeader).toString(); // If the file in URL is not newer load from cache, otherwise get from internet if (createDate == AppRegistry::instance().settingsManager()->cacheData(Image1_LastModifiedDateKey) && fileExists(CacheDir + _pic1_URL.fileName())) { image = QPixmap(CacheDir + _pic1_URL.fileName()); // Load from cache } else { // Get from internet reply->manager()->get(QNetworkRequest(_pic1_URL)); return; } } else if (reply->operation() == QNetworkAccessManager::GetOperation) { image.loadFromData(reply->readAll()); if (image.isNull()) { LOG_MSG("WelcomeTab: Failed to download image file from internet. Reason: " + reply->errorString(), mongo::logger::LogSeverity::Warning()); image = QPixmap(CacheDir + _pic1_URL.fileName()); } else { saveIntoCache(_pic1_URL.fileName(), image, Image1_LastModifiedDateKey, reply->header(QNetworkRequest::LastModifiedHeader).toString()); } } // Set the image if (image.isNull()) return; _image = image; if (0 == _image.size().width()) return hideOrShowWhatsNewHeader(); resize(); } void WelcomeTab::on_downloadRssReply(QNetworkReply* reply) { auto const THIRTY_PERCENT_OF_TAB = _parent->width() * BLOG_TO_TAB_RATIO; QByteArray data = reply->readAll(); if (data.isEmpty() || reply->error() != QNetworkReply::NoError) { // Load from cache QFile file(CacheDir + RssFileName); if (!file.open(QFile::ReadOnly | QFile::Text)) return; data = file.readAll(); } QXmlStreamReader xmlReader(data); int count = 0; int const MaxBlogCountShown = 10; QString title, link, pubDate; while (!xmlReader.atEnd()) { xmlReader.readNext(); if (xmlReader.isStartElement()) { if (xmlReader.name() == "title") title = xmlReader.readElementText(); else if (xmlReader.name() == "link") link = xmlReader.readElementText(); else if (xmlReader.name() == "pubDate") pubDate = xmlReader.readElementText().left(16); if (!pubDate.isEmpty()) { auto blogLink = new BlogLinkLabel(BlogLinkTemplate.arg(link, title)); blogLink->setMouseTracking(true); blogLink->setAttribute(Qt::WA_Hover); blogLink->installEventFilter(this); blogLink->setTextInteractionFlags(Qt::TextBrowserInteraction); blogLink->setOpenExternalLinks(true); blogLink->setWordWrap(true); blogLink->setMinimumWidth(THIRTY_PERCENT_OF_TAB); _blogLinksLay->addWidget(blogLink); _blogLinksLay->addWidget(new QLabel("" + pubDate + "")); _blogLinksLay->addSpacing(_blogLinksLay->spacing()); pubDate.clear(); ++count; if (MaxBlogCountShown == count) break; } } } _blogsHeader->setVisible(true); _allBlogsButton->setVisible(true); adjustSize(); // Save into cache saveIntoCache(RssFileName, data, Rss_LastModifiedDateKey, "NotImplemented"); // todo } void WelcomeTab::on_allBlogsButton_clicked() { QDesktopServices::openUrl(QUrl("https://blog.robomongo.org/")); } void WelcomeTab::setWhatsNewHeaderAndText(QString const& str) { if (!str.contains("\n") || str.size() == 0) return; auto const firstNewLineIndex = str.indexOf("\n"); auto const leftOfStr = str.left(firstNewLineIndex); auto const rightOfStr = str.right(str.size() - firstNewLineIndex - 7); _whatsNewHeader->setText(WhatsNew.arg(leftOfStr)); _whatsNewText->setText(rightOfStr); auto const SIXTY_PERCENT_OF_TAB = _parent->width() * TEXT_TO_TAB_RATIO; _whatsNewText->setMaximumWidth(SIXTY_PERCENT_OF_TAB); adjustSize(); } void WelcomeTab::resize() { auto const tabWidth = _parent->width(); auto const FIFTY_PERCENT_OF_TAB = _parent->width() * IMAGE_TO_TAB_RATIO; _whatsNewText->setFixedWidth(tabWidth * TEXT_TO_TAB_RATIO); _pic1->setPixmap(_image); if (0 == _image.size().width()) return; _pic1->setFixedSize(FIFTY_PERCENT_OF_TAB, (FIFTY_PERCENT_OF_TAB / _image.size().width())*_image.size().height()); _blogsHeader->setFixedWidth(tabWidth * BLOG_TO_TAB_RATIO); for (int i = 0; i < _blogLinksLay->count(); ++i) { if(auto wid = _blogLinksLay->itemAt(i)->widget()) wid->setFixedWidth(tabWidth * BLOG_TO_TAB_RATIO); } adjustSize(); } bool WelcomeTab::eventFilter(QObject *target, QEvent *event) { auto blogLinkLabel = dynamic_cast(target); // Make blog link underlined on mouse hover if (blogLinkLabel) { if (event->type() == QEvent::HoverEnter) { blogLinkLabel->setText(blogLinkLabel->text().replace("text-decoration: none;", "text-decoration: ;")); setCursor(Qt::PointingHandCursor); return true; } else if (event->type() == QEvent::HoverLeave) { blogLinkLabel->setText(blogLinkLabel->text().replace("text-decoration: ;", "text-decoration: none;")); setCursor(Qt::ArrowCursor); return true; } } return QWidget::eventFilter(target, event); } bool deleteOldCacheFile(QString const& absFilePath) { if (!fileExists(absFilePath)) return true; if (!QFile::remove(absFilePath)) { LOG_MSG("WelcomeTab: Failed to delete cached file at: " + absFilePath, mongo::logger::LogSeverity::Warning()); return false; } return true; } bool saveIntoCache(QString const& fileName, QString const& fileData, QString const& lastModifiedKey, QString const& lastModifiedDate) { if (!QDir(CacheDir).exists()) QDir().mkdir(CacheDir); else deleteOldCacheFile(CacheDir + fileName); QFile file(CacheDir + fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return false; QTextStream out(&file); out << fileData; // Save file's last modified date into settings AppRegistry::instance().settingsManager()->addCacheData(lastModifiedKey, lastModifiedDate); AppRegistry::instance().settingsManager()->save(); return true; } bool saveIntoCache(QString const& fileName, QPixmap const& pixMap, QString const& lastModifiedKey, QString const& lastModifiedDate) { if (!QDir(CacheDir).exists()) QDir().mkdir(CacheDir); else deleteOldCacheFile(CacheDir + fileName); QFile file(CacheDir + fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return false; pixMap.save(CacheDir + fileName); // Save file's last modified date into settings AppRegistry::instance().settingsManager()->addCacheData(lastModifiedKey, lastModifiedDate); AppRegistry::instance().settingsManager()->save(); return true; } bool saveIntoCache(QString const& fileName, QByteArray* data, QString const& lastModifiedKey, QString const& lastModifiedDate) { if (!QDir(CacheDir).exists()) QDir().mkdir(CacheDir); else deleteOldCacheFile(CacheDir + fileName); QFile file(CacheDir + fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return false; file.write(*data); // Save file's last modified date into settings AppRegistry::instance().settingsManager()->addCacheData(lastModifiedKey, lastModifiedDate); AppRegistry::instance().settingsManager()->save(); return true; } } #endif // -------------------------------- end of Linux impl. ------------------------------------// ================================================ FILE: src/robomongo/gui/widgets/workarea/WelcomeTab.h ================================================ #pragma once #ifndef __linux__ // ---------------------- Windows, macOS impl. --------------------------// #include #include QT_BEGIN_NAMESPACE class QScrollArea; QT_END_NAMESPACE namespace Robomongo { // ------------------ WelcomeTab class WelcomeTab : public QWidget { Q_OBJECT public: WelcomeTab(QScrollArea* parent = nullptr); QScrollArea* getParent() const { return _parent; } void resize() { /* Not implemented for Windows and macOS */} private: QScrollArea* _parent; }; // ------------------ MyWebPage class MyWebPage : public QWebEnginePage { Q_OBJECT public: MyWebPage(QObject* parent = nullptr) : QWebEnginePage(parent) {} bool acceptNavigationRequest( QUrl const& url, NavigationType type, bool /*isMainFrame*/) override; }; } #else // -------------------------------- Linux impl. ------------------------------------// #include #include QT_BEGIN_NAMESPACE class QPushButton; class QNetworkReply; class QLabel; class QVBoxLayout; class QScrollArea; class QEvent; class QUrl; QT_END_NAMESPACE namespace Robomongo { struct ConnectionEstablishedEvent; class ConnectionSettings; class WelcomeTab : public QWidget { Q_OBJECT public: WelcomeTab(QScrollArea *parent = nullptr); ~WelcomeTab(); QScrollArea* getParent() const { return _parent; } void resize(); protected: bool eventFilter(QObject *target, QEvent *event) override; private Q_SLOTS: void on_downloadTextReply(QNetworkReply* reply); void on_downloadPictureReply(QNetworkReply* reply); void on_downloadRssReply(QNetworkReply* reply); void on_allBlogsButton_clicked(); private: void setWhatsNewHeaderAndText(QString const& str); QLabel* _pic1 = nullptr; QLabel* _blogsSection; QLabel* _blogsHeader; QPushButton* _allBlogsButton = nullptr; QVBoxLayout* _blogLinksLay; QLabel* _whatsNewHeader; QLabel* _whatsNewText; QPushButton* _clearButton; QScrollArea* _parent; QPixmap _image; QUrl _pic1_URL; QUrl _text1_URL; QUrl _rss_URL; }; } #endif // -------------------------------- end of Linux ------------------------------------// ================================================ FILE: src/robomongo/gui/widgets/workarea/WorkAreaTabBar.cpp ================================================ #include "robomongo/gui/widgets/workarea/WorkAreaTabBar.h" #include #include #include namespace Robomongo { /** * @brief Creates WorkAreaTabBar, without parent widget. We are * assuming, that tab bar will be installed to (and owned by) * WorkAreaTabWidget, using QTabWidget::setTabBar(). */ WorkAreaTabBar::WorkAreaTabBar(QWidget *parent) : QTabBar(parent) { setDrawBase(false); setStyleSheet(buildStyleSheet()); _menu = new QMenu(this); _newShellAction = new QAction("&New Shell", _menu); _newShellAction->setShortcut(QKeySequence(QKeySequence::AddTab)); _reloadShellAction = new QAction("&Re-execute Query", _menu); _reloadShellAction->setShortcut(Qt::CTRL + Qt::Key_R); _duplicateShellAction = new QAction("&Duplicate Query In New Tab", _menu); _duplicateShellAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_T); _pinShellAction = new QAction("&Pin Shell", _menu); _closeShellAction = new QAction("&Close Shell", _menu); _closeShellAction->setShortcut(Qt::CTRL + Qt::Key_W); _closeOtherShellsAction = new QAction("Close &Other Shells", _menu); _closeShellsToTheRightAction = new QAction("Close Shells to the R&ight", _menu); _menu->addAction(_newShellAction); _menu->addSeparator(); _menu->addAction(_reloadShellAction); _menu->addAction(_duplicateShellAction); _menu->addSeparator(); _menu->addAction(_closeShellAction); _menu->addAction(_closeOtherShellsAction); _menu->addAction(_closeShellsToTheRightAction); } /** * @brief Overrides QTabBar::mouseReleaseEvent() in order to support * middle-mouse tab close and to implement tab context menu. */ void WorkAreaTabBar::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::MidButton) middleMouseReleaseEvent(event); else if (event->button() == Qt::RightButton) rightMouseReleaseEvent(event); // always calling base event handler, even if we // were interested by this event QTabBar::mouseReleaseEvent(event); } void WorkAreaTabBar::mouseDoubleClickEvent(QMouseEvent *event) { int tabIndex = tabAt(event->pos()); // if tab was double-clicked, ignore this action if (tabIndex >= 0) return; int currentTab = currentIndex(); if (currentTab < 0) return; emit newTabRequested(currentTab); QTabBar::mouseDoubleClickEvent(event); } /** * @brief Handles middle-mouse release event in order to close tab. */ void WorkAreaTabBar::middleMouseReleaseEvent(QMouseEvent *event) { int tabIndex = tabAt(event->pos()); if (tabIndex < 0) return; emit tabCloseRequested(tabIndex); } /** * @brief Handles right-mouse release event to show tab context menu. */ void WorkAreaTabBar::rightMouseReleaseEvent(QMouseEvent *event) { int tabIndex = tabAt(event->pos()); if (tabIndex < 0) return; // If this is a Welcome tab, do not show right click menu. // Note: Scroll area represents a WelcomeTab. auto tabWidget = qobject_cast(parentWidget()); if (qobject_cast(tabWidget->widget(tabIndex))) return; QAction *selected = _menu->exec(QCursor::pos()); if (!selected) return; emitSignalForContextMenuAction(tabIndex, selected); } /** * @brief Emits signal, based on specified action. Only actions * specified in this class are supported. If we don't know specified * action - no signal will be emited. * @param tabIndex: index of tab, for which signal will be emited. * @param action: context menu action. */ void WorkAreaTabBar::emitSignalForContextMenuAction(int tabIndex, QAction *action) { if (action == _newShellAction) emit newTabRequested(tabIndex); else if (action == _reloadShellAction) emit reloadTabRequested(tabIndex); else if (action == _duplicateShellAction) emit duplicateTabRequested(tabIndex); else if (action == _pinShellAction) emit pinTabRequested(tabIndex); else if (action == _closeShellAction) emit tabCloseRequested(tabIndex); else if (action == _closeOtherShellsAction) emit closeOtherTabsRequested(tabIndex); else if (action == _closeShellsToTheRightAction) emit closeTabsToTheRightRequested(tabIndex); } /** * @brief Builds stylesheet for this WorkAreaTabBar widget. */ QString WorkAreaTabBar::buildStyleSheet() { QColor background = palette().window().color(); QColor gradientZero = QColor("#ffffff"); //Qt::white;//.lighter(103); QColor gradientOne = background.lighter(104); //Qt::white;//.lighter(103); QColor gradientTwo = background.lighter(108); //.lighter(103); QColor selectedBorder = background.darker(103); QString aga1 = gradientOne.name(); QString aga2 = gradientTwo.name(); QString aga3 = background.name(); QString styles = QString( #ifndef __APPLE__ "QTabBar::tab:first {" "margin-left: 4px;" "} " "QTabBar::tab:last {" "margin-right: 1px;" "} " #endif "QTabBar::close-button { " #ifdef __APPLE__ "image: url(:/robomongo/icons/close_2_Mac_16x16.png);" #else "image: url(:/robomongo/icons/close_2_16x16.png);" #endif "width: 10px;" "height: 10px;" "}" "QTabBar::close-button:hover { " "image: url(:/robomongo/icons/close_hover_16x16.png);" "width: 15px;" "height: 15px;" "}" "QTabBar::tab {" "background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1," "stop: 0 #F0F0F0, stop: 0.4 #DEDEDE," "stop: 0.5 #E6E6E6, stop: 1.0 #E1E1E1);" "border: 1px solid #C4C4C3;" "border-bottom-color: #B8B7B6;" // #C2C7CB same as the pane color "border-top-left-radius: 6px;" "border-top-right-radius: 6px;" "padding: 4px 0px 5px 0px;" #ifndef __APPLE__ "max-width: 200px;" "margin: 0px;" "margin-left: 1px;" "margin-right: -3px;" // it should be -(tab:first:margin-left + tab:last:margin-left) to fix incorrect text elidement #endif "}" "QTabBar::tab:selected, QTabBar::tab:hover {" "/* background: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0," "stop: 0 %1, stop: 0.3 %2," //#fafafa, #f4f4f4 "stop: 0.6 %3, stop: 1.0 %4); */" //#e7e7e7, #fafafa "background-color: white;" "}" "QTabBar::tab:selected {" "border-color: #9B9B9B;" // "border-bottom-color: %4;" //#fafafa "}" "QTabBar::tab:!selected {" "margin-top: 2px;" // make non-selected tabs look smaller "} " #ifndef __APPLE__ "QTabBar::tab:only-one { margin-top: 2px; margin-left:4px; }" #endif ).arg(gradientZero.name(), gradientOne.name(), gradientTwo.name(), "#ffffff"); QString aga = palette().window().color().name(); return styles; } } ================================================ FILE: src/robomongo/gui/widgets/workarea/WorkAreaTabBar.h ================================================ #pragma once #include #include namespace Robomongo { /** * @brief Tab bar for WorkAreaTabWidget. */ class WorkAreaTabBar : public QTabBar { Q_OBJECT public: /** * @brief Creates WorkAreaTabBar, without parent widget. We are * assuming, that tab bar will be installed to (and owned by) * WorkAreaTabWidget, using QTabWidget::setTabBar(). */ explicit WorkAreaTabBar(QWidget* parent = 0); Q_SIGNALS: /** * @brief Emitted when user requests new tab creation. * @param tabIndex: index of tab on which context menu was called. */ void newTabRequested(int tabIndex); /** * @brief Emitted when user requests tab reload. * @param tabIndex: index of tab on which context menu was called. */ void reloadTabRequested(int tabIndex); /** * @brief Emitted when user requests tab duplication. * @param tabIndex: index of tab on which context menu was called. */ void duplicateTabRequested(int tabIndex); /** * @brief Emitted when user requests tab "pinning". * @param tabIndex: index of tab on which context menu was called. */ void pinTabRequested(int tabIndex); /** * @brief Emitted when user requests to close all other tabs. * @param tabIndex: index of tab, that should be left opened. */ void closeOtherTabsRequested(int tabIndex); /** * @brief Emitted when user requests to close all tabs to the right * of tab with 'tabIndex' index * @param tabIndex: index of tab on which context menu was called. */ void closeTabsToTheRightRequested(int tabIndex); protected: /** * @brief Overrides QTabBar::mouseReleaseEvent() in order to support * middle-mouse tab close and to implement tab context menu. */ void mouseReleaseEvent(QMouseEvent *event); /** * @brief Overrides QTabBar::mouseDoubleClickEvent() in order to * open new shell. */ void mouseDoubleClickEvent(QMouseEvent *); private: /** * @brief Handles middle-mouse release event in order to close tab. */ void middleMouseReleaseEvent(QMouseEvent *event); /** * @brief Handles right-mouse release event to show tab context menu. */ void rightMouseReleaseEvent(QMouseEvent *event); /** * @brief Emits signal, based on specified action. Only actions * specified in this class are supported. If we don't know specified * action - no signal will be emited. * @param tabIndex: index of tab, for which signal will be emited. * @param action: context menu action. */ void emitSignalForContextMenuAction(int tabIndex, QAction *action); /** * @brief Builds stylesheet for this WorkAreaTabBar widget. */ QString buildStyleSheet(); /** * @brief Tab's context menu. */ QMenu *_menu; /** * @brief Tab's context menu actions. */ QAction *_newShellAction; QAction *_reloadShellAction; QAction *_duplicateShellAction; QAction *_pinShellAction; QAction *_closeShellAction; QAction *_closeOtherShellsAction; QAction *_closeShellsToTheRightAction; }; } ================================================ FILE: src/robomongo/gui/widgets/workarea/WorkAreaTabWidget.cpp ================================================ #include "robomongo/gui/widgets/workarea/WorkAreaTabWidget.h" #include #include #include "robomongo/core/AppRegistry.h" #include "robomongo/core/utils/QtUtils.h" #include "robomongo/core/KeyboardManager.h" #include "robomongo/core/domain/MongoShell.h" #include "robomongo/core/settings/SettingsManager.h" #include "robomongo/gui/widgets/workarea/WorkAreaTabBar.h" #include "robomongo/gui/widgets/workarea/QueryWidget.h" #include "robomongo/gui/widgets/workarea/WelcomeTab.h" #include "robomongo/gui/GuiRegistry.h" namespace Robomongo { /** * @brief Creates WorkAreaTabWidget. * @param workAreaWidget: WorkAreaWidget this tab belongs to. */ WorkAreaTabWidget::WorkAreaTabWidget(QWidget *parent) : QTabWidget(parent) { auto tab = new WorkAreaTabBar(this); // This line (setTabBar()) should go before setTabsClosable(true) setTabBar(tab); setTabsClosable(true); setElideMode(Qt::ElideRight); setMovable(true); setDocumentMode(true); #ifdef Q_OS_MAC setDocumentMode(false); QFont font = tab->font(); font.setPixelSize(12); tab->setFont(font); QString styles = QString( "QTabWidget::pane { background-color: white; }" // This style disables default styling under Mac "QTabWidget::tab-bar {" "alignment: left;" "}" "QTabBar::tab:selected { " "background: white; /*#E1E1E1*/; " "color: #282828;" "} " "QTabBar::tab {" "color: #505050;" "font-size: 11px;" "background: %1;" "border-right: 1px solid #aaaaaa;" "padding: 4px 5px 7px 5px;" "}" ).arg(QWidget::palette().color(QWidget::backgroundRole()).darker(114).name()); setStyleSheet(styles); #endif VERIFY(connect(this, SIGNAL(tabCloseRequested(int)), SLOT(tabBar_tabCloseRequested(int)))); VERIFY(connect(this, SIGNAL(currentChanged(int)), SLOT(ui_currentChanged(int)))); VERIFY(connect(tab, SIGNAL(newTabRequested(int)), SLOT(ui_newTabRequested(int)))); VERIFY(connect(tab, SIGNAL(reloadTabRequested(int)), SLOT(ui_reloadTabRequested(int)))); VERIFY(connect(tab, SIGNAL(duplicateTabRequested(int)), SLOT(ui_duplicateTabRequested(int)))); VERIFY(connect(tab, SIGNAL(closeOtherTabsRequested(int)), SLOT(ui_closeOtherTabsRequested(int)))); VERIFY(connect(tab, SIGNAL(closeTabsToTheRightRequested(int)), SLOT(ui_closeTabsToTheRightRequested(int)))); auto scrollArea = new QScrollArea; _welcomeTab = new WelcomeTab(scrollArea); scrollArea->setWidget(_welcomeTab); scrollArea->setBackgroundRole(QPalette::Base); scrollArea->setWidgetResizable(true); if (!AppRegistry::instance().settingsManager()->disableHttpsFeatures()) { #ifdef __APPLE__ addTab(scrollArea, QIcon(), "Welcome"); #else addTab(scrollArea, GuiRegistry::instance().welcomeTabIcon(), "Welcome"); #endif } scrollArea->setFrameShape(QFrame::NoFrame); } void WorkAreaTabWidget::closeTab(int index) { if (index >= 0) { QueryWidget *tabWidget = queryWidget(index); removeTab(index); delete tabWidget; } } void WorkAreaTabWidget::nextTab() { int index = currentIndex(); int tabsCount = count(); if (index == tabsCount - 1) { setCurrentIndex(0); return; } if (index >= 0 && index < tabsCount - 1) { setCurrentIndex(index + 1); return; } } void WorkAreaTabWidget::previousTab() { int index = currentIndex(); if (index == 0) { setCurrentIndex(count() - 1); return; } if (index > 0) { setCurrentIndex(index - 1); return; } } QueryWidget *WorkAreaTabWidget::currentQueryWidget() { return qobject_cast(currentWidget()); } QueryWidget *WorkAreaTabWidget::queryWidget(int index) { return qobject_cast(widget(index)); } WelcomeTab* WorkAreaTabWidget::getWelcomeTab() { return _welcomeTab; } void WorkAreaTabWidget::openWelcomeTab() { auto scrollArea = qobject_cast(_welcomeTab->getParent()); if (!scrollArea) return; _welcomeTab = new WelcomeTab(scrollArea); scrollArea->setWidget(_welcomeTab); scrollArea->setBackgroundRole(QPalette::Base); #ifdef __APPLE__ QIcon icon; #else QIcon const& icon = GuiRegistry::instance().welcomeTabIcon(); #endif // If welcome tab is closed open it as first tab otherwise refresh on // it's current place. if (indexOf(scrollArea) == -1) // Welcome Tab is closed insertTab(0, scrollArea, icon, "Welcome"); else insertTab(indexOf(scrollArea), scrollArea, icon, "Welcome"); scrollArea->setFrameShape(QFrame::NoFrame); setCurrentIndex(indexOf(scrollArea)); } /** * @brief Overrides QTabWidget::keyPressEvent() in order to intercept * tab close key shortcuts (Ctrl+F4 and Ctrl+W) */ void WorkAreaTabWidget::keyPressEvent(QKeyEvent *keyEvent) { if ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->key() == Qt::Key_F4 || keyEvent->key() == Qt::Key_W)) { int index = currentIndex(); closeTab(index); return; } QueryWidget *widget = currentQueryWidget(); if (KeyboardManager::isPreviousTabShortcut(keyEvent)) { previousTab(); return; } else if (KeyboardManager::isNextTabShortcut(keyEvent)) { nextTab(); return; } else if (KeyboardManager::isNewTabShortcut(keyEvent) && widget) { widget->openNewTab(); return; } else if (KeyboardManager::isDuplicateTabShortcut(keyEvent) && widget) { widget->duplicate(); return; } else if (KeyboardManager::isSetFocusOnQueryLineShortcut(keyEvent) && widget) { widget->setScriptFocus(); return; } else if (KeyboardManager::isExecuteScriptShortcut(keyEvent) && widget) { widget->execute(); return; } else if (KeyboardManager::isAutoCompleteShortcut(keyEvent) && widget) { widget->showAutocompletion(); return; } else if (KeyboardManager::isHideAutoCompleteShortcut(keyEvent) && widget) { widget->hideAutocompletion(); return; } QTabWidget::keyPressEvent(keyEvent); } void WorkAreaTabWidget::resizeEvent(QResizeEvent* event) { QTabWidget::resizeEvent(event); if (_welcomeTab && _welcomeTab->isVisible()) _welcomeTab->resize(); } void WorkAreaTabWidget::tabBar_tabCloseRequested(int index) { closeTab(index); } void WorkAreaTabWidget::ui_newTabRequested(int index) { if (QueryWidget *query = queryWidget(index)) query->openNewTab(); } void WorkAreaTabWidget::ui_reloadTabRequested(int index) { QueryWidget *query = queryWidget(index); if (query) query->reload(); } void WorkAreaTabWidget::ui_duplicateTabRequested(int index) { QueryWidget *query = queryWidget(index); if (query) query->duplicate(); } void WorkAreaTabWidget::ui_closeOtherTabsRequested(int index) { tabBar()->moveTab(index, 0); while (count() > 1) { closeTab(1); // close second tab } } void WorkAreaTabWidget::ui_closeTabsToTheRightRequested(int index) { while (count() > index + 1) { closeTab(index + 1); // close nearest tab } } void WorkAreaTabWidget::ui_currentChanged(int index) { if (index < 0) return; QueryWidget *tabWidget = queryWidget(index); if (tabWidget) tabWidget->activateTabContent(); } void WorkAreaTabWidget::tabTextChange(const QString &text) { QWidget *send = qobject_cast(sender()); if (!send) return; setTabText(indexOf(send), text); } void WorkAreaTabWidget::tooltipTextChange(const QString &text) { QWidget *send = qobject_cast(sender()); if (!send) return; setTabToolTip(indexOf(send), text); } void WorkAreaTabWidget::handle(OpeningShellEvent *event) { const QString &title = event->shell->title(); QString shellName; if (event->shell->isExecutable()) shellName = title.isEmpty() ? " Loading..." : title; else shellName = "New Shell"; auto queryWidget = new QueryWidget(event->shell, this); VERIFY(connect(queryWidget, SIGNAL(titleChanged(const QString &)), this, SLOT(tabTextChange(const QString &)))); VERIFY(connect(queryWidget, SIGNAL(toolTipChanged(const QString &)), this, SLOT(tooltipTextChange(const QString &)))); addTab(queryWidget, shellName); setCurrentIndex(count() - 1); #if !defined(Q_OS_MAC) setTabIcon(count() - 1, GuiRegistry::instance().mongodbIcon()); #endif if (!event->shell->isExecutable()) { queryWidget->hideProgress(); queryWidget->setCurrentDatabase(event->shell->dbname()); return; } queryWidget->showProgress(); } } ================================================ FILE: src/robomongo/gui/widgets/workarea/WorkAreaTabWidget.h ================================================ #pragma once #include namespace Robomongo { class QueryWidget; class OpeningShellEvent; class WelcomeTab; /** * @brief WorkArea tab widget. Each tab represents MongoDB shell. */ class WorkAreaTabWidget : public QTabWidget { Q_OBJECT public: /** * @brief Creates WorkAreaTabWidget. * @param workAreaWidget: WorkAreaWidget this tab belongs to. */ explicit WorkAreaTabWidget(QWidget *parent = 0); void closeTab(int index); void nextTab(); void previousTab(); QueryWidget *currentQueryWidget(); QueryWidget *queryWidget(int index); WelcomeTab *getWelcomeTab(); void openWelcomeTab(); public Q_SLOTS: void handle(OpeningShellEvent *event); void tabBar_tabCloseRequested(int index); void ui_newTabRequested(int index); void ui_reloadTabRequested(int index); void ui_duplicateTabRequested(int index); void ui_closeOtherTabsRequested(int index); void ui_closeTabsToTheRightRequested(int index); void ui_currentChanged(int index); void tabTextChange(const QString &text); void tooltipTextChange(const QString &text); protected: /** * @brief Overrides QTabWidget::keyPressEvent() in order to intercept * tab close key shortcuts (Ctrl+F4 and Ctrl+W) */ virtual void keyPressEvent(QKeyEvent *event) override; void resizeEvent(QResizeEvent* event) override; private: WelcomeTab* _welcomeTab; }; } ================================================ FILE: src/robomongo/resources/gnu_gpl3_license.html ================================================

GNU GENERAL PUBLIC LICENSE

Version 3, 29 June 2007

Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

Preamble

The GNU General Public License is a free, copyleft license for software and other kinds of works.

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.

The precise terms and conditions for copying, distribution and modification follow.

TERMS AND CONDITIONS

0. Definitions.

“This License” refers to version 3 of the GNU General Public License.

“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.

To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.

A “covered work” means either the unmodified Program or a work based on the Program.

To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

1. Source Code.

The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.

A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

The Corresponding Source for a work in source code form is that same work.

2. Basic Permissions.

All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

3. Protecting Users' Legal Rights From Anti-Circumvention Law.

No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.

4. Conveying Verbatim Copies.

You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

5. Conveying Modified Source Versions.

You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

  • a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
  • b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
  • c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
  • d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

6. Conveying Non-Source Forms.

You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

  • a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
  • b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
  • c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
  • d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
  • e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

7. Additional Terms.

“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

  • a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
  • b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
  • c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
  • d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
  • e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
  • f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

8. Termination.

You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

9. Acceptance Not Required for Having Copies.

You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

10. Automatic Licensing of Downstream Recipients.

Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

11. Patents.

A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”.

A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

12. No Surrender of Others' Freedom.

If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

13. Use with the GNU Affero General Public License.

Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.

14. Revised Versions of this License.

The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

15. Disclaimer of Warranty.

THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. Limitation of Liability.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

17. Interpretation of Sections 15 and 16.

If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS

================================================ FILE: src/robomongo/resources/robo.qrc ================================================ gnu_gpl3_license.html ================================================ FILE: src/robomongo/shell/bson/json.cpp ================================================ /* Copyright 2009 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault #include "robomongo/shell/bson/json.h" #include "robomongo/shell/db/ptimeutil.h" #include #include #include #include #include "mongo/base/parse_number.h" #include "mongo/bson/json.h" #include "mongo/db/jsobj.h" #include "mongo/platform/decimal128.h" #include "mongo/platform/strtoll.h" #include "mongo/util/base64.h" #include "mongo/util/hex.h" #include "mongo/util/log.h" #include "mongo/util/str.h" #include "mongo/util/time_support.h" #include "robomongo/core/HexUtils.h" namespace mongo { namespace Robomongo { using std::unique_ptr; using std::ostringstream; using std::string; #if 0 #define MONGO_JSON_DEBUG(message) \ log() << "JSON DEBUG @ " << __FILE__ << ":" << __LINE__ << " " << __FUNCTION__ << ": " \ << message << endl; #else #define MONGO_JSON_DEBUG(message) #endif #define ALPHA "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" #define DIGIT "0123456789" #define CONTROL "\a\b\f\n\r\t\v" #define JOPTIONS "gims" // Size hints given to char vectors enum { ID_RESERVE_SIZE = 64, PAT_RESERVE_SIZE = 4096, OPT_RESERVE_SIZE = 64, FIELD_RESERVE_SIZE = 4096, STRINGVAL_RESERVE_SIZE = 4096, BINDATA_RESERVE_SIZE = 4096, BINDATATYPE_RESERVE_SIZE = 4096, NS_RESERVE_SIZE = 64, DB_RESERVE_SIZE = 64, NUMBERLONG_RESERVE_SIZE = 64, NUMBERDECIMAL_RESERVE_SIZE = 64, DATE_RESERVE_SIZE = 64 }; static const char* LBRACE = "{", * RBRACE = "}", * LBRACKET = "[", * RBRACKET = "]", * LPAREN = "(", * RPAREN = ")", * COLON = ":", * COMMA = ",", * FORWARDSLASH = "/", * SINGLEQUOTE = "'", * DOUBLEQUOTE = "\""; JParse::JParse(StringData str) : _buf(str.rawData()), _input(_buf), _input_end(_input + str.size()) {} Status JParse::parseError(StringData msg) { std::ostringstream ossmsg; ossmsg << msg; // offset will be taken using public offset() method and passed to ParseMsgAssertionException // ossmsg << ": offset:"; // ossmsg << offset(); // ossmsg << " of:"; // ossmsg << _buf; return Status(ErrorCodes::FailedToParse, ossmsg.str()); } Status JParse::value(StringData fieldName, BSONObjBuilder& builder) { MONGO_JSON_DEBUG("fieldName: " << fieldName); if (peekToken(LBRACE)) { Status ret = object(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (peekToken(LBRACKET)) { Status ret = array(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("new")) { Status ret = constructor(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("Date")) { Status ret = date(fieldName, builder); if (ret != Status::OK()) { return ret; } } //#ifdef ROBOMONGO else if (readToken("ISODate")) { Status ret = isodate(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("UUID")) { Status ret = uuid(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("LUUID")) { Status ret = luuid(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("JUUID")) { Status ret = juuid(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("NUUID") || readToken("CSUUID")) { Status ret = nuuid(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("PYUUID")) { Status ret = pyuuid(fieldName, builder); if (ret != Status::OK()) { return ret; } } //#endif else if (readToken("Timestamp")) { Status ret = timestamp(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("ObjectId")) { Status ret = objectId(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("NumberLong")) { Status ret = numberLong(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("NumberInt")) { Status ret = numberInt(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("NumberDecimal")) { Status ret = numberDecimal(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (readToken("Dbref") || readToken("DBRef")) { Status ret = dbRef(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (peekToken(FORWARDSLASH)) { Status ret = regex(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (peekToken(DOUBLEQUOTE) || peekToken(SINGLEQUOTE)) { std::string valueString; valueString.reserve(STRINGVAL_RESERVE_SIZE); Status ret = quotedString(&valueString); if (ret != Status::OK()) { return ret; } builder.append(fieldName, valueString); } else if (readToken("true")) { builder.append(fieldName, true); } else if (readToken("false")) { builder.append(fieldName, false); } else if (readToken("null")) { builder.appendNull(fieldName); } else if (readToken("undefined")) { builder.appendUndefined(fieldName); } else if (readToken("NaN")) { builder.append(fieldName, std::numeric_limits::quiet_NaN()); } else if (readToken("Infinity")) { builder.append(fieldName, std::numeric_limits::infinity()); } else if (readToken("-Infinity")) { builder.append(fieldName, -std::numeric_limits::infinity()); } else { Status ret = number(fieldName, builder); if (ret != Status::OK()) { return ret; } } return Status::OK(); } Status JParse::parse(BSONObjBuilder& builder) { return isArray() ? array("UNUSED", builder, false) : object("UNUSED", builder, false); } Status JParse::object(StringData fieldName, BSONObjBuilder& builder, bool subObject) { MONGO_JSON_DEBUG("fieldName: " << fieldName); if (!readToken(LBRACE)) { return parseError("Expecting '{'"); } // Empty object if (readToken(RBRACE)) { if (subObject) { BSONObjBuilder empty(builder.subobjStart(fieldName)); empty.done(); } return Status::OK(); } // Special object std::string firstField; firstField.reserve(FIELD_RESERVE_SIZE); Status ret = field(&firstField); if (ret != Status::OK()) { return ret; } if (firstField == "$oid") { if (!subObject) { return parseError("Reserved field name in base object: $oid"); } Status ret = objectIdObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (firstField == "$binary") { if (!subObject) { return parseError("Reserved field name in base object: $binary"); } Status ret = binaryObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (firstField == "$date") { if (!subObject) { return parseError("Reserved field name in base object: $date"); } Status ret = dateObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (firstField == "$timestamp") { if (!subObject) { return parseError("Reserved field name in base object: $timestamp"); } Status ret = timestampObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (firstField == "$regex") { if (!subObject) { return parseError("Reserved field name in base object: $regex"); } Status ret = regexObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (firstField == "$ref") { if (!subObject) { return parseError("Reserved field name in base object: $ref"); } Status ret = dbRefObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (firstField == "$undefined") { if (!subObject) { return parseError("Reserved field name in base object: $undefined"); } Status ret = undefinedObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (firstField == "$numberLong") { if (!subObject) { return parseError("Reserved field name in base object: $numberLong"); } Status ret = numberLongObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (firstField == "$numberDecimal") { if (!subObject) { return parseError("Reserved field name in base object: $numberDecimal"); } Status ret = numberDecimalObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (firstField == "$minKey") { if (!subObject) { return parseError("Reserved field name in base object: $minKey"); } Status ret = minKeyObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else if (firstField == "$maxKey") { if (!subObject) { return parseError("Reserved field name in base object: $maxKey"); } Status ret = maxKeyObject(fieldName, builder); if (ret != Status::OK()) { return ret; } } else { // firstField != // Normal object // Only create a sub builder if this is not the base object BSONObjBuilder* objBuilder = &builder; unique_ptr subObjBuilder; if (subObject) { subObjBuilder.reset(new BSONObjBuilder(builder.subobjStart(fieldName))); objBuilder = subObjBuilder.get(); } if (!readToken(COLON)) { return parseError("Expecting ':'"); } Status valueRet = value(firstField, *objBuilder); if (valueRet != Status::OK()) { return valueRet; } while (readToken(COMMA)) { std::string fieldName; fieldName.reserve(FIELD_RESERVE_SIZE); Status fieldRet = field(&fieldName); if (fieldRet != Status::OK()) { return fieldRet; } if (!readToken(COLON)) { return parseError("Expecting ':'"); } Status valueRet = value(fieldName, *objBuilder); if (valueRet != Status::OK()) { return valueRet; } } } if (!readToken(RBRACE)) { return parseError("Expecting '}' or ','"); } return Status::OK(); } Status JParse::objectIdObject(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(COLON)) { return parseError("Expected ':'"); } std::string id; id.reserve(ID_RESERVE_SIZE); Status ret = quotedString(&id); if (ret != Status::OK()) { return ret; } if (id.size() != 24) { return parseError("Expecting 24 hex digits: " + id); } if (!isHexString(id)) { return parseError("Expecting hex digits: " + id); } builder.append(fieldName, OID(id)); return Status::OK(); } Status JParse::binaryObject(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(COLON)) { return parseError("Expected ':'"); } std::string binDataString; binDataString.reserve(BINDATA_RESERVE_SIZE); Status dataRet = quotedString(&binDataString); if (dataRet != Status::OK()) { return dataRet; } if (binDataString.size() % 4 != 0) { return parseError("Invalid length base64 encoded string"); } if (!isBase64String(binDataString)) { return parseError("Invalid character in base64 encoded string"); } const std::string& binData = base64::decode(binDataString); if (!readToken(COMMA)) { return parseError("Expected ','"); } if (!readField("$type")) { return parseError("Expected second field name: \"$type\", in \"$binary\" object"); } if (!readToken(COLON)) { return parseError("Expected ':'"); } std::string binDataType; binDataType.reserve(BINDATATYPE_RESERVE_SIZE); Status typeRet = quotedString(&binDataType); if (typeRet != Status::OK()) { return typeRet; } if ((binDataType.size() != 2) || !isHexString(binDataType)) { return parseError( "Argument of $type in $bindata object must be a hex string representation of a single " "byte"); } builder.appendBinData( fieldName, binData.length(), BinDataType(fromHex(binDataType).getValue()), binData.data()); return Status::OK(); } Status JParse::dateObject(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(COLON)) { return parseError("Expected ':'"); } errno = 0; char* endptr; Date_t date; if (peekToken(DOUBLEQUOTE)) { std::string dateString; dateString.reserve(DATE_RESERVE_SIZE); Status ret = quotedString(&dateString); if (!ret.isOK()) { return ret; } StatusWith dateRet = dateFromISOString(dateString); if (!dateRet.isOK()) { return dateRet.getStatus(); } date = dateRet.getValue(); } else if (readToken(LBRACE)) { std::string fieldName; fieldName.reserve(FIELD_RESERVE_SIZE); Status ret = field(&fieldName); if (ret != Status::OK()) { return ret; } if (fieldName != "$numberLong") { return parseError("Expected field name: $numberLong for $date value object"); } if (!readToken(COLON)) { return parseError("Expecting ':'"); } // The number must be a quoted string, since large long numbers could overflow a double // and thus may not be valid JSON std::string numberLongString; numberLongString.reserve(NUMBERLONG_RESERVE_SIZE); ret = quotedString(&numberLongString); if (!ret.isOK()) { return ret; } long long numberLong; ret = parseNumberFromString(numberLongString, &numberLong); if (!ret.isOK()) { return ret; } date = Date_t::fromMillisSinceEpoch(numberLong); } else { // SERVER-11920: We should use parseNumberFromString here, but that function requires // that we know ahead of time where the number ends, which is not currently the case. date = Date_t::fromMillisSinceEpoch(strtoll(_input, &endptr, 10)); if (_input == endptr) { return parseError("Date expecting integer milliseconds"); } if (errno == ERANGE) { /* Need to handle this because jsonString outputs the value of Date_t as unsigned. * See SERVER-8330 and SERVER-8573 */ errno = 0; // SERVER-11920: We should use parseNumberFromString here, but that function // requires that we know ahead of time where the number ends, which is not currently // the case. date = Date_t::fromMillisSinceEpoch(static_cast(strtoull(_input, &endptr, 10))); if (errno == ERANGE) { return parseError("Date milliseconds overflow"); } } _input = endptr; } builder.appendDate(fieldName, date); return Status::OK(); } Status JParse::timestampObject(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(COLON)) { return parseError("Expecting ':'"); } if (!readToken(LBRACE)) { return parseError("Expecting '{' to start \"$timestamp\" object"); } if (!readField("t")) { return parseError("Expected field name \"t\" in \"$timestamp\" sub object"); } if (!readToken(COLON)) { return parseError("Expecting ':'"); } if (readToken("-")) { return parseError("Negative seconds in \"$timestamp\""); } errno = 0; char* endptr; // SERVER-11920: We should use parseNumberFromString here, but that function requires that // we know ahead of time where the number ends, which is not currently the case. uint32_t seconds = strtoul(_input, &endptr, 10); if (errno == ERANGE) { return parseError("Timestamp seconds overflow"); } if (_input == endptr) { return parseError("Expecting unsigned integer seconds in \"$timestamp\""); } _input = endptr; if (!readToken(COMMA)) { return parseError("Expecting ','"); } if (!readField("i")) { return parseError("Expected field name \"i\" in \"$timestamp\" sub object"); } if (!readToken(COLON)) { return parseError("Expecting ':'"); } if (readToken("-")) { return parseError("Negative increment in \"$timestamp\""); } errno = 0; // SERVER-11920: We should use parseNumberFromString here, but that function requires that // we know ahead of time where the number ends, which is not currently the case. uint32_t count = strtoul(_input, &endptr, 10); if (errno == ERANGE) { return parseError("Timestamp increment overflow"); } if (_input == endptr) { return parseError("Expecting unsigned integer increment in \"$timestamp\""); } _input = endptr; if (!readToken(RBRACE)) { return parseError("Expecting '}'"); } builder.append(fieldName, Timestamp(seconds, count)); return Status::OK(); } Status JParse::regexObject(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(COLON)) { return parseError("Expecting ':'"); } std::string pat; pat.reserve(PAT_RESERVE_SIZE); Status patRet = quotedString(&pat); if (patRet != Status::OK()) { return patRet; } if (readToken(COMMA)) { if (!readField("$options")) { return parseError("Expected field name: \"$options\" in \"$regex\" object"); } if (!readToken(COLON)) { return parseError("Expecting ':'"); } std::string opt; opt.reserve(OPT_RESERVE_SIZE); Status optRet = quotedString(&opt); if (optRet != Status::OK()) { return optRet; } Status optCheckRet = regexOptCheck(opt); if (optCheckRet != Status::OK()) { return optCheckRet; } builder.appendRegex(fieldName, pat, opt); } else { builder.appendRegex(fieldName, pat, ""); } return Status::OK(); } Status JParse::dbRefObject(StringData fieldName, BSONObjBuilder& builder) { BSONObjBuilder subBuilder(builder.subobjStart(fieldName)); if (!readToken(COLON)) { return parseError("DBRef: Expecting ':'"); } std::string ns; ns.reserve(NS_RESERVE_SIZE); Status ret = quotedString(&ns); if (ret != Status::OK()) { return ret; } subBuilder.append("$ref", ns); if (!readToken(COMMA)) { return parseError("DBRef: Expecting ','"); } if (!readField("$id")) { return parseError("DBRef: Expected field name: \"$id\" in \"$ref\" object"); } if (!readToken(COLON)) { return parseError("DBRef: Expecting ':'"); } Status valueRet = value("$id", subBuilder); if (valueRet != Status::OK()) { return valueRet; } if (readToken(COMMA)) { if (!readField("$db")) { return parseError("DBRef: Expected field name: \"$db\" in \"$ref\" object"); } if (!readToken(COLON)) { return parseError("DBRef: Expecting ':'"); } std::string db; db.reserve(DB_RESERVE_SIZE); ret = quotedString(&db); if (ret != Status::OK()) { return ret; } subBuilder.append("$db", db); } subBuilder.done(); return Status::OK(); } Status JParse::undefinedObject(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(COLON)) { return parseError("Expecting ':'"); } if (!readToken("true")) { return parseError("Reserved field \"$undefined\" requires value of true"); } builder.appendUndefined(fieldName); return Status::OK(); } Status JParse::numberLongObject(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(COLON)) { return parseError("Expecting ':'"); } // The number must be a quoted string, since large long numbers could overflow a double and // thus may not be valid JSON std::string numberLongString; numberLongString.reserve(NUMBERLONG_RESERVE_SIZE); Status ret = quotedString(&numberLongString); if (!ret.isOK()) { return ret; } long long numberLong; ret = parseNumberFromString(numberLongString, &numberLong); if (!ret.isOK()) { return ret; } builder.append(fieldName, numberLong); return Status::OK(); } Status JParse::numberDecimalObject(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(COLON)) return parseError("Expecting ':'"); // The number must be a quoted string, since large decimal numbers could overflow other types // and thus may not be valid JSON std::string numberDecimalString; numberDecimalString.reserve(NUMBERDECIMAL_RESERVE_SIZE); Status ret = quotedString(&numberDecimalString); if (!ret.isOK()) return ret; Decimal128 numberDecimal(numberDecimalString); builder.appendNumber(fieldName, numberDecimal); return Status::OK(); } Status JParse::minKeyObject(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(COLON)) { return parseError("Expecting ':'"); } if (!readToken("1")) { return parseError("Reserved field \"$minKey\" requires value of 1"); } builder.appendMinKey(fieldName); return Status::OK(); } Status JParse::maxKeyObject(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(COLON)) { return parseError("Expecting ':'"); } if (!readToken("1")) { return parseError("Reserved field \"$maxKey\" requires value of 1"); } builder.appendMaxKey(fieldName); return Status::OK(); } Status JParse::array(StringData fieldName, BSONObjBuilder& builder, bool subObject) { MONGO_JSON_DEBUG("fieldName: " << fieldName); uint32_t index(0); if (!readToken(LBRACKET)) { return parseError("Expecting '['"); } BSONObjBuilder* arrayBuilder = &builder; unique_ptr subObjBuilder; if (subObject) { subObjBuilder.reset(new BSONObjBuilder(builder.subarrayStart(fieldName))); arrayBuilder = subObjBuilder.get(); } if (!peekToken(RBRACKET)) { do { Status ret = value(builder.numStr(index), *arrayBuilder); if (ret != Status::OK()) { return ret; } index++; } while (readToken(COMMA)); } arrayBuilder->done(); if (!readToken(RBRACKET)) { return parseError("Expecting ']' or ','"); } return Status::OK(); } /* NOTE: this could be easily modified to allow "new" before other * constructors, but for now it only allows "new" before Date(). * Also note that unlike the interactive shell "Date(x)" and "new Date(x)" * have the same behavior. XXX: this may not be desired. */ Status JParse::constructor(StringData fieldName, BSONObjBuilder& builder) { if (readToken("Date")) { // Robo 1.4: Disabling warning "-Wunused-result", this file comes from mongo code #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-result" #elif defined(__GNUG__) // gcc #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-result" #endif date(fieldName, builder); #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUG__) // gcc #pragma GCC diagnostic pop #endif } else { return parseError("\"new\" keyword not followed by Date constructor"); } return Status::OK(); } Status JParse::date(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(LPAREN)) { return parseError("Expecting '('"); } errno = 0; char* endptr; // SERVER-11920: We should use parseNumberFromString here, but that function requires that // we know ahead of time where the number ends, which is not currently the case. Date_t date = Date_t::fromMillisSinceEpoch(strtoll(_input, &endptr, 10)); if (_input == endptr) { return parseError("Date expecting integer milliseconds"); } if (errno == ERANGE) { /* Need to handle this because jsonString outputs the value of Date_t as unsigned. * See SERVER-8330 and SERVER-8573 */ errno = 0; // SERVER-11920: We should use parseNumberFromString here, but that function requires // that we know ahead of time where the number ends, which is not currently the case. date = Date_t::fromMillisSinceEpoch(static_cast(strtoull(_input, &endptr, 10))); if (errno == ERANGE) { return parseError("Date milliseconds overflow"); } } _input = endptr; if (!readToken(RPAREN)) { return parseError("Expecting ')'"); } builder.appendDate(fieldName, date); return Status::OK(); } //#ifdef ROBOMONGO Status JParse::isodate(const StringData fieldName, BSONObjBuilder& builder) { if (!readToken(LPAREN)) { return parseError("Expecting '('"); } std::string datestr; Status ret = quotedString(&datestr); if (ret != Status::OK()) { return ret; } // We cannot use built-in parser, because it doesn't support // local times as of MongoDB 3.2 // StatusWith datet = dateFromISOString(datestr); // if (!datet.isOK()) { // return parseError("Invalid date format"); // } bool isSuccessfull = false; boost::posix_time::ptime isotime = miutil::ptimeFromIsoString(datestr, isSuccessfull); if (!isSuccessfull) { return parseError("Invalid date format"); } boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); boost::posix_time::time_duration diff = isotime - epoch; int64_t millis = diff.total_milliseconds(); Date_t datet = Date_t::fromMillisSinceEpoch(millis); if (!readToken(RPAREN)) { return parseError("Expecting ')'"); } builder.appendDate(fieldName, datet); return Status::OK(); } Status JParse::uuid(const StringData fieldName, BSONObjBuilder &builder) { return parseUuid(fieldName, builder, newUUID, ::Robomongo::DefaultEncoding); } Status JParse::luuid(const StringData fieldName, BSONObjBuilder &builder) { return parseUuid(fieldName, builder, bdtUUID, ::Robomongo::DefaultEncoding); } Status JParse::juuid(const StringData fieldName, BSONObjBuilder &builder) { return parseUuid(fieldName, builder, bdtUUID, ::Robomongo::JavaLegacy); } Status JParse::nuuid(const StringData fieldName, BSONObjBuilder &builder) { return parseUuid(fieldName, builder, bdtUUID, ::Robomongo::CSharpLegacy); } Status JParse::pyuuid(const StringData fieldName, BSONObjBuilder &builder) { return parseUuid(fieldName, builder, bdtUUID, ::Robomongo::PythonLegacy); } Status JParse::parseUuid(const StringData fieldName, BSONObjBuilder &builder, BinDataType binType, ::Robomongo::UUIDEncoding uuidEncoding) { if (!readToken(LPAREN)) { return parseError("Expecting '('"); } std::string datastr; Status ret = quotedString(&datastr); if (ret != Status::OK()) { return ret; } std::string hex = ::Robomongo::HexUtils::uuidToHex(datastr, uuidEncoding); if (hex.empty() || !::Robomongo::HexUtils::isHexString(hex)) { return parseError("Invalid hex string for UUID"); } int len; boost::scoped_array data(::Robomongo::HexUtils::fromHex(hex, &len)); if (data == NULL) return parseError("Invalid UUID"); if (!readToken(RPAREN)) return parseError("Expecting ')'"); builder.appendBinData(fieldName, len, binType, data.get()); return Status::OK(); } // #endif Status JParse::timestamp(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(LPAREN)) { return parseError("Expecting '('"); } if (readToken("-")) { return parseError("Negative seconds in \"$timestamp\""); } errno = 0; char* endptr; // SERVER-11920: We should use parseNumberFromString here, but that function requires that // we know ahead of time where the number ends, which is not currently the case. uint32_t seconds = strtoul(_input, &endptr, 10); if (errno == ERANGE) { return parseError("Timestamp seconds overflow"); } if (_input == endptr) { return parseError("Expecting unsigned integer seconds in \"$timestamp\""); } _input = endptr; if (!readToken(COMMA)) { return parseError("Expecting ','"); } if (readToken("-")) { return parseError("Negative seconds in \"$timestamp\""); } errno = 0; // SERVER-11920: We should use parseNumberFromString here, but that function requires that // we know ahead of time where the number ends, which is not currently the case. uint32_t count = strtoul(_input, &endptr, 10); if (errno == ERANGE) { return parseError("Timestamp increment overflow"); } if (_input == endptr) { return parseError("Expecting unsigned integer increment in \"$timestamp\""); } _input = endptr; if (!readToken(RPAREN)) { return parseError("Expecting ')'"); } builder.append(fieldName, Timestamp(seconds, count)); return Status::OK(); } Status JParse::objectId(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(LPAREN)) { return parseError("Expecting '('"); } std::string id; id.reserve(ID_RESERVE_SIZE); Status ret = quotedString(&id); if (ret != Status::OK()) { return ret; } if (!readToken(RPAREN)) { return parseError("Expecting ')'"); } if (id.size() != 24) { return parseError("Expecting 24 hex digits: " + id); } if (!isHexString(id)) { return parseError("Expecting hex digits: " + id); } builder.append(fieldName, OID(id)); return Status::OK(); } Status JParse::numberLong(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(LPAREN)) { return parseError("Expecting '('"); } errno = 0; char* endptr; // SERVER-11920: We should use parseNumberFromString here, but that function requires that // we know ahead of time where the number ends, which is not currently the case. int64_t val = strtoll(_input, &endptr, 10); if (errno == ERANGE) { return parseError("NumberLong out of range"); } if (_input == endptr) { return parseError("Expecting number in NumberLong"); } _input = endptr; if (!readToken(RPAREN)) { return parseError("Expecting ')'"); } builder.append(fieldName, static_cast(val)); return Status::OK(); } Status JParse::numberDecimal(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(LPAREN)) return parseError("Expecting '('"); std::string decString; decString.reserve(NUMBERDECIMAL_RESERVE_SIZE); Status ret = quotedString(&decString); if (ret != Status::OK()) return ret; Decimal128 val(decString); if (!readToken(RPAREN)) return parseError("Expecting ')'"); builder.appendNumber(fieldName, val); return Status::OK(); } Status JParse::numberInt(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(LPAREN)) { return parseError("Expecting '('"); } errno = 0; char* endptr; // SERVER-11920: We should use parseNumberFromString here, but that function requires that // we know ahead of time where the number ends, which is not currently the case. int32_t val = strtol(_input, &endptr, 10); if (errno == ERANGE) { return parseError("NumberInt out of range"); } if (_input == endptr) { return parseError("Expecting unsigned number in NumberInt"); } _input = endptr; if (!readToken(RPAREN)) { return parseError("Expecting ')'"); } builder.appendNumber(fieldName, static_cast(val)); return Status::OK(); } Status JParse::dbRef(StringData fieldName, BSONObjBuilder& builder) { BSONObjBuilder subBuilder(builder.subobjStart(fieldName)); if (!readToken(LPAREN)) { return parseError("Expecting '('"); } std::string ns; ns.reserve(NS_RESERVE_SIZE); Status refRet = quotedString(&ns); if (refRet != Status::OK()) { return refRet; } subBuilder.append("$ref", ns); if (!readToken(COMMA)) { return parseError("Expecting ','"); } Status valueRet = value("$id", subBuilder); if (valueRet != Status::OK()) { return valueRet; } if (readToken(COMMA)) { std::string db; db.reserve(DB_RESERVE_SIZE); Status dbRet = quotedString(&db); if (dbRet != Status::OK()) { return dbRet; } subBuilder.append("$db", db); } if (!readToken(RPAREN)) { return parseError("Expecting ')'"); } subBuilder.done(); return Status::OK(); } Status JParse::regex(StringData fieldName, BSONObjBuilder& builder) { if (!readToken(FORWARDSLASH)) { return parseError("Expecting '/'"); } std::string pat; pat.reserve(PAT_RESERVE_SIZE); Status patRet = regexPat(&pat); if (patRet != Status::OK()) { return patRet; } if (!readToken(FORWARDSLASH)) { return parseError("Expecting '/'"); } std::string opt; opt.reserve(OPT_RESERVE_SIZE); Status optRet = regexOpt(&opt); if (optRet != Status::OK()) { return optRet; } Status optCheckRet = regexOptCheck(opt); if (optCheckRet != Status::OK()) { return optCheckRet; } builder.appendRegex(fieldName, pat, opt); return Status::OK(); } Status JParse::regexPat(std::string* result) { MONGO_JSON_DEBUG(""); return chars(result, "/"); } Status JParse::regexOpt(std::string* result) { MONGO_JSON_DEBUG(""); return chars(result, "", JOPTIONS); } Status JParse::regexOptCheck(StringData opt) { MONGO_JSON_DEBUG("opt: " << opt); std::size_t i; for (i = 0; i < opt.size(); i++) { if (!match(opt[i], JOPTIONS)) { return parseError(string("Bad regex option: ") + opt[i]); } } return Status::OK(); } Status JParse::number(StringData fieldName, BSONObjBuilder& builder) { char* endptrll; char* endptrd; long long retll; double retd; // reset errno to make sure that we are getting it from strtod errno = 0; // SERVER-11920: We should use parseNumberFromString here, but that function requires that // we know ahead of time where the number ends, which is not currently the case. retd = strtod(_input, &endptrd); // if pointer does not move, we found no digits if (_input == endptrd) { return parseError("Bad characters in value"); } if (errno == ERANGE) { return parseError("Value cannot fit in double"); } // reset errno to make sure that we are getting it from strtoll errno = 0; // SERVER-11920: We should use parseNumberFromString here, but that function requires that // we know ahead of time where the number ends, which is not currently the case. retll = strtoll(_input, &endptrll, 10); if (endptrll < endptrd || errno == ERANGE) { // The number either had characters only meaningful for a double or // could not fit in a 64 bit int MONGO_JSON_DEBUG("Type: double"); builder.append(fieldName, retd); } else if (retll == static_cast(retll)) { // The number can fit in a 32 bit int MONGO_JSON_DEBUG("Type: 32 bit int"); builder.append(fieldName, static_cast(retll)); } else { // The number can fit in a 64 bit int MONGO_JSON_DEBUG("Type: 64 bit int"); builder.append(fieldName, retll); } _input = endptrd; if (_input >= _input_end) { return parseError("Trailing number at end of input"); } return Status::OK(); } Status JParse::field(std::string* result) { MONGO_JSON_DEBUG(""); if (peekToken(DOUBLEQUOTE) || peekToken(SINGLEQUOTE)) { // Quoted key // TODO: make sure quoted field names cannot contain null characters return quotedString(result); } else { // Unquoted key // 'isspace()' takes an 'int' (signed), so (default signed) 'char's get sign-extended // and therefore 'corrupted' unless we force them to be unsigned ... 0x80 becomes // 0xffffff80 as seen by isspace when sign-extended ... we want it to be 0x00000080 while (_input < _input_end && isspace(*reinterpret_cast(_input))) { ++_input; } if (_input >= _input_end) { return parseError("Field name expected"); } if (!match(*_input, ALPHA "_$")) { return parseError("First character in field must be [A-Za-z$_]"); } return chars(result, "", ALPHA DIGIT "_$"); } } Status JParse::quotedString(std::string* result) { MONGO_JSON_DEBUG(""); if (readToken(DOUBLEQUOTE)) { Status ret = chars(result, "\""); if (ret != Status::OK()) { return ret; } if (!readToken(DOUBLEQUOTE)) { return parseError("Expecting '\"'"); } } else if (readToken(SINGLEQUOTE)) { Status ret = chars(result, "'"); if (ret != Status::OK()) { return ret; } if (!readToken(SINGLEQUOTE)) { return parseError("Expecting '''"); } } else { return parseError("Expecting quoted string"); } return Status::OK(); } /* * terminalSet are characters that signal end of string (e.g.) [ :\0] * allowedSet are the characters that are allowed, if this is set */ Status JParse::chars(std::string* result, const char* terminalSet, const char* allowedSet) { MONGO_JSON_DEBUG("terminalSet: " << terminalSet); if (_input >= _input_end) { return parseError("Unexpected end of input"); } const char* q = _input; while (q < _input_end && !match(*q, terminalSet)) { MONGO_JSON_DEBUG("q: " << q); if (allowedSet != NULL) { if (!match(*q, allowedSet)) { _input = q; return Status::OK(); } } if (0x00 <= *q && *q <= 0x1F) { return parseError("Invalid control character"); } if (*q == '\\' && q + 1 < _input_end) { switch (*(++q)) { // Escape characters allowed by the JSON spec case '"': result->push_back('"'); break; case '\'': result->push_back('\''); break; case '\\': result->push_back('\\'); break; case '/': result->push_back('/'); break; case 'b': result->push_back('\b'); break; case 'f': result->push_back('\f'); break; case 'n': result->push_back('\n'); break; case 'r': result->push_back('\r'); break; case 't': result->push_back('\t'); break; case 'u': { // expect 4 hexdigits // TODO: handle UTF-16 surrogate characters ++q; if (q + 4 >= _input_end) { return parseError("Expecting 4 hex digits"); } if (!isHexString(StringData(q, 4))) { return parseError("Expecting 4 hex digits"); } unsigned char first = fromHex(q).getValue(); unsigned char second = fromHex(q += 2).getValue(); const std::string& utf8str = encodeUTF8(first, second); for (unsigned int i = 0; i < utf8str.size(); i++) { result->push_back(utf8str[i]); } ++q; break; } // Vertical tab character. Not in JSON spec but allowed in // our implementation according to test suite. case 'v': result->push_back('\v'); break; // Escape characters we explicity disallow case 'x': return parseError("Hex escape not supported"); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': return parseError("Octal escape not supported"); // By default pass on the unescaped character default: result->push_back(*q); break; // TODO: check for escaped control characters } ++q; } else { result->push_back(*q++); } } if (q < _input_end) { _input = q; return Status::OK(); } return parseError("Unexpected end of input"); } std::string JParse::encodeUTF8(unsigned char first, unsigned char second) const { std::ostringstream oss; if (first == 0 && second < 0x80) { oss << second; } else if (first < 0x08) { oss << char(0xc0 | (first << 2 | second >> 6)); oss << char(0x80 | (~0xc0 & second)); } else { oss << char(0xe0 | (first >> 4)); oss << char(0x80 | (~0xc0 & (first << 2 | second >> 6))); oss << char(0x80 | (~0xc0 & second)); } return oss.str(); } inline bool JParse::peekToken(const char* token) { return readTokenImpl(token, false); } inline bool JParse::readToken(const char* token) { return readTokenImpl(token, true); } bool JParse::readTokenImpl(const char* token, bool advance) { MONGO_JSON_DEBUG("token: " << token); const char* check = _input; if (token == NULL) { return false; } // 'isspace()' takes an 'int' (signed), so (default signed) 'char's get sign-extended // and therefore 'corrupted' unless we force them to be unsigned ... 0x80 becomes // 0xffffff80 as seen by isspace when sign-extended ... we want it to be 0x00000080 while (check < _input_end && isspace(*reinterpret_cast(check))) { ++check; } while (*token != '\0') { if (check >= _input_end) { return false; } if (*token++ != *check++) { return false; } } if (advance) { _input = check; } return true; } bool JParse::readField(StringData expectedField) { MONGO_JSON_DEBUG("expectedField: " << expectedField); std::string nextField; nextField.reserve(FIELD_RESERVE_SIZE); Status ret = field(&nextField); if (ret != Status::OK()) { return false; } if (expectedField != nextField) { return false; } return true; } inline bool JParse::match(char matchChar, const char* matchSet) const { if (matchSet == NULL) { return true; } if (*matchSet == '\0') { return false; } return (strchr(matchSet, matchChar) != NULL); } bool JParse::isHexString(StringData str) const { MONGO_JSON_DEBUG("str: " << str); std::size_t i; for (i = 0; i < str.size(); i++) { if (!isxdigit(str[i])) { return false; } } return true; } bool JParse::isBase64String(StringData str) const { MONGO_JSON_DEBUG("str: " << str); return base64::validate(str); } bool JParse::isArray() { return peekToken(LBRACKET); } BSONObj fromjson(const char* jsonString, int* len) { MONGO_JSON_DEBUG("jsonString: " << jsonString); if (jsonString[0] == '\0') { if (len) *len = 0; return BSONObj(); } JParse jparse(jsonString); BSONObjBuilder builder; Status ret = Status::OK(); try { ret = jparse.parse(builder); } catch (std::exception& e) { std::ostringstream message; message << "caught exception from within JSON parser: " << e.what(); // throw MsgAssertionException(17031, message.str()); throw ParseMsgAssertionException(17031, message.str(), jparse.offset(), ret.reason()); } if (ret != Status::OK()) { ostringstream message; message << "code " << ret.code() << ": " << ret.codeString() << ": " << ret.reason(); //throw MsgAssertionException(16619, message.str()); throw ParseMsgAssertionException(16619, message.str(), jparse.offset(), ret.reason()); } if (len) *len = jparse.offset(); return builder.obj(); } BSONObj fromjson(const std::string& str) { return fromjson(str.c_str()); } std::string tojson(const BSONObj& obj, JsonStringFormat format, bool pretty) { return obj.jsonString(format, pretty); } std::string tojson(const BSONArray& arr, JsonStringFormat format, bool pretty) { return arr.jsonString(format, pretty, true); } bool isArray(StringData str) { JParse parser(str); return parser.isArray(); } } /* namespace Robomongo */ } /* namespace mongo */ ================================================ FILE: src/robomongo/shell/bson/json.h ================================================ /** * Copyright (C) 2008 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #pragma once #include #include "mongo/bson/bsonobj.h" #include "mongo/base/status.h" #include "robomongo/core/Enums.h" namespace mongo { namespace Robomongo { /** * Create a BSONObj from a JSON , * string. In addition to the JSON * extensions extensions described here * , this function * accepts unquoted field names and allows single quotes to optionally be * used when specifying field names and std::string values instead of double * quotes. JSON unicode escape sequences (of the form \uXXXX) are * converted to utf8. * * @throws MsgAssertionException if parsing fails. The message included with * this assertion includes the character offset where parsing failed. */ BSONObj fromjson(const std::string& str); /** @param len will be size of JSON object in text chars. */ BSONObj fromjson(const char* str, int* len = NULL); /** * Tests whether the JSON string is an Array. * * Useful for assigning the result of fromjson to the right object type. Either: * BSONObj * BSONArray * * @example Using the method to select the proper type. * If this method returns true, the user could store the result of fromjson * inside a BSONArray, rather than a BSONObj, in order to have it print as an * array when passed to tojson. * * @param obj The JSON string to test. */ bool isArray(StringData str); /** * Convert a BSONArray to a JSON string. * * @param arr The BSON Array. * @param format The JSON format (JS, TenGen, Strict). * @param pretty Enables pretty output. */ std::string tojson(const BSONArray& arr, JsonStringFormat format = Strict, bool pretty = false); /** * Convert a BSONObj to a JSON string. * * @param obj The BSON Object. * @param format The JSON format (JS, TenGen, Strict). * @param pretty Enables pretty output. */ std::string tojson(const BSONObj& obj, JsonStringFormat format = Strict, bool pretty = false); /** * Parser class. A BSONObj is constructed incrementally by passing a * BSONObjBuilder to the recursive parsing methods. The grammar for the * element parsed is described before each function. */ class JParse { public: explicit JParse(StringData str); /* * Notation: All-uppercase symbols denote non-terminals; all other * symbols are literals. */ /* * VALUE : * STRING * | NUMBER * | NUMBERINT * | NUMBERLONG * | NUMBERDECIMAL * | OBJECT * | ARRAY * * | true * | false * | null * | undefined * * | NaN * | Infinity * | -Infinity * * | DATE * | TIMESTAMP * | REGEX * | OBJECTID * | DBREF * * | new CONSTRUCTOR */ private: Status value(StringData fieldName, BSONObjBuilder&); /* * OBJECT : * {} * | { MEMBERS } * | SPECIALOBJECT * * MEMBERS : * PAIR * | PAIR , MEMBERS * * PAIR : * FIELD : VALUE * * SPECIALOBJECT : * OIDOBJECT * | BINARYOBJECT * | DATEOBJECT * | TIMESTAMPOBJECT * | REGEXOBJECT * | REFOBJECT * | UNDEFINEDOBJECT * | NUMBERLONGOBJECT * | NUMBERDECIMALOBJECT * | MINKEYOBJECT * | MAXKEYOBJECT * */ public: Status object(StringData fieldName, BSONObjBuilder&, bool subObj = true); Status parse(BSONObjBuilder& builder); bool isArray(); private: /* The following functions are called with the '{' and the first * field already parsed since they are both implied given the * context. */ /* * OIDOBJECT : * { FIELD("$oid") : <24 character hex std::string> } */ Status objectIdObject(StringData fieldName, BSONObjBuilder&); /* * BINARYOBJECT : * { FIELD("$binary") : , * FIELD("$type") : } */ Status binaryObject(StringData fieldName, BSONObjBuilder&); /* * DATEOBJECT : * { FIELD("$date") : <64 bit signed integer for milliseconds since epoch> } */ Status dateObject(StringData fieldName, BSONObjBuilder&); /* * TIMESTAMPOBJECT : * { FIELD("$timestamp") : { * FIELD("t") : <32 bit unsigned integer for seconds since epoch>, * FIELD("i") : <32 bit unsigned integer for the increment> } } */ Status timestampObject(StringData fieldName, BSONObjBuilder&); /* * NOTE: the rules for the body of the regex are different here, * since it is quoted instead of surrounded by slashes. * REGEXOBJECT : * { FIELD("$regex") : } * | { FIELD("$regex") : , * FIELD("$options") : } */ Status regexObject(StringData fieldName, BSONObjBuilder&); /* * REFOBJECT : * { FIELD("$ref") : , * FIELD("$id") : <24 character hex std::string> } * | { FIELD("$ref") : std::string , FIELD("$id") : OBJECTID } * | { FIELD("$ref") : std::string , FIELD("$id") : OIDOBJECT } */ Status dbRefObject(StringData fieldName, BSONObjBuilder&); /* * UNDEFINEDOBJECT : * { FIELD("$undefined") : true } */ Status undefinedObject(StringData fieldName, BSONObjBuilder&); /* * NUMBERLONGOBJECT : * { FIELD("$numberLong") : "" } */ Status numberLongObject(StringData fieldName, BSONObjBuilder&); /* * NUMBERDECIMALOBJECT : * { FIELD("$numberDecimal") : "" } */ Status numberDecimalObject(StringData fieldName, BSONObjBuilder&); /* * MINKEYOBJECT : * { FIELD("$minKey") : 1 } */ Status minKeyObject(StringData fieldName, BSONObjBuilder& builder); /* * MAXKEYOBJECT : * { FIELD("$maxKey") : 1 } */ Status maxKeyObject(StringData fieldName, BSONObjBuilder& builder); /* * ARRAY : * [] * | [ ELEMENTS ] * * ELEMENTS : * VALUE * | VALUE , ELEMENTS */ Status array(StringData fieldName, BSONObjBuilder&, bool subObj = true); /* * NOTE: Currently only Date can be preceded by the "new" keyword * CONSTRUCTOR : * DATE */ Status constructor(StringData fieldName, BSONObjBuilder&); /* The following functions only parse the body of the constructor * between the parentheses, not including the constructor name */ /* * DATE : * Date( <64 bit signed integer for milliseconds since epoch> ) */ Status date(StringData fieldName, BSONObjBuilder&); // #ifdef ROBOMONGO /* The following functions only parse the body of the ISODate function * between the parentheses, not including the function name */ /* * ISODATE : * ISODate( ) */ Status isodate(const StringData fieldName, BSONObjBuilder&); /* UUIDs in different formats: * UUID(), LUUID(), JUUID(), NUUID(), PYUUID() */ Status uuid(const StringData fieldName, BSONObjBuilder&); Status luuid(const StringData fieldName, BSONObjBuilder&); Status juuid(const StringData fieldName, BSONObjBuilder&); Status nuuid(const StringData fieldName, BSONObjBuilder&); Status pyuuid(const StringData fieldName, BSONObjBuilder&); Status parseUuid(const StringData fieldName, BSONObjBuilder&, BinDataType binType, ::Robomongo::UUIDEncoding uuidEncoding); // #endif /* * TIMESTAMP : * Timestamp( <32 bit unsigned integer for seconds since epoch>, * <32 bit unsigned integer for the increment> ) */ Status timestamp(StringData fieldName, BSONObjBuilder&); /* * OBJECTID : * ObjectId( <24 character hex std::string> ) */ Status objectId(StringData fieldName, BSONObjBuilder&); /* * NUMBERLONG : * NumberLong( ) */ Status numberLong(StringData fieldName, BSONObjBuilder&); /* * NUMBERDECIMAL : * NumberDecimal( ) */ Status numberDecimal(StringData fieldName, BSONObjBuilder&); /* * NUMBERINT : * NumberInt( ) */ Status numberInt(StringData fieldName, BSONObjBuilder&); /* * DBREF : * Dbref( , <24 character hex std::string> ) */ Status dbRef(StringData fieldName, BSONObjBuilder&); /* * REGEX : * / REGEXCHARS / REGEXOPTIONS * * REGEXCHARS : * REGEXCHAR * | REGEXCHAR REGEXCHARS * * REGEXCHAR : * any-Unicode-character-except-/-or-\-or-CONTROLCHAR * | \" * | \' * | \\ * | \/ * | \b * | \f * | \n * | \r * | \t * | \v * | \u HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT * | \any-Unicode-character-except-x-or-[0-7] * * REGEXOPTIONS : * REGEXOPTION * | REGEXOPTION REGEXOPTIONS * * REGEXOPTION : * g | i | m | s */ Status regex(StringData fieldName, BSONObjBuilder&); Status regexPat(std::string* result); Status regexOpt(std::string* result); Status regexOptCheck(StringData opt); /* * NUMBER : * * NOTE: Number parsing is based on standard library functions, not * necessarily on the JSON numeric grammar. * * Number as value - strtoll and strtod * Date - strtoll * Timestamp - strtoul for both timestamp and increment and '-' * before a number explicity disallowed */ Status number(StringData fieldName, BSONObjBuilder&); /* * FIELD : * STRING * | [a-zA-Z$_] FIELDCHARS * * FIELDCHARS : * [a-zA-Z0-9$_] * | [a-zA-Z0-9$_] FIELDCHARS */ Status field(std::string* result); /* * std::string : * " " * | ' ' * | " CHARS " * | ' CHARS ' */ Status quotedString(std::string* result); /* * CHARS : * CHAR * | CHAR CHARS * * Note: " or ' may be allowed depending on whether the std::string is * double or single quoted * * CHAR : * any-Unicode-character-except-"-or-'-or-\-or-CONTROLCHAR * | \" * | \' * | \\ * | \/ * | \b * | \f * | \n * | \r * | \t * | \v * | \u HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT * | \any-Unicode-character-except-x-or-[0-9] * * HEXDIGIT : [0..9a..fA..F] * * per http://www.ietf.org/rfc/rfc4627.txt, control characters are * (U+0000 through U+001F). U+007F is not mentioned as a control * character. * CONTROLCHAR : [0x00..0x1F] * * If there is not an error, result will contain a null terminated * string, but there is no guarantee that it will not contain other * null characters. */ Status chars(std::string* result, const char* terminalSet, const char* allowedSet = NULL); /** * Converts the two byte Unicode code point to its UTF8 character * encoding representation. This function returns a std::string because * UTF8 encodings for code points from 0x0000 to 0xFFFF can range * from one to three characters. */ std::string encodeUTF8(unsigned char first, unsigned char second) const; /** * @return true if the given token matches the next non whitespace * sequence in our buffer, and false if the token doesn't match or * we reach the end of our buffer. Do not update the pointer to our * buffer (same as calling readTokenImpl with advance=false). */ inline bool peekToken(const char* token); /** * @return true if the given token matches the next non whitespace * sequence in our buffer, and false if the token doesn't match or * we reach the end of our buffer. Updates the pointer to our * buffer (same as calling readTokenImpl with advance=true). */ inline bool readToken(const char* token); /** * @return true if the given token matches the next non whitespace * sequence in our buffer, and false if the token doesn't match or * we reach the end of our buffer. Do not update the pointer to our * buffer if advance is false. */ bool readTokenImpl(const char* token, bool advance = true); /** * @return true if the next field in our stream matches field. * Handles single quoted, double quoted, and unquoted field names */ bool readField(StringData field); /** * @return true if matchChar is in matchSet * @return true if matchSet is NULL and false if it is an empty string */ bool match(char matchChar, const char* matchSet) const; /** * @return true if every character in the std::string is a hex digit */ bool isHexString(StringData) const; /** * @return true if every character in the std::string is a valid base64 * character */ bool isBase64String(StringData) const; /** * @return FailedToParse status with the given message and some * additional context information */ Status parseError(StringData msg); public: inline int offset() { return (_input - _buf); } private: /* * _buf - start of our input buffer * _input - cursor we advance in our input buffer * _input_end - sentinel for the end of our input buffer * * _buf is the null terminated buffer containing the JSON std::string we * are parsing. _input_end points to the null byte at the end of * the buffer. strtoll, strtol, and strtod will access the null * byte at the end of the buffer because they are assuming a c-style * string. */ const char* const _buf; const char* _input; const char* const _input_end; }; //#ifdef ROBOMONGO class ParseMsgAssertionException : public std::exception { public: ParseMsgAssertionException(int c, const std::string& m, int offset, const std::string reason) : _reason(reason), _offset(offset) {} virtual ~ParseMsgAssertionException() throw() { } virtual bool severe() { return false; } std::string reason() const { return _reason; } int offset() const { return _offset; } private: std::string _reason; int _offset; }; //#endif } // namespace Robomongo } // namespace mongo ================================================ FILE: src/robomongo/shell/db/ptimeutil.cpp ================================================ /* wdb - weather and water data storage Copyright (C) 2007 met.no Contact information: Norwegian Meteorological Institute Box 43 Blindern 0313 OSLO NORWAY E-mail: wdb@met.no This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "ptimeutil.h" #include #include #include #include //#include #include namespace { int getInt( const std::string &timebuf, std::string::size_type &index, int numberOfChar, bool &isSuccessfull ) { int nextN=0; char buf[10]={0}; isSuccessfull = numberOfChar < 9; assert(isSuccessfull); while ( nextN < numberOfChar && isSuccessfull) { isSuccessfull = index < timebuf.length() && isdigit( timebuf[index] ); assert(isSuccessfull); buf[nextN]=timebuf[index]; nextN++; index++; } return atoi( buf ); } } namespace miutil { const long long minDate = -9218988800000; // "1677-11-10T17:46:40.001Z" const long long maxDate = 9218988800000; // "2262-02-20T06:13:19.999Z" std::string rfc1123date( const boost::posix_time::ptime &pt ) { const char *day = NULL; const char *mon = NULL; if( pt.is_special() ) return std::string(); boost::gregorian::date d( pt.date() ); boost::posix_time::time_duration t( pt.time_of_day() ); switch( d.day_of_week() ) { using namespace boost::date_time; case Sunday: day="Sun"; break; case Monday: day="Mon"; break; case Tuesday: day="Tue"; break; case Wednesday: day="Wed"; break; case Thursday: day="Thu"; break; case Friday: day="Fri"; break; case Saturday: day="Sat"; break; } switch( d.month() ){ using namespace boost::date_time; case Jan: mon="Jan"; break; case Feb: mon="Feb"; break; case Mar: mon="Mar"; break; case Apr: mon="Apr"; break; case May: mon="May"; break; case Jun: mon="Jun"; break; case Jul: mon="Jul"; break; case Aug: mon="Aug"; break; case Sep: mon="Sep"; break; case Oct: mon="Oct"; break; case Nov: mon="Nov"; break; case Dec: mon="Dec"; break; } if( !day || !mon ) return std::string(); unsigned short year = d.year(); char buf[64] = {0}; #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat" #elif defined(__GNUG__) // gcc #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat" #endif sprintf( buf, "%s, %02d %s %04d %02d:%02d:%02d GMT", day, d.day().as_number(), mon, year, t.hours(), t.minutes(), t.seconds() ); #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUG__) // gcc #pragma GCC diagnostic pop #endif return buf; } boost::posix_time::ptime rfc1123date( const std::string &rfc1123 ) { return rfc1123date( rfc1123.c_str() ); } boost::posix_time::ptime rfc1123date( const char *rfc1123 ) { char gmt[4]; char sday[4]; char smon[4]; int day, mon, year, h, m, s; int weekDay; if( !rfc1123 ) return boost::posix_time::ptime( boost::date_time::not_a_date_time ); if ( sscanf( rfc1123, "%3s, %02d %3s %04d %02d:%02d:%02d %3s", sday, &day, smon, &year, &h, &m, &s, gmt) !=8 ) return boost::posix_time::ptime( boost::date_time::not_a_date_time ); if( strcmp(gmt, "GMT")!=0 ) return boost::posix_time::ptime( boost::date_time::not_a_date_time ); if ( strcmp(smon, "Jan")==0 ) mon=1; else if ( strcmp(smon, "Feb")==0 ) mon=2; else if ( strcmp(smon, "Mar")==0 ) mon=3; else if ( strcmp(smon, "Apr")==0 ) mon=4; else if ( strcmp(smon, "May")==0 ) mon=5; else if ( strcmp(smon, "Jun")==0 ) mon=6; else if ( strcmp(smon, "Jul")==0 ) mon=7; else if ( strcmp(smon, "Aug")==0 ) mon=8; else if ( strcmp(smon, "Sep")==0 ) mon=9; else if ( strcmp(smon, "Oct")==0 ) mon=10; else if ( strcmp(smon, "Nov")==0 ) mon=11; else if ( strcmp(smon, "Dec")==0 ) mon=12; else return boost::posix_time::ptime( boost::date_time::not_a_date_time ); //The day chack is a consistent check that could be dropped. if ( strcmp(sday, "Sun")==0 ) weekDay=0; else if ( strcmp(sday, "Mon")==0 ) weekDay=1; else if ( strcmp(sday, "Tue")==0 ) weekDay=2; else if ( strcmp(sday, "Wed")==0 ) weekDay=3; else if ( strcmp(sday, "Thu")==0 ) weekDay=4; else if ( strcmp(sday, "Fri")==0 ) weekDay=5; else if ( strcmp(sday, "Sat")==0 ) weekDay=6; else return boost::posix_time::ptime( boost::date_time::not_a_date_time ); if( day<0 || day>31 || h<0 || h>23 || m<0 || m>59 || s<0 || s>60 ) return boost::posix_time::ptime( boost::date_time::not_a_date_time ); boost::posix_time::ptime date( boost::gregorian::date(year, mon, day) , boost::posix_time::time_duration( h, m, s ) ); //This is a consistent check. We could skip this test. if( weekDay != date.date().day_of_week() ) return boost::posix_time::ptime( boost::date_time::not_a_date_time ); return date; } std::string isotimeString(const boost::posix_time::ptime &pt, bool useTseparator, bool isLocalFormat) { if( pt.is_special() ) return ""; char buf[64]={0}; char sep=' '; if( useTseparator ) sep = 'T'; boost::gregorian::date d( pt.date() ); boost::posix_time::time_duration t( pt.time_of_day() ); if( !isLocalFormat ){ #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat" #elif defined(__GNUG__) // gcc #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat" #endif sprintf( buf, "%04d-%02d-%02d%c%02d:%02d:%02d.%03dZ", static_cast(d.year()), d.month().as_number(), d.day().as_number(), sep, t.hours(), t.minutes(), t.seconds(), (static_cast(t.total_milliseconds()))%1000 ); #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUG__) // gcc #pragma GCC diagnostic pop #endif } else{ boost::posix_time::ptime timeP(d, t); time_t rawtime; time ( &rawtime ); struct tm *ptm = std::gmtime ( &rawtime ); int utcD = ptm->tm_mday; int utcH = ptm->tm_hour; int utcM = ptm->tm_min; struct tm *timeinfo = std::localtime (&rawtime); int diffH = timeinfo->tm_hour - utcH; // Time zone calculation if (timeinfo->tm_mday < utcD && diffH > 0) diffH -= 24; else if (timeinfo->tm_mday > utcD && diffH < 0) diffH += 24; int diffM = timeinfo->tm_min - utcM; boost::posix_time::time_duration diffT = boost::posix_time::time_duration(diffH, diffM, 0); timeP += diffT; d = timeP.date(); t = timeP.time_of_day(); char utc_buff[8]={0}; #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat" #elif defined(__GNUG__) // gcc #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat" #endif sprintf(utc_buff, (diffT.hours() >= 0) ? "+%02d:%02d" : "%03d:%02d", diffT.hours(), abs(diffM)); sprintf(buf, "%04d-%02d-%02d%c%02d:%02d:%02d.%03d", static_cast(d.year()), d.month().as_number(), d.day().as_number(), sep, t.hours(), t.minutes(), t.seconds(), (static_cast(t.total_milliseconds()))%1000); #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUG__) // gcc #pragma GCC diagnostic pop #endif strcat(buf, utc_buff); } return buf; } boost::posix_time::ptime ptimeFromIsoString( const std::string &isoTime) { bool isSuccessfull=false; return ptimeFromIsoString(isoTime, isSuccessfull); } boost::posix_time::ptime ptimeFromIsoString( const std::string &isoTime, bool &isSuccessfull) { struct DEF { int number; unsigned char numberOfChars; const char *nextSep; }; DEF def[] = { {0, 4, "-"}, //year {0, 2, "-"}, //Month {0, 2, "T "},//day {0, 2, ":"}, //hour {0, 2, ":"}, //minute {0, 2, ".Z"}, //second {0, 3, "+-Z"}, //msecond {0, 0, 0} //terminator }; isSuccessfull = true; if( isoTime == "infinity" ) return boost::posix_time::ptime( boost::posix_time::pos_infin ); else if( isoTime == "-infinity" ) return boost::posix_time::ptime( boost::posix_time::neg_infin ); else if( isoTime == "epoch" ) return boost::posix_time::ptime( boost::gregorian::date( 1970, 1, 1 ), boost::posix_time::time_duration( 0, 0, 0) ); else if( isoTime == "now" ) return boost::posix_time::second_clock::universal_time(); else if( isoTime == "today" ) { boost::posix_time::ptime now( boost::posix_time::second_clock::universal_time() ); return boost::posix_time::ptime( now.date(), boost::posix_time::time_duration( 0, 0, 0) ); } else if( isoTime == "tomorrow" ) { boost::posix_time::ptime now( boost::posix_time::second_clock::universal_time() ); now += boost::posix_time::hours(24); return boost::posix_time::ptime( now.date(), boost::posix_time::time_duration( 0, 0, 0) ); } else if( isoTime == "yesterday" ) { boost::posix_time::ptime now( boost::posix_time::second_clock::universal_time() ); now -= boost::posix_time::hours(24); return boost::posix_time::ptime( now.date(), boost::posix_time::time_duration( 0, 0, 0) ); } std::string::size_type iIsoTime = isoTime.find_first_not_of( " ", 0 ); //Skip whitespace in front isSuccessfull = iIsoTime != std::string::npos; assert( isSuccessfull ); if( ! isdigit( (int)isoTime[iIsoTime] ) ) { //We try to decode it as an rfc1123date. boost::posix_time::ptime pt = rfc1123date( isoTime.c_str() ); isSuccessfull = !pt.is_special(); assert( isSuccessfull ); return pt; } //Decode the YYYY-MM-DDThh:mm:ss part //Remeber -, T and : is optional. for( unsigned short defIndex=0; def[defIndex].nextSep && iIsoTime < isoTime.length() && isSuccessfull; ++defIndex ) { def[defIndex].number = getInt( isoTime, iIsoTime, def[defIndex].numberOfChars, isSuccessfull); iIsoTime = isoTime.find_first_of( def[defIndex].nextSep, iIsoTime ); if(defIndex!=6) ++iIsoTime; } if(!isSuccessfull){ return boost::posix_time::ptime(); } int hourOffset=0; int minuteOffset=0; //Decode the UTC offset part of //YYYY-MM-DDThh:mm:ssSHHMM part //The UTC offset part is SHHMM //Where S is the sign. + or -. // HH the hour offset, mandatory if we have an UTC offset part. // MM the minute offset, otional. if( iIsoTime < isoTime.length() ) { //We have a possible UTC offset. //The format is SHHMM, where S is the sign. char ch = isoTime[iIsoTime]; if( ch == '+' || ch == '-' || isdigit( ch ) ) { int sign=1; if( ! isdigit( ch ) ) { if( ch=='-' ) sign=-1; isSuccessfull = isdigit( isoTime[++iIsoTime] )!=0; assert( isSuccessfull ); } hourOffset = getInt( isoTime, iIsoTime, 2, isSuccessfull); hourOffset*=sign; if( iIsoTime < isoTime.length() && isdigit( isoTime[++iIsoTime] ) ) { minuteOffset = getInt( isoTime, iIsoTime, 2, isSuccessfull); } } } try{ boost::gregorian::date date(def[0].number, def[1].number, def[2].number); boost::posix_time::time_duration td(def[3].number, def[4].number, def[5].number, def[6].number*1000); boost::posix_time::ptime pt(date, td); td = boost::posix_time::time_duration(hourOffset, minuteOffset, 0); return pt - td; } catch(const std::out_of_range &) { isSuccessfull = false; return boost::posix_time::ptime(); } } } ================================================ FILE: src/robomongo/shell/db/ptimeutil.h ================================================ /* wdb - weather and water data storage Copyright (C) 2007 met.no Contact information: Norwegian Meteorological Institute Box 43 Blindern 0313 OSLO NORWAY E-mail: wdb@met.no This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef __RFC1123DATE_H__ #define __RFC1123DATE_H__ #include #include namespace miutil { extern const long long minDate; extern const long long maxDate; /** * Takes an ptime and convert it to a time string coded in * the RFC 1123 format. This is the date format used by * the HTTP protocol. * * \b Ex. on RFC 1123 coded string * * Fri, 16 Mar 2007 08:13:37 GMT * * @param t the time to convert to a RFC 1123 coded string representation. * @return On success a string coded in the RFC 1123 format. On error an * empty string is returned. */ std::string rfc1123date(const boost::posix_time::ptime &t); /** * Takes an RFC 1123 coded date string and return an boost::posix_time. * * \b Ex. on RFC 1123 coded string * * Fri, 16 Mar 2007 08:13:37 GMT * * @param rfc1123 coded data string. * @return An valid ptime on success and not_a_date_time on failure. */ boost::posix_time::ptime rfc1123date(const std::string &rfc1123); /** * Takes an RFC 1123 coded date string and return an boost::posix_time. * * \b Ex. on RFC 1123 coded string * * Fri, 16 Mar 2007 08:13:37 GMT * * @param rfc1123 coded data string. * @return An valid ptime on success and not_a_date_time on failure. */ boost::posix_time::ptime rfc1123date(const char *rfc1123); /** * isotimeString generates a timeformatted string in one of two * iso compatible formats. * * - YYYY-MM-DD hh:mm:ss * - YYYY-MM-DDThh:mm:ss * * The second form separates the date part from the timepart with a T. * * If markAsZulu is true an Z is added to the end of the timestring. * * ex. 2008-02-15T18:00:00Z * * An empty string is returned if the \em time is not a valid * ptime, ie. the call to is_special return true. * * @param t the time to return as a string. * @param useTseparator is true if we want the datepart and timepart separated with a T. * @param markAsZulu Add an Z to the end of the timestring * @return An iso compatible string on success or an empty string if \em t is invalid. */ std::string isotimeString(const boost::posix_time::ptime &t, bool useTseparator=false, bool isLocalFormat=false); /** * ptimeFromIsoString decodes a time string on the followwing formates to a ptime. * * - YYYY-MM-DDThh:mm:ssZ * - YYYY-MM-DDThh:mm:ssSHHMM * - YYYYMMDDThhmmssSHHMM * - Sun, 01 Apr 2007 09:51:04 GMT * * Z is optional and means ZULU time (GMT). * S in front of HHMM stands for - or +. HHMM is an offset to UTC. * T is an optional separator it my bee an space. * * If the timestring is invallid an logic_error exception is thrown. * * @param isoTime A timestring. * @return An ptime. */ boost::posix_time::ptime ptimeFromIsoString( const std::string &isoTime); boost::posix_time::ptime ptimeFromIsoString( const std::string &isoTime, bool &isSuccessfull); } #endif ================================================ FILE: src/robomongo/shell/shell/dbshell.cpp ================================================ // dbshell.cpp /* * Copyright 2010 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault #include "mongo/platform/basic.h" #include #include #include #include #include #include #include #include "mongo/base/initializer.h" #include "mongo/base/status.h" #include "mongo/client/dbclient_base.h" #include "mongo/client/sasl_client_authenticate.h" #include "mongo/db/client.h" #include "mongo/db/log_process_details.h" #include "mongo/db/server_options.h" #include "mongo/logger/console_appender.h" #include "mongo/logger/logger.h" #include "mongo/logger/message_event_utf8_encoder.h" #include "mongo/scripting/engine.h" #include "mongo/shell/linenoise.h" #include "mongo/shell/shell_options.h" #include "mongo/shell/shell_utils.h" #include "mongo/shell/shell_utils_launcher.h" #include "mongo/util/exit_code.h" #include "mongo/util/file.h" #include "mongo/util/log.h" #include "mongo/util/net/ssl_options.h" #include "mongo/util/password.h" #include "mongo/util/quick_exit.h" #include "mongo/util/scopeguard.h" #include "mongo/util/signal_handlers.h" #include "mongo/util/stacktrace.h" #include "mongo/util/startup_test.h" #include "mongo/util/static_observer.h" #include "mongo/util/text.h" #include "mongo/util/version.h" #ifdef _WIN32 #include #include #define isatty _isatty #define fileno _fileno #else #include #endif using namespace std; using namespace mongo; string historyFile; bool gotInterrupted = false; bool inMultiLine = false; static volatile bool atPrompt = false; // can eval before getting to prompt namespace mongo { Scope* shellMainScope; extern bool dbexitCalled; } void generateCompletions(const string& prefix, vector& all) { if (prefix.find('"') != string::npos) return; try { BSONObj args = BSON("0" << prefix); shellMainScope->invokeSafe( "function callShellAutocomplete(x) {shellAutocomplete(x)}", &args, NULL); BSONObjBuilder b; shellMainScope->append(b, "", "__autocomplete__"); BSONObj res = b.obj(); BSONObj arr = res.firstElement().Obj(); BSONObjIterator i(arr); while (i.more()) { BSONElement e = i.next(); all.push_back(e.String()); } } catch (...) { } } void completionHook(const char* text, linenoiseCompletions* lc) { vector all; generateCompletions(text, all); for (unsigned i = 0; i < all.size(); ++i) linenoiseAddCompletion(lc, (char*)all[i].c_str()); } void shellHistoryInit() { stringstream ss; const char* h = shell_utils::getUserDir(); if (h) ss << h << "/"; ss << ".dbshell"; historyFile = ss.str(); linenoiseHistoryLoad(historyFile.c_str()); linenoiseSetCompletionCallback(completionHook); } void shellHistoryDone() { linenoiseHistorySave(historyFile.c_str()); linenoiseHistoryFree(); } void shellHistoryAdd(const char* line) { if (line[0] == '\0') return; // dont record duplicate lines static string lastLine; if (lastLine == line) return; lastLine = line; // We don't want any .auth() or .createUser() shell helpers added, but we want to // be able to add things like `.author`, so be smart about how this is // detected by using regular expresions. This is so we can avoid storing passwords // in the history file in plaintext. static pcrecpp::RE hiddenHelpers( "\\.\\s*(auth|createUser|updateUser|changeUserPassword)\\s*\\("); // Also don't want the raw user management commands to show in the shell when run directly // via runCommand. static pcrecpp::RE hiddenCommands( "(run|admin)Command\\s*\\(\\s*{\\s*(createUser|updateUser)\\s*:"); if (!hiddenHelpers.PartialMatch(line) && !hiddenCommands.PartialMatch(line)) { linenoiseHistoryAdd(line); } } void killOps() { if (mongo::shell_utils::_nokillop) return; if (atPrompt) return; sleepmillis(10); // give current op a chance to finish mongo::shell_utils::connectionRegistry.killOperationsOnAllConnections( !shellGlobalParams.autoKillOp); } // Stubs for signal_handlers.cpp namespace mongo { void logProcessDetailsForLogRotate() {} void exitCleanly(ExitCode code) { { stdx::lock_guard lk(mongo::shell_utils::mongoProgramOutputMutex); mongo::dbexitCalled = true; } ::killOps(); ::shellHistoryDone(); quickExit(0); } } void quitNicely(int sig) { exitCleanly(EXIT_CLEAN); } // the returned string is allocated with strdup() or malloc() and must be freed by calling free() char* shellReadline(const char* prompt, int handlesigint = 0) { atPrompt = true; char* ret = linenoise(prompt); if (!ret) { gotInterrupted = true; // got ^C, break out of multiline } atPrompt = false; return ret; } void setupSignals() { signal(SIGINT, quitNicely); } string fixHost(const std::string& url, const std::string& host, const std::string& port) { if (host.size() == 0 && port.size() == 0) { if (url.find("/") == string::npos) { // check for ips if (url.find(".") != string::npos) return url + "/test"; if (url.rfind(":") != string::npos && isdigit(url[url.rfind(":") + 1])) return url + "/test"; } return url; } if (url.find("/") != string::npos) { cerr << "url can't have host or port if you specify them individually" << endl; quickExit(-1); } string newurl((host.size() == 0) ? "127.0.0.1" : host); if (port.size() > 0) newurl += ":" + port; else if (host.find(':') == string::npos) { // need to add port with IPv6 addresses newurl += ":27017"; } newurl += "/" + url; return newurl; } static string OpSymbols = "~!%^&*-+=|:,<>/?."; bool isOpSymbol(char c) { for (size_t i = 0; i < OpSymbols.size(); i++) if (OpSymbols[i] == c) return true; return false; } bool isUseCmd(const std::string& code) { string cmd = code; if (cmd.find(" ") > 0) cmd = cmd.substr(0, cmd.find(" ")); return cmd == "use"; } /** * Skip over a quoted string, including quotes escaped with backslash * * @param code String * @param start Starting position within string, always > 0 * @param quote Quote character (single or double quote) * @return Position of ending quote, or code.size() if no quote found */ size_t skipOverString(const std::string& code, size_t start, char quote) { size_t pos = start; while (pos < code.size()) { pos = code.find(quote, pos); if (pos == std::string::npos) { return code.size(); } // We want to break if the quote we found is not escaped, but we need to make sure // that the escaping backslash is not itself escaped. Comparisons of start and pos // are to keep us from reading beyond the beginning of the quoted string. // if (start == pos || code[pos - 1] != '\\' || // previous char was backslash start == pos - 1 || code[pos - 2] == '\\' // char before backslash was not another ) { break; // The quote we found was not preceded by an unescaped backslash; it is real } ++pos; // The quote we found was escaped with backslash, so it doesn't count } return pos; } bool isBalanced(const std::string& code) { if (isUseCmd(code)) return true; // don't balance "use " in case dbname contains special chars int curlyBrackets = 0; int squareBrackets = 0; int parens = 0; bool danglingOp = false; for (size_t i = 0; i < code.size(); i++) { switch (code[i]) { case '/': if (i + 1 < code.size() && code[i + 1] == '/') { while (i < code.size() && code[i] != '\n') i++; } continue; case '{': curlyBrackets++; break; case '}': if (curlyBrackets <= 0) return true; curlyBrackets--; break; case '[': squareBrackets++; break; case ']': if (squareBrackets <= 0) return true; squareBrackets--; break; case '(': parens++; break; case ')': if (parens <= 0) return true; parens--; break; case '"': case '\'': i = skipOverString(code, i + 1, code[i]); if (i >= code.size()) { return true; // Do not let unterminated strings enter multi-line mode } break; case '\\': if (i + 1 < code.size() && code[i + 1] == '/') i++; break; case '+': case '-': if (i + 1 < code.size() && code[i + 1] == code[i]) { i++; continue; // postfix op (++/--) can't be a dangling op } break; } if (i >= code.size()) { danglingOp = false; break; } if (isOpSymbol(code[i])) danglingOp = true; else if (!std::isspace(static_cast(code[i]))) danglingOp = false; } return curlyBrackets == 0 && squareBrackets == 0 && parens == 0 && !danglingOp; } struct BalancedTest : public mongo::StartupTest { public: void run() { verify(isBalanced("x = 5")); verify(isBalanced("function(){}")); verify(isBalanced("function(){\n}")); verify(!isBalanced("function(){")); verify(isBalanced("x = \"{\";")); verify(isBalanced("// {")); verify(!isBalanced("// \n {")); verify(!isBalanced("\"//\" {")); verify(isBalanced("{x:/x\\//}")); verify(!isBalanced("{ \\/// }")); verify(isBalanced("x = 5 + y ")); verify(!isBalanced("x = ")); verify(!isBalanced("x = // hello")); verify(!isBalanced("x = 5 +")); verify(isBalanced(" x ++")); verify(isBalanced("-- x")); verify(!isBalanced("a.")); verify(!isBalanced("a. ")); verify(isBalanced("a.b")); // SERVER-5809 and related cases -- verify(isBalanced("a = {s:\"\\\"\"}")); // a = {s:"\""} verify(isBalanced("db.test.save({s:\"\\\"\"})")); // db.test.save({s:"\""}) verify(isBalanced("printjson(\" \\\" \")")); // printjson(" \" ") -- SERVER-8554 verify(isBalanced("var a = \"\\\\\";")); // var a = "\\"; verify(isBalanced("var a = (\"\\\\\") //\"")); // var a = ("\\") //" verify(isBalanced("var a = (\"\\\\\") //\\\"")); // var a = ("\\") //\" verify(isBalanced("var a = (\"\\\\\") //")); // var a = ("\\") // verify(isBalanced("var a = (\"\\\\\")")); // var a = ("\\") verify(isBalanced("var a = (\"\\\\\\\"\")")); // var a = ("\\\"") verify(!isBalanced("var a = (\"\\\\\" //\"")); // var a = ("\\" //" verify(!isBalanced("var a = (\"\\\\\" //")); // var a = ("\\" // verify(!isBalanced("var a = (\"\\\\\"")); // var a = ("\\" } } balanced_test; string finishCode(string code) { while (!isBalanced(code)) { inMultiLine = true; code += "\n"; // cancel multiline if two blank lines are entered if (code.find("\n\n\n") != string::npos) return ";"; char* line = shellReadline("... ", 1); if (gotInterrupted) { if (line) free(line); return ""; } if (!line) return ""; char* linePtr = line; while (str::startsWith(linePtr, "... ")) linePtr += 4; code += linePtr; free(line); } return code; } bool execPrompt(mongo::Scope& scope, const char* promptFunction, string& prompt) { string execStatement = string("__prompt__ = ") + promptFunction + "();"; scope.exec("delete __prompt__;", "", false, false, false, 0); scope.exec(execStatement, "", false, false, false, 0); if (scope.type("__prompt__") == String) { prompt = scope.getString("__prompt__"); return true; } return false; } /** * Edit a variable or input buffer text in an external editor -- EDITOR must be defined * * @param whatToEdit Name of JavaScript variable to be edited, or any text string */ static void edit(const string& whatToEdit) { // EDITOR may be defined in the JavaScript scope or in the environment string editor; if (shellMainScope->type("EDITOR") == String) { editor = shellMainScope->getString("EDITOR"); } else { static const char* editorFromEnv = getenv("EDITOR"); if (editorFromEnv) { editor = editorFromEnv; } } if (editor.empty()) { cout << "please define EDITOR as a JavaScript string or as an environment variable" << endl; return; } // "whatToEdit" might look like a variable/property name bool editingVariable = true; for (const char* p = whatToEdit.c_str(); *p; ++p) { if (!(isalnum(*p) || *p == '_' || *p == '.')) { editingVariable = false; break; } } string js; if (editingVariable) { // If "whatToEdit" is undeclared or uninitialized, declare int varType = shellMainScope->type(whatToEdit.c_str()); if (varType == Undefined) { shellMainScope->exec("var " + whatToEdit, "(shell)", false, true, false); } // Convert "whatToEdit" to JavaScript (JSON) text if (!shellMainScope->exec( "__jsout__ = tojson(" + whatToEdit + ")", "tojs", false, false, false)) return; // Error already printed js = shellMainScope->getString("__jsout__"); if (strstr(js.c_str(), "[native code]")) { cout << "can't edit native functions" << endl; return; } } else { js = whatToEdit; } // Pick a name to use for the temp file string filename; const int maxAttempts = 10; int i; for (i = 0; i < maxAttempts; ++i) { StringBuilder sb; #ifdef _WIN32 char tempFolder[MAX_PATH]; GetTempPathA(sizeof tempFolder, tempFolder); sb << tempFolder << "mongo_edit" << time(0) + i << ".js"; #else sb << "/tmp/mongo_edit" << time(0) + i << ".js"; #endif filename = sb.str(); if (!::mongo::shell_utils::fileExists(filename)) break; } if (i == maxAttempts) { cout << "couldn't create unique temp file after " << maxAttempts << " attempts" << endl; return; } // Create the temp file FILE* tempFileStream; tempFileStream = fopen(filename.c_str(), "wt"); if (!tempFileStream) { cout << "couldn't create temp file (" << filename << "): " << errnoWithDescription() << endl; return; } // Write JSON into the temp file size_t fileSize = js.size(); if (fwrite(js.data(), sizeof(char), fileSize, tempFileStream) != fileSize) { int systemErrno = errno; cout << "failed to write to temp file: " << errnoWithDescription(systemErrno) << endl; fclose(tempFileStream); remove(filename.c_str()); return; } fclose(tempFileStream); // Pass file to editor StringBuilder sb; sb << editor << " " << filename; int ret = ::system(sb.str().c_str()); if (ret) { if (ret == -1) { int systemErrno = errno; cout << "failed to launch $EDITOR (" << editor << "): " << errnoWithDescription(systemErrno) << endl; } else cout << "editor exited with error (" << ret << "), not applying changes" << endl; remove(filename.c_str()); return; } // The editor gave return code zero, so read the file back in tempFileStream = fopen(filename.c_str(), "rt"); if (!tempFileStream) { cout << "couldn't open temp file on return from editor: " << errnoWithDescription() << endl; remove(filename.c_str()); return; } sb.reset(); int bytes; do { char buf[1024]; bytes = fread(buf, sizeof(char), sizeof buf, tempFileStream); if (ferror(tempFileStream)) { cout << "failed to read temp file: " << errnoWithDescription() << endl; fclose(tempFileStream); remove(filename.c_str()); return; } sb.append(StringData(buf, bytes)); } while (bytes); // Done with temp file, close and delete it fclose(tempFileStream); remove(filename.c_str()); if (editingVariable) { // Try to execute assignment to copy edited value back into the variable const string code = whatToEdit + string(" = ") + sb.str(); if (!shellMainScope->exec(code, "tojs", false, true, false)) { cout << "error executing assignment: " << code << endl; } } else { linenoisePreloadBuffer(sb.str().c_str()); } } int _main(int argc, char* argv[], char** envp) { setupSignalHandlers(); setupSignals(); mongo::shell_utils::RecordMyLocation(argv[0]); shellGlobalParams.url = "test"; mongo::runGlobalInitializersOrDie(argc, argv, envp); // hide password from ps output for (int i = 0; i < (argc - 1); ++i) { if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--password")) { char* arg = argv[i + 1]; while (*arg) { *arg++ = 'x'; } } } if (!mongo::serverGlobalParams.quiet) cout << "MongoDB shell version: " << mongo::versionString << endl; mongo::StartupTest::runTests(); logger::globalLogManager() ->getNamedDomain("javascriptOutput") ->attachAppender(logger::MessageLogDomain::AppenderAutoPtr( new logger::ConsoleAppender( new logger::MessageEventUnadornedEncoder))); if (!shellGlobalParams.nodb) { // connect to db stringstream ss; if (mongo::serverGlobalParams.quiet) ss << "__quiet = true;"; ss << "db = connect( \"" << fixHost(shellGlobalParams.url, shellGlobalParams.dbhost, shellGlobalParams.port) << "\")"; mongo::shell_utils::_dbConnect = ss.str(); if (shellGlobalParams.usingPassword && shellGlobalParams.password.empty()) { shellGlobalParams.password = mongo::askPassword(); } } // Construct the authentication-related code to execute on shell startup. // // This constructs and immediately executes an anonymous function, to avoid // the shell's default behavior of printing statement results to the console. // // It constructs a statement of the following form: // // (function() { // // Set default authentication mechanism and, maybe, authenticate. // }()) stringstream authStringStream; authStringStream << "(function() { " << endl; if (!shellGlobalParams.authenticationMechanism.empty()) { authStringStream << "DB.prototype._defaultAuthenticationMechanism = \"" << escape(shellGlobalParams.authenticationMechanism) << "\";" << endl; } if (!shellGlobalParams.gssapiServiceName.empty()) { authStringStream << "DB.prototype._defaultGssapiServiceName = \"" << escape(shellGlobalParams.gssapiServiceName) << "\";" << endl; } if (!shellGlobalParams.nodb && shellGlobalParams.username.size()) { authStringStream << "var username = \"" << escape(shellGlobalParams.username) << "\";" << endl; if (shellGlobalParams.usingPassword) { authStringStream << "var password = \"" << escape(shellGlobalParams.password) << "\";" << endl; } if (shellGlobalParams.authenticationDatabase.empty()) { authStringStream << "var authDb = db;" << endl; } else { authStringStream << "var authDb = db.getSiblingDB(\"" << escape(shellGlobalParams.authenticationDatabase) << "\");" << endl; } authStringStream << "authDb._authOrThrow({ " << saslCommandUserFieldName << ": username "; if (shellGlobalParams.usingPassword) { authStringStream << ", " << saslCommandPasswordFieldName << ": password "; } if (!shellGlobalParams.gssapiHostName.empty()) { authStringStream << ", " << saslCommandServiceHostnameFieldName << ": \"" << escape(shellGlobalParams.gssapiHostName) << '"' << endl; } authStringStream << "});" << endl; } authStringStream << "}())"; mongo::shell_utils::_dbAuth = authStringStream.str(); mongo::ScriptEngine::setConnectCallback(mongo::shell_utils::onConnect); mongo::ScriptEngine::setup(); mongo::globalScriptEngine->setScopeInitCallback(mongo::shell_utils::initScope); mongo::globalScriptEngine->enableJIT(!shellGlobalParams.nojit); auto poolGuard = MakeGuard([] { ScriptEngine::dropScopeCache(); }); unique_ptr scope(mongo::globalScriptEngine->newScope()); shellMainScope = scope.get(); if (shellGlobalParams.runShell) cout << "type \"help\" for help" << endl; // Load and execute /etc/mongorc.js before starting shell std::string rcGlobalLocation; #ifndef _WIN32 rcGlobalLocation = "/etc/mongorc.js"; #else wchar_t programDataPath[MAX_PATH]; if (S_OK == SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, programDataPath)) { rcGlobalLocation = str::stream() << toUtf8String(programDataPath) << "\\MongoDB\\mongorc.js"; } #endif if (!rcGlobalLocation.empty() && ::mongo::shell_utils::fileExists(rcGlobalLocation)) { if (!scope->execFile(rcGlobalLocation, false, true)) { cout << "The \"" << rcGlobalLocation << "\" file could not be executed" << endl; } } if (!shellGlobalParams.script.empty()) { mongo::shell_utils::MongoProgramScope s; if (!scope->exec(shellGlobalParams.script, "(shell eval)", false, true, false)) return -4; scope->exec("shellPrintHelper( __lastres__ );", "(shell2 eval)", true, true, false); } for (size_t i = 0; i < shellGlobalParams.files.size(); ++i) { mongo::shell_utils::MongoProgramScope s; if (shellGlobalParams.files.size() > 1) cout << "loading file: " << shellGlobalParams.files[i] << endl; if (!scope->execFile(shellGlobalParams.files[i], false, true)) { cout << "failed to load: " << shellGlobalParams.files[i] << endl; return -3; } } if (shellGlobalParams.files.size() == 0 && shellGlobalParams.script.empty()) shellGlobalParams.runShell = true; bool lastLineSuccessful = true; if (shellGlobalParams.runShell) { mongo::shell_utils::MongoProgramScope s; // If they specify norc, assume it's not their first time bool hasMongoRC = shellGlobalParams.norc; string rcLocation; if (!shellGlobalParams.norc) { #ifndef _WIN32 if (getenv("HOME") != NULL) rcLocation = str::stream() << getenv("HOME") << "/.mongorc.js"; #else if (getenv("HOMEDRIVE") != NULL && getenv("HOMEPATH") != NULL) rcLocation = str::stream() << toUtf8String(_wgetenv(L"HOMEDRIVE")) << toUtf8String(_wgetenv(L"HOMEPATH")) << "\\.mongorc.js"; #endif if (!rcLocation.empty() && ::mongo::shell_utils::fileExists(rcLocation)) { hasMongoRC = true; if (!scope->execFile(rcLocation, false, true)) { cout << "The \".mongorc.js\" file located in your home folder could not be " "executed" << endl; return -5; } } } if (!hasMongoRC && isatty(fileno(stdin))) { cout << "Welcome to the MongoDB shell.\n" "For interactive help, type \"help\".\n" "For more comprehensive documentation, see\n\thttp://docs.mongodb.org/\n" "Questions? Try the support group\n\thttp://groups.google.com/group/mongodb-user" << endl; File f; f.open(rcLocation.c_str(), false); // Create empty .mongorc.js file } if (!shellGlobalParams.nodb && !mongo::serverGlobalParams.quiet && isatty(fileno(stdin))) { scope->exec( "shellHelper( 'show', 'startupWarnings' )", "(shellwarnings", false, true, false); } shellHistoryInit(); string prompt; int promptType; while (1) { inMultiLine = false; gotInterrupted = false; promptType = scope->type("prompt"); if (promptType == String) { prompt = scope->getString("prompt"); } else if ((promptType == Code) && execPrompt(*scope, "prompt", prompt)) { } else if (execPrompt(*scope, "defaultPrompt", prompt)) { } else { prompt = "> "; } char* line = shellReadline(prompt.c_str()); char* linePtr = line; // can't clobber 'line', we need to free() it later if (linePtr) { while (linePtr[0] == ' ') ++linePtr; int lineLen = strlen(linePtr); while (lineLen > 0 && linePtr[lineLen - 1] == ' ') linePtr[--lineLen] = 0; } if (!linePtr || (strlen(linePtr) == 4 && strstr(linePtr, "exit"))) { if (!mongo::serverGlobalParams.quiet) cout << "bye" << endl; if (line) free(line); break; } string code = linePtr; if (code == "exit" || code == "exit;") { free(line); break; } if (code == "cls") { free(line); linenoiseClearScreen(); continue; } if (code.size() == 0) { free(line); continue; } if (str::startsWith(linePtr, "edit ")) { shellHistoryAdd(linePtr); const char* s = linePtr + 5; // skip "edit " while (*s && isspace(*s)) s++; edit(s); free(line); continue; } gotInterrupted = false; code = finishCode(code); if (gotInterrupted) { cout << endl; free(line); continue; } if (code.size() == 0) { free(line); break; } bool wascmd = false; { string cmd = linePtr; string::size_type firstSpace; if ((firstSpace = cmd.find(" ")) != string::npos) cmd = cmd.substr(0, firstSpace); if (cmd.find("\"") == string::npos) { try { lastLineSuccessful = scope->exec((string) "__iscmd__ = shellHelper[\"" + cmd + "\"];", "(shellhelp1)", false, true, true); if (scope->getBoolean("__iscmd__")) { lastLineSuccessful = scope->exec((string) "shellHelper( \"" + cmd + "\" , \"" + code.substr(cmd.size()) + "\");", "(shellhelp2)", false, true, false); wascmd = true; } } catch (std::exception& e) { cout << "error2:" << e.what() << endl; wascmd = true; lastLineSuccessful = false; } } } if (!wascmd) { try { lastLineSuccessful = scope->exec(code.c_str(), "(shell)", false, true, false); if (lastLineSuccessful) { scope->exec( "shellPrintHelper( __lastres__ );", "(shell2)", true, true, false); } } catch (std::exception& e) { cout << "error:" << e.what() << endl; lastLineSuccessful = false; } } shellHistoryAdd(code.c_str()); free(line); } shellHistoryDone(); } { stdx::lock_guard lk(mongo::shell_utils::mongoProgramOutputMutex); mongo::dbexitCalled = true; } return (lastLineSuccessful ? 0 : 1); } #ifdef _WIN32 int wmain(int argc, wchar_t* argvW[], wchar_t* envpW[]) { static mongo::StaticObserver staticObserver; int returnCode; try { WindowsCommandLine wcl(argc, argvW, envpW); returnCode = _main(argc, wcl.argv(), wcl.envp()); } catch (mongo::DBException& e) { cerr << "exception: " << e.what() << endl; returnCode = 1; } quickExit(returnCode); } #else // #ifdef _WIN32 int main(int argc, char* argv[], char** envp) { static mongo::StaticObserver staticObserver; int returnCode; try { returnCode = _main(argc, argv, envp); } catch (mongo::DBException& e) { cerr << "exception: " << e.what() << endl; returnCode = 1; } quickExit(returnCode); } #endif // #ifdef _WIN32 ================================================ FILE: src/robomongo/ssh/CMakeLists.txt ================================================ include(CheckIncludeFiles) include(CheckSymbolExists) ## Platform checks check_include_files(inttypes.h HAVE_INTTYPES_H) check_include_files(unistd.h HAVE_UNISTD_H) check_include_files(stdlib.h HAVE_STDLIB_H) check_include_files(sys/select.h HAVE_SYS_SELECT_H) check_include_files(sys/socket.h HAVE_SYS_SOCKET_H) check_include_files(sys/time.h HAVE_SYS_TIME_H) check_include_files(arpa/inet.h HAVE_ARPA_INET_H) check_include_files(netinet/in.h HAVE_NETINET_IN_H) check_include_files(winsock2.h HAVE_WINSOCK2_H) check_symbol_exists(strcasecmp strings.h HAVE_STRCASECMP) check_symbol_exists(_stricmp string.h HAVE__STRICMP) check_symbol_exists(snprintf stdio.h HAVE_SNPRINTF) check_symbol_exists(_snprintf stdio.h HAVE__SNPRINTF) check_symbol_exists(__func__ "" HAVE___FUNC__) check_symbol_exists(__FUNCTION__ "" HAVE___FUNCTION__) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/libssh2_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/libssh2_config.h) # Direct-tcpip sample add_library(ssh ssh.c log.c array.c) target_link_libraries(ssh PUBLIC libssh2) target_include_directories(ssh PUBLIC ${CMAKE_BINARY_DIR}/src ${CMAKE_SOURCE_DIR}/src) # Server add_executable(server server.c) target_link_libraries(server PRIVATE ssh) target_include_directories(server PRIVATE ${CMAKE_BINARY_DIR}/src ${CMAKE_SOURCE_DIR}/src) # Tests add_executable(ssh_test test.c) target_link_libraries(ssh_test PRIVATE ssh) target_include_directories(ssh_test PRIVATE ${CMAKE_BINARY_DIR}/src ${CMAKE_SOURCE_DIR}/src) ================================================ FILE: src/robomongo/ssh/README.md ================================================ SSH tunnel submodule ==================== Implemented in vanilla C99 and uses LIBSSH2 library. Notes ----- 1. Users of this module should include *only* `ssh.h`. Do not pollute it with unnecessary includes or implementation specific declarations. 1. Implementation files include `private.h`, which is also includes `ssh.h` automatically. 1. The preferred use of `*` is adjacent to the data name or function name and not adjacent to the type name (i.e. `char *data`, not `char* data`) 1. Public functions and types always should be prefixed with `rbm_ssh_`. It means that all declarations in `ssh.h` should use this prefix. 1. Private functions and types always should be prefixed with just `rbm_`. You may declare file-scoped objects without this prefix, but still prefer to always use `rbm_` prefix. ================================================ FILE: src/robomongo/ssh/array.c ================================================ #include "robomongo/ssh/private.h" #include /* * Adds pointer to array. * You should pass your array (sometype**) and size of it (int) by reference. * Array can be NULL, in this case new array will be allocated * @array Address of (void **) array * @currentsize Address of size * @data Pointer that should be added to array */ int rbm_array_add(void ***array, int *currentsize, void *data) { if (!*array && *currentsize) return RBM_ERROR; int newsize = *currentsize + 1; // Acts like malloc when (*array == NULL) void **newarray = realloc(*array, newsize * sizeof(void*)); if (!newarray) return RBM_ERROR; newarray[*currentsize] = data; *array = newarray; *currentsize = newsize; return RBM_SUCCESS; } int rbm_array_remove(void ***array, int *currentsize, void *data) { int newsize = *currentsize - 1; void **newarray; for (int i = 0; i < *currentsize; i++) { if ((*array)[i] != data) continue; if (*currentsize == 1) { newarray = NULL; } else { newarray = malloc(newsize * sizeof(void*)); if (!newarray) return RBM_ERROR; memcpy(newarray, *array, i * sizeof(void*)); if (i + 1 < *currentsize) memcpy(newarray + i, *array + i + 1, (*currentsize - i - 1) * sizeof(void*)); } free(*array); *currentsize = newsize; *array = newarray; return RBM_SUCCESS; } return RBM_ERROR; } ================================================ FILE: src/robomongo/ssh/libssh2_config.h.in ================================================ /* Copyright (c) 2014 Alexander Lamaison * * 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 copyright holder nor the names * of any other 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. */ /* Headers */ #cmakedefine HAVE_UNISTD_H #cmakedefine HAVE_INTTYPES_H #cmakedefine HAVE_STDLIB_H #cmakedefine HAVE_SYS_SELECT_H #cmakedefine HAVE_SYS_SOCKET_H #cmakedefine HAVE_SYS_TIME_H #cmakedefine HAVE_ARPA_INET_H #cmakedefine HAVE_NETINET_IN_H #cmakedefine HAVE_WINSOCK2_H /* Functions */ #cmakedefine HAVE_STRCASECMP #cmakedefine HAVE__STRICMP #cmakedefine HAVE_SNPRINTF #cmakedefine HAVE__SNPRINTF /* Workaround for platforms without POSIX strcasecmp (e.g. Windows) */ #ifndef HAVE_STRCASECMP # ifdef HAVE__STRICMP # define strcasecmp _stricmp # define HAVE_STRCASECMP # endif #endif /* Symbols */ #cmakedefine HAVE___FUNC__ #cmakedefine HAVE___FUNCTION__ /* Workaround for platforms without C90 __func__ */ #ifndef HAVE___FUNC__ # ifdef HAVE___FUNCTION__ # define __func__ __FUNCTION__ # define HAVE___FUNC__ # endif #endif ================================================ FILE: src/robomongo/ssh/log.c ================================================ #include "robomongo/ssh/private.h" #include #include enum {BUF_SIZE = 1024 }; int log_error(const char *format, ...) { int errsave = errno; char buf[BUF_SIZE]; va_list args; va_start(args, format); vsnprintf(buf, BUF_SIZE, format, args); if (errsave) { fprintf(stderr, "Error (%d): %s. %s\n", errno, strerror(errsave), buf); } else { fprintf(stderr, "Error: %s\n", buf); } va_end(args); return 1; } int log_msg(const char *format, ...) { char buf[BUF_SIZE]; va_list args; va_start(args, format); vsnprintf(buf, BUF_SIZE, format, args); printf("%s\n", buf); va_end(args); return 1; } /* * errsave: use 0, if you are not logging errors (i.e. errno == 0). */ void ssh_log_v(struct rbm_session* session, enum rbm_ssh_log_type type, const char *format, va_list args, int errsave) { char buf[BUF_SIZE]; vsnprintf(buf, BUF_SIZE, format, args); if (type == RBM_SSH_LOG_TYPE_ERROR || type == RBM_SSH_LOG_TYPE_WARN) { if (errsave) { sprintf(session->lasterror, "%s. %s. (Error #%d)", strerror(errsave), buf, errsave); } else { sprintf(session->lasterror, "%s", buf); } fprintf(stderr, "%s\n", session->lasterror); if (session->config->logcontext) session->config->logcallback(session->config->logcontext, session->lasterror, type); return; } if (type != RBM_SSH_LOG_TYPE_INFO && type != RBM_SSH_LOG_TYPE_DEBUG) return; if (type > session->config->loglevel) return; printf("%s\n", buf); if (session->config->logcontext) session->config->logcallback(session->config->logcontext, buf, type); } void ssh_log_msg(struct rbm_session* session, const char *format, ...) { const int type = RBM_SSH_LOG_TYPE_INFO; // For performance reasons, return as quick as possible, // if this level of logging is not enabled if (type > session->config->loglevel) return; va_list args; va_start(args, format); ssh_log_v(session, type, format, args, 0); va_end(args); } // When you faced with an error that you are planning to overcome or handle, // log it as a warning. If you do not have plan how to proceed further, log // as an error. void ssh_log_warn(struct rbm_session* session, const char *format, ...) { int errsave = errno; const int type = RBM_SSH_LOG_TYPE_WARN; // For performance reasons, return as quick as possible, // if this level of logging is not enabled if (type > session->config->loglevel) return; va_list args; va_start(args, format); ssh_log_v(session, type, format, args, errsave); va_end(args); } void ssh_log_debug(struct rbm_session* session, const char *format, ...) { const int type = RBM_SSH_LOG_TYPE_DEBUG; // For performance reasons, return as quick as possible, // if this level of logging is not enabled if (type > session->config->loglevel) return; va_list args; va_start(args, format); ssh_log_v(session, type, format, args, 0); va_end(args); } void ssh_log_error(struct rbm_session* session, const char *format, ...) { int errsave = errno; va_list args; va_start(args, format); ssh_log_v(session, RBM_SSH_LOG_TYPE_ERROR, format, args, errsave); va_end(args); } ================================================ FILE: src/robomongo/ssh/private.h ================================================ #ifndef ROBOMONGO_SSH_PRIVATE_H #define ROBOMONGO_SSH_PRIVATE_H #ifdef __cplusplus extern "C" { #endif // We do not need to use IPv6 for local bind. But we do support IPv6 for remote connection. // It means we can ignore MSVC warning: "Use inet_ntop() or InetNtop() instead" of "inet_ntoa" #define _WINSOCK_DEPRECATED_NO_WARNINGS #include #include // for va_list in log functions #include "robomongo/ssh/ssh.h" #ifdef _WIN32 #include "robomongo/ssh/win.h" #else #include "robomongo/ssh/unix.h" #endif //===----------------------------------------------------------------------===// // Common constants //===----------------------------------------------------------------------===// enum { RBM_SUCCESS = 0, RBM_ERROR = -1, RBM_CHANNEL_CREATION_ERROR = -10, RBM_BUFSIZE = 16384, // Size of the in/out buffers }; //===----------------------------------------------------------------------===// // Data structures //===----------------------------------------------------------------------===// struct rbm_channel { struct rbm_session* session; LIBSSH2_CHANNEL *channel; rbm_socket_t socket; char *inbuf; char *outbuf; int bufmaxsize; }; struct rbm_session { rbm_socket_t localsocket; rbm_socket_t sshsocket; LIBSSH2_SESSION *sshsession; struct rbm_ssh_tunnel_config *config; struct rbm_channel **channels; // array of channels int channelssize; // number of channels struct rbm_ssh_session *publicsession; char lasterror[2048]; }; // Channels struct rbm_channel *rbm_channel_create(struct rbm_session* session, rbm_socket_t socket, LIBSSH2_CHANNEL *lchannel); void rbm_channel_close(struct rbm_channel *channel); struct rbm_channel *rbm_channel_find_by_socket(struct rbm_session *session, rbm_socket_t socket); void rbm_session_cleanup(struct rbm_session *session); int rbm_open_tunnel(struct rbm_session *connection); int rbm_ssh_setup(struct rbm_session *session); static rbm_socket_t socket_connect(struct rbm_session* session, char *ip, int port); LIBSSH2_SESSION *ssh_connect(struct rbm_session *rsession, rbm_socket_t sock, enum rbm_ssh_auth_type type, char *username, char *password, char *publickeypath, char *privatekeypath, char *passphrase); rbm_socket_t socket_listen(struct rbm_session *rsession, char *ip, int *port); //===----------------------------------------------------------------------===// // Logging //===----------------------------------------------------------------------===// int log_error(const char *format, ...); int log_msg(const char *format, ...); void ssh_log_v(struct rbm_session *session, enum rbm_ssh_log_type type, const char *format, va_list args, int errsave); void ssh_log_msg(struct rbm_session *session, const char *format, ...); void ssh_log_warn(struct rbm_session *session, const char *format, ...); void ssh_log_debug(struct rbm_session *session, const char *format, ...); void ssh_log_error(struct rbm_session *session, const char *format, ...); //===----------------------------------------------------------------------===// // Utils //===----------------------------------------------------------------------===// int rbm_array_add(void ***array, int *currentsize, void *data); int rbm_array_remove(void ***array, int *currentsize, void *data); #ifdef __cplusplus } #endif #endif // ROBOMONGO_SSH_PRIVATE_H ================================================ FILE: src/robomongo/ssh/server.c ================================================ #include "ssh.h" #include int main(int argc, char *argv[]) { struct rbm_ssh_tunnel_config config; config.authtype = RBM_SSH_AUTH_TYPE_PUBLICKEY; config.localip = "127.0.0.1"; config.localport = 27040; config.username = "dmitry"; config.password = ""; #ifdef WIN32 config.privatekeyfile = "d:\\ubuntik"; #else config.privatekeyfile = "/Users/dmitry/.ssh/ubuntik"; #endif config.publickeyfile = NULL; //"/Users/dmitry/.ssh/ubuntik.pub"; config.passphrase = ""; config.sshserverhost = "198.61.166.171"; config.sshserverport = 22; config.remotehost = "localhost"; config.remoteport = 27017; config.logcontext = NULL; config.loglevel = RBM_SSH_LOG_TYPE_DEBUG; if (rbm_ssh_init()) return 1; struct rbm_ssh_session *session = NULL; session = rbm_ssh_session_create(&config); if (!session) { printf("Unable to create session. Shutdown\n"); return 1; } // rbm_ssh_session_close(session); printf("II About to open tunnel...\n"); if (rbm_ssh_session_setup(session) == -1) { printf("Setup failed. Shutdowning...\n"); rbm_ssh_session_close(session); return 1; } // rbm_ssh_session_close(session); // printf("About to open tunnel...\n"); if (rbm_ssh_open_tunnel(session) != 0) { printf("Tunnel stopped because of error.\n"); return 1; } // // printf("Planned shutdown of the tunnel."); rbm_ssh_cleanup(); return 0; } ================================================ FILE: src/robomongo/ssh/ssh.c ================================================ #include "robomongo/ssh/private.h" #include "robomongo/ssh/libssh2_config.h" #ifdef WIN32 #include #include #include #else #include #include #include #include #include #include #endif #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include static int handle_new_client_connections(struct rbm_session *connection, int *fdmax, fd_set *masterset); static int handle_ssh_connections(struct rbm_session *connection, fd_set *masterset); static int handle_client_connections(struct rbm_session *connection, rbm_socket_t i, fd_set *masterset); static void rbm_sleep_ms(int ms); static void rbm_socket_close(rbm_socket_t socket); //===----------------------------------------------------------------------===// // Public API //===----------------------------------------------------------------------===// /* * Initialize SSH submodule, should be called only once * Returns 0 if succeeded, or a negative value for error. */ int rbm_ssh_init() { #ifndef WIN32 // Ignore SIGPIPE signal. If we will not do that, an attempt to send/write // to the socket that do not have readers will force OS to generate SIGPIPE // signal. Default handler for this signal is a termination of program. // While signal is ignored, send/write will still return error code, that // should be handled by the code. signal(SIGPIPE, SIG_IGN); #endif int err; #ifdef WIN32 WSADATA wsadata; err = WSAStartup(MAKEWORD(2,0), &wsadata); if (err) { log_error("WSAStartup failed with error code: (%d)", err); return RBM_ERROR; } #endif err = libssh2_init(0); if (err) { log_error("Initialization of LIBSSH2 failed with error code: (%d)", err); return RBM_ERROR; } return RBM_SUCCESS; } /* * Cleanup SSH submodule, should be called only once */ void rbm_ssh_cleanup() { libssh2_exit(); #ifdef WIN32 WSACleanup(); #endif } /* * Returns NULL on error, valid rbm_ssh_session* if no errors */ struct rbm_ssh_session *rbm_ssh_session_create(struct rbm_ssh_tunnel_config *config) { struct rbm_session *session = (struct rbm_session *) malloc(sizeof(struct rbm_session)); if (!session) return NULL; session->localsocket = rbm_socket_invalid; session->sshsocket = rbm_socket_invalid; session->sshsession = NULL; session->config = config; session->channels = NULL; session->channelssize = 0; session->lasterror[0] = '\0'; // Check that loglevel is valid if (config->loglevel != RBM_SSH_LOG_TYPE_ERROR && config->loglevel != RBM_SSH_LOG_TYPE_WARN && config->loglevel != RBM_SSH_LOG_TYPE_INFO && config->loglevel != RBM_SSH_LOG_TYPE_DEBUG) { log_error("Invalid log level for SSH submodule"); return NULL; } struct rbm_ssh_session *publicsession = (struct rbm_ssh_session *) malloc(sizeof(struct rbm_ssh_session)); if (!publicsession) return NULL; publicsession->lasterror = session->lasterror; // Point to each other publicsession->handle = session; session->publicsession = publicsession; return publicsession; } void rbm_ssh_session_close(struct rbm_ssh_session *sshsession) { struct rbm_session *session = (struct rbm_session*)sshsession->handle; if (session->localsocket != rbm_socket_invalid) { ssh_log_debug(session, "Closing local accept socket"); rbm_socket_close(session->localsocket); session->localsocket = rbm_socket_invalid; } rbm_session_cleanup(session); ssh_log_debug(session, "SSH tunnel successfully closed."); free(session); free(sshsession); } int rbm_ssh_open_tunnel(struct rbm_ssh_session *sshsession) { struct rbm_session *connection = (struct rbm_session*)sshsession->handle; int rc = 0; while (1) { rc = rbm_open_tunnel(connection); if (rc == 0) break; if (rc == RBM_CHANNEL_CREATION_ERROR) return RBM_ERROR; // Cleanup SSH connection we hope that local connection // will not break so often rbm_session_cleanup(connection); ssh_log_warn(connection, "Reconnecting SSH tunnel..."); if (rbm_ssh_setup(connection) == -1) return RBM_ERROR; } return RBM_SUCCESS; } // Returns -1 on error, 0 when otherwise int rbm_ssh_session_setup(struct rbm_ssh_session *sshsession) { struct rbm_session *session = (struct rbm_session*)sshsession->handle; struct rbm_ssh_tunnel_config *config = session->config; if (rbm_ssh_setup(session) == -1) return RBM_ERROR; session->localsocket = socket_listen(session, config->localip, (int *) &config->localport); if (session->localsocket == -1) { session->localsocket = rbm_socket_invalid; return RBM_ERROR; // errors are already logged by socket_listen } ssh_log_debug(session, "Waiting for TCP connection on %s:%d...", config->localip, config->localport); return RBM_SUCCESS; } //===----------------------------------------------------------------------===// // Private API //===----------------------------------------------------------------------===// // Returns NULL on error, or valid rbm_ssh_channel otherwise. struct rbm_channel *rbm_channel_create(struct rbm_session* session, rbm_socket_t socket, LIBSSH2_CHANNEL *lchannel) { struct rbm_channel *channel = malloc(sizeof(struct rbm_channel)); if (!channel) return NULL; char *inbuf = malloc(sizeof(char) * RBM_BUFSIZE); if (!inbuf) return NULL; char *outbuf = malloc(sizeof(char) * RBM_BUFSIZE); if (!outbuf) return NULL; channel->session = session; channel->channel = lchannel; channel->socket = socket; channel->inbuf = inbuf; channel->outbuf = outbuf; channel->bufmaxsize = RBM_BUFSIZE; if (rbm_array_add((void ***)&session->channels, &session->channelssize, channel)) return NULL; return channel; } void rbm_channel_close(struct rbm_channel *channel) { struct rbm_session *session = channel->session; if (rbm_array_remove((void ***)&session->channels, &session->channelssize, channel)) return; // 3. Close socket if (channel->socket != rbm_socket_invalid) { rbm_socket_close(channel->socket); channel->socket = rbm_socket_invalid; } // 1. Free libssh2 channel if (channel->channel) { libssh2_channel_free(channel->channel); channel->channel = NULL; } // 2. Free input/output buffers free(channel->inbuf); free(channel->outbuf); channel->inbuf = NULL; channel->outbuf = NULL; // 4. Free channel struct free(channel); ssh_log_debug(session, "Channel closed"); } /* * Returns channel for specified socket, or NULL otherwise */ struct rbm_channel *rbm_channel_find_by_socket(struct rbm_session *session, rbm_socket_t socket) { for (int i = 0; i < session->channelssize; i++) if (session->channels[i]->socket == socket) return session->channels[i]; return NULL; } // Returns -1 on error, 0 when otherwise int rbm_ssh_setup(struct rbm_session *session) { struct rbm_ssh_tunnel_config *config = session->config; ssh_log_debug(session, "Connecting to SSH server (%s:%d)...", config->sshserverhost, config->sshserverport); session->sshsocket = socket_connect(session, config->sshserverhost, config->sshserverport); if (session->sshsocket == -1) { session->sshsocket = rbm_socket_invalid; return RBM_ERROR; // errors are already logged by socket_connect } session->sshsession = ssh_connect(session, session->sshsocket, config->authtype, config->username, config->password, config->publickeyfile, config->privatekeyfile, config->passphrase); if (session->sshsession == 0) { return RBM_ERROR; // errors are already logged by ssh_connect } // Must use non-blocking IO hereafter due to the current libssh3 API libssh2_session_set_blocking(session->sshsession, 0); return RBM_SUCCESS; } void rbm_session_cleanup(struct rbm_session *session) { if (session == NULL) return; // Order is important: // 1. free libssh2 channels (and client sockets) // 2. free libssh2 session // 3. close ssh socket // 1. ssh_log_debug(session, "Closing channels"); while (session->channelssize > 0) { rbm_channel_close(session->channels[0]); } // 2. if (session->sshsession) { ssh_log_debug(session, "Closing SSH session"); libssh2_session_disconnect(session->sshsession, "Client disconnecting normally"); libssh2_session_free(session->sshsession); session->sshsession = NULL; } // 3. if (session->sshsocket != rbm_socket_invalid) { ssh_log_debug(session, "Closing SSH socket"); rbm_socket_close(session->sshsocket); session->sshsocket = rbm_socket_invalid; } } int rbm_open_tunnel(struct rbm_session *connection) { rbm_socket_t local_socket = connection->localsocket; rbm_socket_t ssh_socket = connection->sshsocket; const int maxerrors = 25; // number of serial errors, when we probably should stop the loop int errors = 0; // counter for serial errors int rc = 0; int fdmax; // maximum socket (file descriptor) number int isocket; // index for traversing sockets fd_set masterset, readset; FD_ZERO(&masterset); // Add the listener to the master set FD_SET(local_socket, &masterset); FD_SET(ssh_socket, &masterset); // Keep track of the biggest file descriptor fdmax = local_socket > ssh_socket ? local_socket : ssh_socket; while (errors < maxerrors) { readset = masterset; // copy set // If local (accept) socket is closed, it means that // session is closed and we should stop our work if (!FD_ISSET(local_socket, &readset)) break; ssh_log_debug(connection, "* Okay, we are ready to select."); if (select(fdmax + 1, &readset, NULL, NULL, NULL) == -1) { ssh_log_error(connection, "Error on select()"); break; } ssh_log_debug(connection, "* Selected!"); // Run through the existing connections looking for data to read for(isocket = 0; isocket <= fdmax; isocket++) { // Skip connections that are not available for reading if (!FD_ISSET(isocket, &readset)) continue; if (isocket == local_socket) { rc = handle_new_client_connections(connection, &fdmax, &masterset); goto next; } if (isocket == ssh_socket) { rc = handle_ssh_connections(connection, &masterset); goto next; } rc = handle_client_connections(connection, isocket, &masterset); next: // Increment "errors" counter, if we found an error, // or zero it, if not. errors = (rc == -2) ? errors + 1 : 0; if (errors > 0) { ssh_log_warn(connection, "*** COLLECTED %d AGAIN ***", errors); } if (rc == -1) { ssh_log_warn(connection, "SSH tunnel shutdown because of error"); return RBM_ERROR; } if (rc == RBM_CHANNEL_CREATION_ERROR) { return RBM_CHANNEL_CREATION_ERROR; } } } if (errors >= maxerrors) { ssh_log_warn(connection, "SSH tunnel shutdown because of series of successive EAGAIN errors"); return RBM_ERROR; } rbm_ssh_session_close(connection->publicsession); return RBM_SUCCESS; } // Return -1 on error. 0 otherwise. static int handle_new_client_connections(struct rbm_session *connection, int *fdmax, fd_set *masterset) { rbm_socket_t local_socket = connection->localsocket; rbm_socket_t ssh_socket = connection->sshsocket; LIBSSH2_SESSION* session = connection->sshsession; struct rbm_ssh_tunnel_config* config = connection->config; rbm_socket_t newfd = rbm_socket_invalid; ssh_log_debug(connection, "Data on accept socket is available"); struct sockaddr_in remoteaddr; socklen_t slen = sizeof(remoteaddr); // handle new connections if ((newfd = accept(local_socket, (struct sockaddr *) &remoteaddr, &slen)) == -1) { ssh_log_error(connection, "Error on accept()"); return RBM_ERROR; } else { FD_SET(newfd, masterset); // add to master set if (newfd > *fdmax) { // keep track of the maximum *fdmax = newfd; } ssh_log_debug(connection, "New connection from %s on socket %d", inet_ntoa(remoteaddr.sin_addr), newfd); } LIBSSH2_CHANNEL *channel = NULL; int maxattempts = 25; //45; int attempts = 0; while (attempts < maxattempts) { ++attempts; channel = libssh2_channel_direct_tcpip_ex(session, config->remotehost, config->remoteport, config->localip, config->localport); int errsave = errno; if (!channel) { int lerr = libssh2_session_last_error(connection->sshsession, NULL, NULL, 0); ssh_log_warn(connection, "Could not open the direct TCP/IP channel (%d)", lerr); // Error 35: Resource temporarily unavailable. if (lerr == LIBSSH2_ERROR_EAGAIN) { rbm_sleep_ms(200); // usleep(200 * 1000); continue; } break; } ssh_log_debug(connection, "Channel successfully created!"); break; } if (!channel) { ssh_log_error(connection, "Failed to create SSH channel"); return RBM_CHANNEL_CREATION_ERROR; } if (rbm_channel_create(connection, newfd, channel) == NULL) { return RBM_ERROR; } return RBM_SUCCESS; } // 0: success // -1: static int handle_ssh_connections(struct rbm_session *connection, fd_set *masterset) { const int AGAIN = -2; struct rbm_ssh_tunnel_config* config = connection->config; ssh_log_debug(connection, "Data on SSH socket is available"); ssh_log_debug(connection, "[%d] <- Number of channels", connection->channelssize); if (connection->channelssize == 0) { FD_CLR(connection->localsocket, masterset); // remove from master set FD_CLR(connection->sshsocket, masterset); // remove from master set return RBM_SUCCESS; } int s = 0; int result = RBM_SUCCESS; int eagain = 0; while (s < connection->channelssize) { struct rbm_channel *context = connection->channels[s]; ++s; int firstflag = 1; while (1) { int len; len = libssh2_channel_read(context->channel, context->outbuf, context->bufmaxsize); if (len == LIBSSH2_ERROR_EAGAIN) { if (firstflag) { ++eagain; if (eagain == connection->channelssize) { result = AGAIN; ssh_log_warn(connection, "All channels are in a non ready state (EAGAIN)"); } } // Proceed with the next channel break; } else if (len < 0) { // ETIMEDOUT (60) Connection timed out // We need to reconnect // if (errno == 60) { // return 2; // } // Endless cycle: // Network is down. libssh2_channel_read: -43. (Error #50) result = RBM_ERROR; ssh_log_error(connection, "libssh2_channel_read: %d", len); return RBM_ERROR; } firstflag = 0; ssh_log_debug(connection, "Received %d bytes from tunnel", len); int wr = 0; int rc = 0; // result while (wr < len) { rc = send(context->socket, context->outbuf + wr, len - wr, 0); if (rc <= 0) { result = RBM_ERROR; ssh_log_error(connection, "Failure to write data to client"); break; } wr += rc; } if (libssh2_channel_eof(context->channel)) { result = RBM_SUCCESS; ssh_log_debug(connection, "The server at %s:%d disconnected!\n", config->remotehost, config->remoteport); break; } } } return result; } static int handle_client_connections(struct rbm_session *connection, rbm_socket_t i, fd_set *masterset) { int result = RBM_SUCCESS; ssh_log_debug(connection, "Data on client socket is available"); struct rbm_channel *context = rbm_channel_find_by_socket(connection, i); if (!context) { rbm_socket_close(i); // bye! FD_CLR(i, masterset); // remove from master set return RBM_ERROR; } // Read data from a client int nbytes = recv(context->socket, context->inbuf, context->bufmaxsize, 0); if (nbytes <= 0) { if (nbytes == 0) { // Normal situation result = RBM_SUCCESS; ssh_log_debug(connection, "Client disconnected"); } else { // Got error result = RBM_ERROR; ssh_log_error(connection, "Error when recv()"); } // In both these cases, close and cleanup connection // rbm_socket_close(context->socket); // bye! FD_CLR(context->socket, masterset); // remove from master set rbm_channel_close(context); if (connection->channelssize == 0) { FD_CLR(connection->localsocket, masterset); // remove from master set FD_CLR(connection->sshsocket, masterset); // remove from master set } return result; } ssh_log_debug(connection, "Received %d bytes from client", nbytes); // Write data to ssh tunnel const int againmax = 100; int again = 0; int wr = 0; int rc = 0; // result while (wr < nbytes) { rc = libssh2_channel_write(context->channel, context->inbuf + wr, nbytes - wr); if (LIBSSH2_ERROR_EAGAIN == rc) { ++again; if (again > againmax) { ssh_log_warn(connection, "Number of attempts to libssh2_channel_write exсeed max value"); return RBM_ERROR; } continue; } if (rc < 0) { ssh_log_error(connection, "Failed to write to SSH channel"); return RBM_ERROR; } wr += rc; } ssh_log_debug(connection, "Written %d bytes to tunnel", wr); return RBM_SUCCESS; } /* * Returns socket if succeed, otherwise -1 on error */ static rbm_socket_t socket_connect(struct rbm_session* session, char *ip, int port) { rbm_socket_t sock; struct addrinfo hints, *res; int err; char cport[20]; // We need port as decimal chars for "getaddrinfo" sprintf(cport, "%d", port); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(ip, cport, &hints, &res); if (err) { ssh_log_error(session, "Failed to get address info"); return -1; } sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock == rbm_socket_invalid) { ssh_log_error(session, "Failed to open socket"); return -1; } /* Connect to SSH server */ if (connect(sock, res->ai_addr, res->ai_addrlen) != 0) { ssh_log_error(session, "Failed to connect to %s:%d", ip, port); return -1; } freeaddrinfo(res); return sock; } /* * Returns socket (binded and in listen state) if succeed, otherwise (rbm_socket_invalid) on error */ rbm_socket_t socket_listen(struct rbm_session *rsession, char *ip, int *port) { rbm_socket_t listensock; struct sockaddr_in sin; #ifdef WIN32 char sockopt; #else int sockopt; #endif socklen_t sinlen; listensock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (listensock == rbm_socket_invalid) { ssh_log_error(rsession, "Failed to open socket"); return rbm_socket_invalid; } sin.sin_family = AF_INET; sin.sin_port = htons(0); // Bind to any available port (htons is not needed, but still it's here) if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(ip))) { ssh_log_error(rsession, "inet_addr"); return rbm_socket_invalid; } sockopt = 1; setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); sinlen = sizeof(sin); if (-1 == bind(listensock, (struct sockaddr *)&sin, sinlen)) { ssh_log_error(rsession, "Cannot bind to port %d", port); return rbm_socket_invalid; } if (-1 == listen(listensock, 2)) { ssh_log_error(rsession, "Failed to listen opened socket"); return rbm_socket_invalid; } if (getsockname(listensock, (struct sockaddr *)&sin, &sinlen) == -1) { ssh_log_error(rsession, "Failed to get socket address"); return rbm_socket_invalid; } *port = ntohs(sin.sin_port); return listensock; } /* * Returns 0 if error. */ LIBSSH2_SESSION *ssh_connect(struct rbm_session *rsession, rbm_socket_t sock, enum rbm_ssh_auth_type type, char *username, char *password, char *publickeypath, char *privatekeypath, char *passphrase) { int rc, auth = RBM_SSH_AUTH_TYPE_NONE; LIBSSH2_SESSION *session; const char *fingerprint; char *userauthlist; ssh_log_debug(rsession, "ssh_connect: username: %s", username); ssh_log_debug(rsession, "ssh_connect: privatekeyfile: %s", privatekeypath); ssh_log_debug(rsession, "ssh_connect: publickeyfile: %s", publickeypath); /* Create a session instance */ session = libssh2_session_init(); if (!session) { ssh_log_error(rsession, "Could not initialize SSH session"); return 0; } /* ... start it up. This will trade welcome banners, exchange keys, * and setup crypto, compression, and MAC layers */ rc = libssh2_session_handshake(session, sock); if (rc) { ssh_log_error(rsession, "Error when starting up SSH session: %d\n", rc); return 0; } /* At this point we havn't yet authenticated. The first thing to do * is check the hostkey's fingerprint against our known hosts Your app * may have it hard coded, may go to a file, may present it to the * user, that's your call */ fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); /* fprintf(stderr, "Fingerprint: "); for(i = 0; i < 20; i++) fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); fprintf(stderr, "\n"); */ /* check what authentication methods are available */ userauthlist = libssh2_userauth_list(session, username, strlen(username)); ssh_log_debug(rsession, "Authentication methods: %s", userauthlist); if (strstr(userauthlist, "password")) auth |= RBM_SSH_AUTH_TYPE_PASSWORD; if (strstr(userauthlist, "publickey")) auth |= RBM_SSH_AUTH_TYPE_PUBLICKEY; // If authentication by password is available // and was chosen by the user, then use it if (auth & RBM_SSH_AUTH_TYPE_PASSWORD && type == RBM_SSH_AUTH_TYPE_PASSWORD) { if (libssh2_userauth_password(session, username, password)) { ssh_log_error(rsession, "Authentication by password failed"); return 0; } ssh_log_debug(rsession, "Authentication by password succeeded."); // If authentication by key is available // and was chosen by the user, then use it } else if (auth & RBM_SSH_AUTH_TYPE_PUBLICKEY && type == RBM_SSH_AUTH_TYPE_PUBLICKEY) { rc = libssh2_userauth_publickey_fromfile(session, username, publickeypath, privatekeypath, passphrase); if (rc) { ssh_log_error(rsession, "Authentication by key (%s) failed (Error %d)", privatekeypath, rc); return 0; } ssh_log_debug(rsession, "Authentication by key succeeded."); } else { ssh_log_error(rsession, "No supported authentication methods found"); return 0; } return session; } static void rbm_sleep_ms(int ms) { #ifdef WIN32 Sleep(ms); #else usleep(ms * 1000); #endif } static void rbm_socket_close(rbm_socket_t socket) { #ifdef WIN32 closesocket(socket); #else close(socket); #endif } ================================================ FILE: src/robomongo/ssh/ssh.h ================================================ #ifndef ROBOMONGO_SSH_H #define ROBOMONGO_SSH_H #ifdef __cplusplus extern "C" { #endif enum rbm_ssh_log_type { RBM_SSH_LOG_TYPE_ERROR = 1, RBM_SSH_LOG_TYPE_WARN = 2, RBM_SSH_LOG_TYPE_INFO = 3, RBM_SSH_LOG_TYPE_DEBUG = 100 // log as much as possible }; enum rbm_ssh_auth_type { RBM_SSH_AUTH_TYPE_NONE = 0, RBM_SSH_AUTH_TYPE_PASSWORD = 1, RBM_SSH_AUTH_TYPE_PUBLICKEY = 2 }; /* * SSH Tunnel configuration */ struct rbm_ssh_tunnel_config { enum rbm_ssh_auth_type authtype; // Keys and optional passphrase char *privatekeyfile; char *publickeyfile; char *passphrase; // May be NULL or "" // Local IP and port to bind and listen to char *localip; unsigned int localport; // Remote host (domain or IPv4/v6) and port to connect to char *remotehost; // Resolved by the remote server unsigned int remoteport; // Username and password of remote user char *username; char *password; // May be NULL or "" // Remote host (domain or IPv4/v6) char *sshserverhost; unsigned int sshserverport; // Logging facilities enum rbm_ssh_log_type loglevel; void *logcontext; // Pointer to user-defined data (can be NULL) void (*logcallback)(void *logcontext, char *message, int level); }; struct rbm_ssh_session { char *lasterror; void *handle; // opaque pointer to rbm_session }; int rbm_ssh_init(); void rbm_ssh_cleanup(); struct rbm_ssh_session* rbm_ssh_session_create(struct rbm_ssh_tunnel_config *config); int rbm_ssh_open_tunnel(struct rbm_ssh_session *connection); int rbm_ssh_session_setup(struct rbm_ssh_session *session); void rbm_ssh_session_close(struct rbm_ssh_session *session); #ifdef __cplusplus } #endif #endif // ROBOMONGO_SSH_H ================================================ FILE: src/robomongo/ssh/temp/direct_tcpip.c ================================================ #include "libssh2_config.h" #include #ifdef WIN32 #include #include #include #else #include #include #include #include #endif #include #include #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_SYS_SELECT_H #include #endif #ifndef INADDR_NONE #define INADDR_NONE (in_addr_t)-1 #endif const char *keyfile1 = "/Users/dmitry/.ssh/ubuntik.pub"; const char *keyfile2 = "/Users/dmitry/.ssh/ubuntik"; const char *username = "username"; const char *password = ""; const char *server_ip = "127.0.0.1"; const char *local_listenip = "127.0.0.1"; unsigned int local_listenport = 2222; const char *remote_desthost = "localhost"; /* resolved by the server */ unsigned int remote_destport = 22; enum { AUTH_NONE = 0, AUTH_PASSWORD, AUTH_PUBLICKEY }; int main(int argc, char *argv[]) { int rc, i, auth = AUTH_NONE; struct sockaddr_in sin; socklen_t sinlen; const char *fingerprint; char *userauthlist; LIBSSH2_SESSION *session; LIBSSH2_CHANNEL *channel = NULL; const char *shost; unsigned int sport; fd_set fds; struct timeval tv; ssize_t len, wr; char buf[16384]; #ifdef WIN32 char sockopt; SOCKET sock = INVALID_SOCKET; SOCKET listensock = INVALID_SOCKET, forwardsock = INVALID_SOCKET; WSADATA wsadata; int err; err = WSAStartup(MAKEWORD(2,0), &wsadata); if (err != 0) { fprintf(stderr, "WSAStartup failed with error: %d\n", err); return 1; } #else int sockopt, sock = -1; int listensock = -1, forwardsock = -1; #endif if (argc > 1) server_ip = argv[1]; if (argc > 2) username = argv[2]; if (argc > 3) password = argv[3]; if (argc > 4) local_listenip = argv[4]; if (argc > 5) local_listenport = atoi(argv[5]); if (argc > 6) remote_desthost = argv[6]; if (argc > 7) remote_destport = atoi(argv[7]); rc = libssh2_init (0); if (rc != 0) { fprintf (stderr, "libssh2 initialization failed (%d)\n", rc); return 1; } /* Connect to SSH server */ sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); #ifdef WIN32 if (sock == INVALID_SOCKET) { fprintf(stderr, "failed to open socket!\n"); return -1; } #else if (sock == -1) { perror("socket"); return -1; } #endif sin.sin_family = AF_INET; if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(server_ip))) { perror("inet_addr"); return -1; } sin.sin_port = htons(22); if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) { fprintf(stderr, "failed to connect!\n"); return -1; } /* Create a session instance */ session = libssh2_session_init(); if(!session) { fprintf(stderr, "Could not initialize SSH session!\n"); return -1; } /* ... start it up. This will trade welcome banners, exchange keys, * and setup crypto, compression, and MAC layers */ rc = libssh2_session_handshake(session, sock); if(rc) { fprintf(stderr, "Error when starting up SSH session: %d\n", rc); return -1; } /* At this point we havn't yet authenticated. The first thing to do * is check the hostkey's fingerprint against our known hosts Your app * may have it hard coded, may go to a file, may present it to the * user, that's your call */ fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); fprintf(stderr, "Fingerprint: "); for(i = 0; i < 20; i++) fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); fprintf(stderr, "\n"); /* check what authentication methods are available */ userauthlist = libssh2_userauth_list(session, username, strlen(username)); fprintf(stderr, "Authentication methods: %s\n", userauthlist); if (strstr(userauthlist, "password")) auth |= AUTH_PASSWORD; if (strstr(userauthlist, "publickey")) auth |= AUTH_PUBLICKEY; /* check for options */ if(argc > 8) { if ((auth & AUTH_PASSWORD) && !strcasecmp(argv[8], "-p")) auth = AUTH_PASSWORD; if ((auth & AUTH_PUBLICKEY) && !strcasecmp(argv[8], "-k")) auth = AUTH_PUBLICKEY; } if (auth & AUTH_PASSWORD) { if (libssh2_userauth_password(session, username, password)) { fprintf(stderr, "Authentication by password failed.\n"); goto shutdown; } } else if (auth & AUTH_PUBLICKEY) { fprintf(stderr, "\tPassword: %s\n", password); if (libssh2_userauth_publickey_fromfile(session, username, keyfile1, keyfile2, password)) { fprintf(stderr, "\tAuthentication by public key failed!\n"); goto shutdown; } fprintf(stderr, "\tAuthentication by public key succeeded.\n"); } else { fprintf(stderr, "No supported authentication methods found!\n"); goto shutdown; } listensock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); #ifdef WIN32 if (listensock == INVALID_SOCKET) { fprintf(stderr, "failed to open listen socket!\n"); return -1; } #else if (listensock == -1) { perror("socket"); return -1; } #endif sin.sin_family = AF_INET; sin.sin_port = htons(local_listenport); if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(local_listenip))) { perror("inet_addr"); goto shutdown; } sockopt = 1; setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); sinlen=sizeof(sin); if (-1 == bind(listensock, (struct sockaddr *)&sin, sinlen)) { perror("bind"); goto shutdown; } if (-1 == listen(listensock, 2)) { perror("listen"); goto shutdown; } fprintf(stderr, "Waiting for TCP connection on %s:%d...\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); forwardsock = accept(listensock, (struct sockaddr *)&sin, &sinlen); #ifdef WIN32 if (forwardsock == INVALID_SOCKET) { fprintf(stderr, "failed to accept forward socket!\n"); goto shutdown; } #else if (forwardsock == -1) { perror("accept"); goto shutdown; } #endif shost = inet_ntoa(sin.sin_addr); sport = local_listenport; // ntohs(sin.sin_port); fprintf(stderr, "Forwarding connection from %s:%d here to remote %s:%d\n", shost, sport, remote_desthost, remote_destport); channel = libssh2_channel_direct_tcpip_ex(session, remote_desthost, remote_destport, shost, sport); if (!channel) { fprintf(stderr, "Could not open the direct-tcpip channel!\n" "(Note that this can be a problem at the server!" " Please review the server logs.)\n"); goto shutdown; } /* Must use non-blocking IO hereafter due to the current libssh2 API */ libssh2_session_set_blocking(session, 0); while (1) { FD_ZERO(&fds); FD_SET(forwardsock, &fds); tv.tv_sec = 0; tv.tv_usec = 100000; rc = select(forwardsock + 1, &fds, NULL, NULL, &tv); if (-1 == rc) { perror("select"); goto shutdown; } if (rc && FD_ISSET(forwardsock, &fds)) { len = recv(forwardsock, buf, sizeof(buf), 0); if (len < 0) { perror("read"); goto shutdown; } else if (0 == len) { fprintf(stderr, "The client at %s:%d disconnected!\n", shost, sport); goto shutdown; } wr = 0; while(wr < len) { i = libssh2_channel_write(channel, buf + wr, len - wr); if (LIBSSH2_ERROR_EAGAIN == i) { continue; } if (i < 0) { fprintf(stderr, "libssh2_channel_write: %d\n", i); goto shutdown; } wr += i; } } while (1) { len = libssh2_channel_read(channel, buf, sizeof(buf)); if (LIBSSH2_ERROR_EAGAIN == len) break; else if (len < 0) { fprintf(stderr, "libssh2_channel_read: %d", (int)len); goto shutdown; } wr = 0; while (wr < len) { i = send(forwardsock, buf + wr, len - wr, 0); if (i <= 0) { perror("write"); goto shutdown; } wr += i; } if (libssh2_channel_eof(channel)) { fprintf(stderr, "The server at %s:%d disconnected!\n", remote_desthost, remote_destport); goto shutdown; } } } shutdown: #ifdef WIN32 closesocket(forwardsock); closesocket(listensock); #else close(forwardsock); close(listensock); #endif if (channel) libssh2_channel_free(channel); libssh2_session_disconnect(session, "Client disconnecting normally"); libssh2_session_free(session); #ifdef WIN32 closesocket(sock); #else close(sock); #endif libssh2_exit(); return 0; } ================================================ FILE: src/robomongo/ssh/temp/internal.h ================================================ #include "robomongo/ssh/ssh.h" #include int log_error(const char *format, ...); int log_msg(const char *format, ...); void ssh_log_v(rbm_ssh_session* session, enum rbm_ssh_log_type type, const char *format, va_list args, int errsave); void ssh_log_msg(rbm_ssh_session* session, const char *format, ...); void ssh_log_warn(rbm_ssh_session* session, const char *format, ...); void ssh_log_debug(rbm_ssh_session* session, const char *format, ...); void ssh_log_error(rbm_ssh_session* session, const char *format, ...); ================================================ FILE: src/robomongo/ssh/temp/temp_ssh.c ================================================ #include "robomongo/ssh/ssh.h" #include "robomongo/ssh/internal.h" #include "robomongo/ssh/libssh2_config.h" #ifdef WIN32 #include #include #include #else #include #include #include #include #endif #include #include #include #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_SYS_SELECT_H #include #endif #ifndef INADDR_NONE #define INADDR_NONE (in_addr_t)-1 #endif /* * Initialises Sockets and Libssh * Returns 0 if initialization succeeds */ int rbm_ssh_init() { int err; #ifdef WIN32 WSADATA wsadata; #endif #ifdef WIN32 err = WSAStartup(MAKEWORD(2,0), &wsadata); if (err != 0) { log_error("WSAStartup failed with error: %d", err); return 1; } #endif err = libssh2_init (0); if (err != 0) { log_error("libssh2 initialization failed (%d)", err); return 1; } return 0; } /* * Cleanups Sockets and Libssh */ void rbm_ssh_cleanup() { libssh2_exit(); #ifdef WIN32 WSACleanup(); #endif } /* * Returns socket if succeed, otherwise -1 on error */ static rbm_socket_t socket_connect(rbm_ssh_session* session, char *ip, int port) { rbm_socket_t sock; struct sockaddr_in sin; /* Connect to SSH server */ sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == rbm_socket_invalid) { ssh_log_error(session, "Failed to open socket"); return -1; } sin.sin_family = AF_INET; if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(ip))) { ssh_log_error(session, "Call to inet_addr failed"); return -1; } sin.sin_port = htons(port); if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) { ssh_log_error(session, "Failed to connect to %s:%d", ip, port); return -1; } return sock; } /* * Returns socket (binded and in listen state) if succeed, otherwise (rbm_socket_invalid) on error */ rbm_socket_t socket_listen(rbm_ssh_session *rsession, char *ip, int *port) { rbm_socket_t listensock; struct sockaddr_in sin; int sockopt; socklen_t sinlen; listensock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (listensock == rbm_socket_invalid) { ssh_log_error(rsession, "Failed to open socket"); return rbm_socket_invalid; } sin.sin_family = AF_INET; sin.sin_port = htons(0); // Bind to any available port (htons is not needed, but still it's here) if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(ip))) { ssh_log_error(rsession, "inet_addr"); return rbm_socket_invalid; } sockopt = 1; setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); sinlen = sizeof(sin); if (-1 == bind(listensock, (struct sockaddr *)&sin, sinlen)) { ssh_log_error(rsession, "Cannot bind to port %d", port); return rbm_socket_invalid; } if (-1 == listen(listensock, 2)) { ssh_log_error(rsession, "Failed to listen opened socket"); return rbm_socket_invalid; } if (getsockname(listensock, (struct sockaddr *)&sin, &sinlen) == -1) { ssh_log_error(rsession, "Failed to get socket address"); return rbm_socket_invalid; } *port = ntohs(sin.sin_port); return listensock; } /* * Returns 0 if error. */ LIBSSH2_SESSION *ssh_connect(rbm_ssh_session *rsession, rbm_socket_t sock, enum rbm_ssh_auth_type type, char *username, char *password, char *publickeypath, char *privatekeypath, char *passphrase) { int rc, i, auth = RBM_SSH_AUTH_TYPE_NONE; LIBSSH2_SESSION *session; const char *fingerprint; char *userauthlist; ssh_log_debug(rsession, "ssh_connect: username: %s", username); ssh_log_debug(rsession, "ssh_connect: password: %s", password); ssh_log_debug(rsession, "ssh_connect: privatekeyfile: %s", privatekeypath); ssh_log_debug(rsession, "ssh_connect: publickeyfile: %s", publickeypath); /* Create a session instance */ session = libssh2_session_init(); if (!session) { ssh_log_error(rsession, "Could not initialize SSH session"); return 0; } /* ... start it up. This will trade welcome banners, exchange keys, * and setup crypto, compression, and MAC layers */ rc = libssh2_session_handshake(session, sock); if (rc) { ssh_log_error(rsession, "Error when starting up SSH session: %d\n", rc); return 0; } /* At this point we havn't yet authenticated. The first thing to do * is check the hostkey's fingerprint against our known hosts Your app * may have it hard coded, may go to a file, may present it to the * user, that's your call */ fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); /* fprintf(stderr, "Fingerprint: "); for(i = 0; i < 20; i++) fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); fprintf(stderr, "\n"); */ /* check what authentication methods are available */ userauthlist = libssh2_userauth_list(session, username, strlen(username)); ssh_log_debug(rsession, "Authentication methods: %s", userauthlist); if (strstr(userauthlist, "password")) auth |= RBM_SSH_AUTH_TYPE_PASSWORD; if (strstr(userauthlist, "publickey")) auth |= RBM_SSH_AUTH_TYPE_PUBLICKEY; // If authentication by password is available // and was chosen by the user, then use it if (auth & RBM_SSH_AUTH_TYPE_PASSWORD && type == RBM_SSH_AUTH_TYPE_PASSWORD) { if (libssh2_userauth_password(session, username, password)) { ssh_log_error(rsession, "Authentication by password failed"); return 0; } ssh_log_debug(rsession, "Authentication by password succeeded."); // If authentication by key is available // and was chosen by the user, then use it } else if (auth & RBM_SSH_AUTH_TYPE_PUBLICKEY && type == RBM_SSH_AUTH_TYPE_PUBLICKEY) { rc = libssh2_userauth_publickey_fromfile(session, username, publickeypath, privatekeypath, passphrase); if (rc) { ssh_log_error(rsession, "Authentication by key (%s) failed (Error %d)", privatekeypath, rc); return 0; } ssh_log_debug(rsession, "Authentication by key succeeded."); } else { ssh_log_error(rsession, "No supported authentication methods found"); return 0; } return session; } // Returns NULL on error, or valid rbm_ssh_channel otherwise. rbm_ssh_channel *ssh_channel_create(rbm_ssh_session* session, rbm_socket_t socket, LIBSSH2_CHANNEL *lchannel) { const int bufsize = 16384; rbm_ssh_channel **channels; rbm_ssh_channel *channel = malloc(sizeof(rbm_ssh_channel)); channel->session = session; channel->channel = lchannel; channel->socket = socket; channel->inbuf = malloc(sizeof(char) * bufsize); channel->outbuf = malloc(sizeof(char) * bufsize); channel->bufmaxsize = bufsize; channels = (rbm_ssh_channel**) realloc (session->channels, (session->channelssize + 1) * sizeof(rbm_ssh_channel*)); // acts like malloc when (session->channels == NULL) if (!channels) { ssh_log_error(session, "Not enough memory (call to realloc)"); return NULL; } channels[session->channelssize] = channel; session->channels = channels; ++session->channelssize; return channel; } void ssh_channel_close(rbm_ssh_channel* channel) { int i; rbm_ssh_session* session; rbm_ssh_channel** channels; session = channel->session; for (i = 0; i < session->channelssize; i++) { if (session->channels[i] != channel) { continue; } if (session->channelssize == 1) { channels = NULL; } else { channels = (rbm_ssh_channel**) malloc((session->channelssize - 1) * sizeof(rbm_ssh_channel*)); memcpy(channels, session->channels, i * sizeof(rbm_ssh_channel*)); if (i + 1 < session->channelssize) { memcpy(channels + i, session->channels + i + 1, (session->channelssize - i - 1) * sizeof(rbm_ssh_channel*)); } } free(session->channels); session->channels = channels; --session->channelssize; // 1. Free libssh2 channel if (channel->channel) { libssh2_channel_free(channel->channel); channel->channel = NULL; } // 2. Free input/output buffers free(channel->inbuf); channel->inbuf = NULL; free(channel->outbuf); channel->outbuf = NULL; // 3. Close socket if (channel->socket != rbm_socket_invalid) { close(channel->socket); channel->socket = rbm_socket_invalid; } // 4. Free channel struct free(channel); ssh_log_debug(session, "Channel closed"); break; } } rbm_ssh_channel* ssh_channel_find_by_socket(rbm_ssh_session* session, rbm_socket_t socket) { for (int i = 0; i < session->channelssize; i++) { if (session->channels[i]->socket == socket) { return session->channels[i]; } } return NULL; } // Return -1 on error. 0 otherwise. static int handle_new_client_connections(rbm_ssh_session *connection, int *fdmax, fd_set *masterset) { rbm_socket_t local_socket = connection->localsocket; rbm_socket_t ssh_socket = connection->sshsocket; LIBSSH2_SESSION* session = connection->sshsession; struct rbm_ssh_tunnel_config* config = connection->config; rbm_socket_t newfd = rbm_socket_invalid; const int ERROR = -1; const int SUCCESS = 0; ssh_log_debug(connection, "Data on accept socket is available"); struct sockaddr_in remoteaddr; socklen_t slen = sizeof(remoteaddr); // handle new connections if ((newfd = accept(local_socket, (struct sockaddr *) &remoteaddr, &slen)) == -1) { ssh_log_error(connection, "Error on accept()"); return ERROR; } else { FD_SET(newfd, masterset); // add to master set if (newfd > *fdmax) { // keep track of the maximum *fdmax = newfd; } ssh_log_debug(connection, "New connection from %s on socket %d", inet_ntoa(remoteaddr.sin_addr), newfd); } LIBSSH2_CHANNEL *channel = NULL; int maxattempts = 25; //45; int attempts = 0; while (attempts < maxattempts) { ++attempts; channel = libssh2_channel_direct_tcpip_ex(session, config->remotehost, config->remoteport, config->localip, config->localport); int errsave = errno; if (!channel) { ssh_log_warn(connection, "Could not open the direct TCP/IP channel (%d)", channel); // Error 35: Resource temporarily unavailable. if (errsave == 35) { usleep(200 * 1000); continue; } break; } ssh_log_debug(connection, "Channel successfully created!"); break; } if (!channel) { ssh_log_error(connection, "Failed to create SSH channel"); return ERROR; } if (ssh_channel_create(connection, newfd, channel) == NULL) { return ERROR; } return SUCCESS; } // 0: success // -1: static int handle_ssh_connections(rbm_ssh_session *connection, fd_set *masterset) { const int AGAIN = -2; const int ERROR = -1; const int SUCCESS = 0; struct rbm_ssh_tunnel_config* config = connection->config; ssh_log_debug(connection, "Data on SSH socket is available"); ssh_log_debug(connection, "[%d] <- Number of channels", connection->channelssize); if (connection->channelssize == 0) { FD_CLR(connection->localsocket, masterset); // remove from master set FD_CLR(connection->sshsocket, masterset); // remove from master set return SUCCESS; } int s = 0; int result = SUCCESS; int eagain = 0; while (s < connection->channelssize) { rbm_ssh_channel *context = connection->channels[s]; ++s; int firstflag = 1; while (1) { int len; len = libssh2_channel_read(context->channel, context->outbuf, context->bufmaxsize); if (len == LIBSSH2_ERROR_EAGAIN) { if (firstflag) { ++eagain; if (eagain == connection->channelssize) { result = AGAIN; ssh_log_warn(connection, "All channels are in a non ready state (EAGAIN)"); } } // Proceed with the next channel break; } else if (len < 0) { // ETIMEDOUT (60) Connection timed out // We need to reconnect // if (errno == 60) { // return 2; // } // Endless cycle: // Network is down. libssh2_channel_read: -43. (Error #50) result = ERROR; ssh_log_error(connection, "libssh2_channel_read: %d", len); break; } firstflag = 0; ssh_log_debug(connection, "Received %d bytes from tunnel", len); int wr = 0; int rc = 0; // result while (wr < len) { rc = send(context->socket, context->outbuf + wr, len - wr, 0); if (rc <= 0) { result = ERROR; ssh_log_error(connection, "Failure to write data to client"); break; } wr += rc; } if (libssh2_channel_eof(context->channel)) { result = SUCCESS; ssh_log_debug(connection, "The server at %s:%d disconnected!\n", config->remotehost, config->remoteport); break; } } } return result; } static int handle_client_connections(rbm_ssh_session *connection, rbm_socket_t i, fd_set *masterset) { const int ERROR = -1; const int SUCCESS = 0; int result = SUCCESS; ssh_log_debug(connection, "Data on client socket is available"); rbm_ssh_channel *context = ssh_channel_find_by_socket(connection, i); if (!context) { close(i); // bye! FD_CLR(i, masterset); // remove from master set return ERROR; } // Read data from a client int nbytes = recv(context->socket, context->inbuf, context->bufmaxsize, 0); if (nbytes <= 0) { if (nbytes == 0) { // Normal situation result = SUCCESS; ssh_log_debug(connection, "Client disconnected"); } else { // Got error result = ERROR; ssh_log_error(connection, "Error when recv()"); } // In both these cases, close and cleanup connection close(context->socket); // bye! FD_CLR(context->socket, masterset); // remove from master set ssh_channel_close(context); if (connection->channelssize == 0) { FD_CLR(connection->localsocket, masterset); // remove from master set FD_CLR(connection->sshsocket, masterset); // remove from master set } return result; } ssh_log_debug(connection, "Received %d bytes from client", nbytes); // Write data to ssh tunnel const int againmax = 100; int again = 0; int wr = 0; int rc = 0; // result while (wr < nbytes) { rc = libssh2_channel_write(context->channel, context->inbuf + wr, nbytes - wr); if (LIBSSH2_ERROR_EAGAIN == rc) { ++again; if (again > againmax) { ssh_log_warn(connection, "Number of attempts to libssh2_channel_write exсeed max value"); return ERROR; } continue; } if (rc < 0) { ssh_log_error(connection, "Failed to write to SSH channel"); return ERROR; } wr += rc; } ssh_log_debug(connection, "Written %d bytes to tunnel", wr); return SUCCESS; } int rbm_ssh_open_tunnel_ex(rbm_ssh_session *connection) { const int ERROR = -1; const int SUCCESS = 0; rbm_socket_t local_socket = connection->localsocket; rbm_socket_t ssh_socket = connection->sshsocket; const int maxerrors = 25; // number of serial errors, when we probably should stop the loop int errors = 0; // counter for serial errors int rc = 0; int fdmax; // maximum socket (file descriptor) number int isocket; // index for traversing sockets fd_set masterset, readset, clearset; FD_ZERO(&masterset); FD_ZERO(&clearset); // Add the listener to the master set FD_SET(local_socket, &masterset); FD_SET(ssh_socket, &masterset); // Keep track of the biggest file descriptor fdmax = local_socket > ssh_socket ? local_socket : ssh_socket; while (errors < maxerrors) { readset = masterset; // copy set // If readset has no descriptors, it means that // session is closed and we should stop our work if (!memcmp(&readset, &clearset, sizeof(fd_set))) break; ssh_log_debug(connection, "* Okay, we are ready to select."); if (select(fdmax + 1, &readset, NULL, NULL, NULL) == -1) { ssh_log_error(connection, "Error on select()"); break; } ssh_log_debug(connection, "* Selected!"); // Run through the existing connections looking for data to read for(isocket = 0; isocket <= fdmax; isocket++) { // Skip connections that are not available for reading if (!FD_ISSET(isocket, &readset)) continue; if (isocket == local_socket) { rc = handle_new_client_connections(connection, &fdmax, &masterset); goto next; } if (isocket == ssh_socket) { rc = handle_ssh_connections(connection, &masterset); goto next; } rc = handle_client_connections(connection, isocket, &masterset); next: // Increment "errors" counter, if we found an error, // or zero it, if not. errors = (rc == -2) ? errors + 1 : 0; if (errors > 0) { ssh_log_warn(connection, "*** COLLECTED %d AGAIN ***", errors); } if (rc == -1) { ssh_log_warn(connection, "SSH tunnel shutdown because of error"); return ERROR; } } } if (errors >= maxerrors) { ssh_log_warn(connection, "SSH tunnel shutdown because of series of successive EAGAIN errors"); return ERROR; } rbm_ssh_session_close(connection); return SUCCESS; } void rbm_ssh_session_cleanup(rbm_ssh_session *session); int rbm_ssh_open_tunnel(rbm_ssh_session *connection) { const int ERROR = -1; const int SUCCESS = 0; int rc = 0; while (1) { rc = rbm_ssh_open_tunnel_ex(connection); if (rc == 0) break; // Cleanup SSH connection we hope that local connection // will not break so often rbm_ssh_session_cleanup(connection); ssh_log_warn(connection, "STARTING AGAIN!!!!!!!!!111"); if (rbm_ssh_setup(connection) == -1) { rbm_ssh_session_close(connection); return ERROR; } } return SUCCESS; } void rbm_ssh_session_cleanup(rbm_ssh_session *session) { if (session == NULL) { return; } // Order is important: // 1. close accept socket // 2. free libssh2 channels (and client sockets) // 3. free libssh2 session // 4. close ssh socket // 2. ssh_log_debug(session, "Closing channels"); while (session->channelssize > 0) { ssh_channel_close(session->channels[0]); ssh_log_debug(session, "Channel closed"); } // 3. if (session->sshsession) { ssh_log_debug(session, "Closing SSH session"); libssh2_session_disconnect(session->sshsession, "Client disconnecting normally"); libssh2_session_free(session->sshsession); session->sshsession = NULL; } // 4. if (session->sshsocket != rbm_socket_invalid) { ssh_log_debug(session, "Closing SSH socket"); close(session->sshsocket); session->sshsocket = rbm_socket_invalid; } } void rbm_ssh_session_close(rbm_ssh_session *session) { if (session->localsocket != rbm_socket_invalid) { ssh_log_debug(session, "Closing local accept socket"); close(session->localsocket); session->localsocket = rbm_socket_invalid; } rbm_ssh_session_cleanup(session); ssh_log_debug(session, "SSH tunnel successfully closed."); free(session); } // Returns 0 on error, valid rbm_ssh_session* if no errors rbm_ssh_session* rbm_ssh_session_create(struct rbm_ssh_tunnel_config *config) { rbm_ssh_session *session = (rbm_ssh_session *) malloc(sizeof(rbm_ssh_session)); if (!session) { return NULL; } session->localsocket = rbm_socket_invalid; session->sshsocket = rbm_socket_invalid; session->sshsession = 0; session->config = config; session->channelssize = 0; session->channels = NULL; session->lasterror[0] = '\0'; // Check that loglevel is valid if (config->loglevel != RBM_SSH_LOG_TYPE_ERROR && config->loglevel != RBM_SSH_LOG_TYPE_WARN && config->loglevel != RBM_SSH_LOG_TYPE_INFO && config->loglevel != RBM_SSH_LOG_TYPE_DEBUG) { log_error("Invalid log level for SSH submodule"); return NULL; } return session; } // Returns -1 on error, 0 when otherwise int rbm_ssh_setup(rbm_ssh_session *session) { const int ERROR = -1; const int SUCCESS = 0; rbm_ssh_tunnel_config *config = session->config; ssh_log_debug(session, "Connecting to SSH server (%s:%d)...", config->sshserverip, config->sshserverport); session->sshsocket = socket_connect(session, config->sshserverip, config->sshserverport); if (session->sshsocket == -1) { session->sshsocket = rbm_socket_invalid; return ERROR; // errors are already logged by socket_connect } session->sshsession = ssh_connect(session, session->sshsocket, config->authtype, config->username, config->password, config->publickeyfile, config->privatekeyfile, config->passphrase); if (session->sshsession == 0) { return ERROR; // errors are already logged by ssh_connect } // Must use non-blocking IO hereafter due to the current libssh3 API libssh2_session_set_blocking(session->sshsession, 0); return SUCCESS; } // Returns -1 on error, 0 when otherwise int rbm_ssh_session_setup(rbm_ssh_session *session) { const int ERROR = -1; const int SUCCESS = 0; rbm_ssh_tunnel_config *config = session->config; if (rbm_ssh_setup(session) == -1) return ERROR; session->localsocket = socket_listen(session, config->localip, (int *) &config->localport); if (session->localsocket == -1) { session->localsocket = rbm_socket_invalid; return ERROR; // errors are already logged by socket_listen } ssh_log_debug(session, "Waiting for TCP connection on %s:%d...", config->localip, config->localport); return SUCCESS; } ================================================ FILE: src/robomongo/ssh/temp/temp_ssh.h ================================================ #ifndef ROBOMONGO_SSH_H #define ROBOMONGO_SSH_H #include #ifdef __cplusplus extern "C" { #endif #ifdef _WIN32 # define rbm_socket_t SOCKET # define rbm_socket_invalid INVALID_SOCKET #else # define rbm_socket_t int # define rbm_socket_invalid (-1) #endif enum rbm_ssh_log_type { RBM_SSH_LOG_TYPE_ERROR = 1, RBM_SSH_LOG_TYPE_WARN = 2, RBM_SSH_LOG_TYPE_INFO = 3, RBM_SSH_LOG_TYPE_DEBUG = 100 // log as much as possible }; enum rbm_ssh_auth_type { RBM_SSH_AUTH_TYPE_NONE = 0, RBM_SSH_AUTH_TYPE_PASSWORD = 1, RBM_SSH_AUTH_TYPE_PUBLICKEY = 2 }; struct rbm_ssh_session; /* * SSH Tunnel configuration */ typedef struct rbm_ssh_tunnel_config { enum rbm_ssh_auth_type authtype; enum rbm_ssh_log_type loglevel; // Keys and optional passphrase char *privatekeyfile; char *publickeyfile; char *passphrase; // May be NULL or "" // Local IP and port to bind and listen to char *localip; unsigned int localport; // Remote host and port to connect to char *remotehost; // Resolved by the remote server unsigned int remoteport; // Username and password of remote user char *username; char *password; // May be NULL or "" // Remote IP and (host, port) in remote network char *sshserverip; unsigned int sshserverport; // SSH port void *context; // pointer to user-defined data void (*logcallback)(struct rbm_ssh_session* session, char *message, int level); } rbm_ssh_tunnel_config; typedef struct rbm_ssh_channel { struct rbm_ssh_session* session; LIBSSH2_CHANNEL *channel; rbm_socket_t socket; char *inbuf; char *outbuf; int bufmaxsize; } rbm_ssh_channel; typedef struct rbm_ssh_session { rbm_socket_t localsocket; rbm_socket_t sshsocket; LIBSSH2_SESSION *sshsession; rbm_ssh_tunnel_config *config; rbm_ssh_channel **channels; int channelssize; // number of channels char lasterror[2048]; } rbm_ssh_session; int rbm_ssh_init(); void rbm_ssh_cleanup(); rbm_ssh_session* rbm_ssh_session_create(struct rbm_ssh_tunnel_config *config); int rbm_ssh_session_setup(rbm_ssh_session *session); void rbm_ssh_session_close(rbm_ssh_session *session); int rbm_ssh_open_tunnel(struct rbm_ssh_session *connection); #ifdef __cplusplus } #endif #endif // ROBOMONGO_SSH_H ================================================ FILE: src/robomongo/ssh/temp/temp_ssh_log.c ================================================ #include "robomongo/ssh/internal.h" #include #include #include int log_error(const char *format, ...) { int errsave = errno; const int buf_size = 1024; char buf[buf_size]; va_list args; va_start(args, format); vsnprintf(buf, buf_size, format, args); if (errsave) { fprintf(stderr, "Error (%d): %s. %s\n", errno, strerror(errsave), buf); } else { fprintf(stderr, "Error: %s\n", buf); } va_end(args); return 1; } int log_msg(const char *format, ...) { const int buf_size = 1024; char buf[buf_size]; va_list args; va_start(args, format); vsnprintf(buf, buf_size, format, args); printf("%s\n", buf); va_end(args); return 1; } /* * errsave: use 0, if you are not logging errors (i.e. errno == 0). */ void ssh_log_v(rbm_ssh_session* session, enum rbm_ssh_log_type type, const char *format, va_list args, int errsave) { const size_t bufsize = 2000; char buf[bufsize]; vsnprintf(buf, bufsize, format, args); if (type == RBM_SSH_LOG_TYPE_ERROR || type == RBM_SSH_LOG_TYPE_WARN) { if (errsave) { sprintf(session->lasterror, "%s. %s. (Error #%d)", strerror(errsave), buf, errsave); } else { sprintf(session->lasterror, "%s", buf); } fprintf(stderr, "%s\n", session->lasterror); session->config->logcallback(session, session->lasterror, type); return; } if (type != RBM_SSH_LOG_TYPE_INFO && type != RBM_SSH_LOG_TYPE_DEBUG) return; if (type > session->config->loglevel) return; printf("%s\n", buf); session->config->logcallback(session, buf, type); } void ssh_log_msg(rbm_ssh_session* session, const char *format, ...) { const int type = RBM_SSH_LOG_TYPE_INFO; // For performance reasons, return as quick as possible, // if this level of logging is not enabled if (type > session->config->loglevel) return; va_list args; va_start(args, format); ssh_log_v(session, type, format, args, 0); va_end(args); } // When you faced with an error that you are planning to overcome or handle, // log it as a warning. If you do not have plan how to proceed further, log // as an error. void ssh_log_warn(rbm_ssh_session* session, const char *format, ...) { int errsave = errno; const int type = RBM_SSH_LOG_TYPE_WARN; // For performance reasons, return as quick as possible, // if this level of logging is not enabled if (type > session->config->loglevel) return; va_list args; va_start(args, format); ssh_log_v(session, type, format, args, errsave); va_end(args); } void ssh_log_debug(rbm_ssh_session* session, const char *format, ...) { const int type = RBM_SSH_LOG_TYPE_DEBUG; // For performance reasons, return as quick as possible, // if this level of logging is not enabled if (type > session->config->loglevel) return; va_list args; va_start(args, format); ssh_log_v(session, type, format, args, 0); va_end(args); } void ssh_log_error(rbm_ssh_session* session, const char *format, ...) { int errsave = errno; va_list args; va_start(args, format); ssh_log_v(session, RBM_SSH_LOG_TYPE_ERROR, format, args, errsave); va_end(args); } ================================================ FILE: src/robomongo/ssh/test.c ================================================ #include #include #include #include "robomongo/ssh/private.h" static int *elem1; static int *elem2; static int *elem3; static int *elem4; static int *elem5; int add_one_element() { int **array = NULL; int size = 0; rbm_array_add((void***)&array, &size, elem1); assert(size == 1); assert(array[0] == elem1); assert(*array[0] == 100); free(array); return 0; } int add_two_elements() { int **array = NULL; int size = 0; rbm_array_add((void***)&array, &size, elem1); assert(size == 1); rbm_array_add((void***)&array, &size, elem2); assert(size == 2); assert(array[0] == elem1); assert(*array[0] == 100); assert(array[1] == elem2); assert(*array[1] == 200); free(array); return 0; } int array_remove_when_only_single() { int **array = NULL; int size = 0; rbm_array_add((void***)&array, &size, elem1); rbm_array_remove((void***)&array, &size, elem1); assert(size == 0); assert(*elem1 == 100); free(array); return 0; } int array_remove_when_only_two() { int **array = NULL; int size = 0; rbm_array_add((void***)&array, &size, elem1); rbm_array_add((void***)&array, &size, elem2); rbm_array_remove((void***)&array, &size, elem1); assert(size == 1); assert(array[0] == elem2); assert(*array[0] == 200); free(array); return 0; } int array_remove_when_only_two_variation() { int **array = NULL; int size = 0; rbm_array_add((void***)&array, &size, elem1); rbm_array_add((void***)&array, &size, elem2); rbm_array_remove((void***)&array, &size, elem2); assert(size == 1); assert(array[0] == elem1); assert(*array[0] == 100); free(array); return 0; } int array_remove_when_five_elements() { int **array = NULL; int size = 0; rbm_array_add((void***)&array, &size, elem1); rbm_array_add((void***)&array, &size, elem2); rbm_array_add((void***)&array, &size, elem3); rbm_array_add((void***)&array, &size, elem4); rbm_array_add((void***)&array, &size, elem5); assert(size == 5); rbm_array_remove((void***)&array, &size, elem2); rbm_array_remove((void***)&array, &size, elem4); assert(size == 3); assert(array[0] == elem1); assert(array[1] == elem3); assert(array[2] == elem5); free(array); return 0; } int remove_last_element() { int **array = NULL; int size = 0; rbm_array_add((void***)&array, &size, elem1); rbm_array_add((void***)&array, &size, elem2); rbm_array_add((void***)&array, &size, elem3); assert(size == 3); rbm_array_remove((void***)&array, &size, elem3); assert(size == 2); assert(array[0] == elem1); assert(array[1] == elem2); free(array); return 0; } int add_with_incorrect_params() { int **array = NULL; int size = 1; int rc = rbm_array_add((void***)&array, &size, elem1); assert(rc == -1); assert(size == 1); // should not be modified free(array); return 0; } void init() { elem1 = malloc(sizeof(int)); elem2 = malloc(sizeof(int)); elem3 = malloc(sizeof(int)); elem4 = malloc(sizeof(int)); elem5 = malloc(sizeof(int)); *elem1 = 100; *elem2 = 200; *elem3 = 300; *elem4 = 400; *elem5 = 500; } void cleanup() { free(elem1); free(elem2); free(elem3); free(elem4); free(elem5); } int main(int argc, char *argv[]) { init(); add_one_element(); add_two_elements(); array_remove_when_only_two(); array_remove_when_only_single(); array_remove_when_only_two_variation(); array_remove_when_five_elements(); remove_last_element(); add_with_incorrect_params(); cleanup(); printf("All tests completed successfully.\n"); } ================================================ FILE: src/robomongo/ssh/unix.h ================================================ #define rbm_socket_t int #define rbm_socket_invalid (-1) ================================================ FILE: src/robomongo/ssh/valgrind/macosx-clang.supp ================================================ { Memcheck:Leak match-leak-kinds: possible fun:malloc_zone_malloc fun:_objc_copyClassNamesForImage fun:_ZL9protocolsv fun:_Z9readClassP10objc_classbb fun:gc_init fun:_ZL33objc_initializeClassPair_internalP10objc_classPKcS0_S0_ fun:layout_string_create fun:_ZL12realizeClassP10objc_class fun:_ZL22copySwiftV1MangledNamePKcb fun:_ZL22copySwiftV1MangledNamePKcb fun:_ZL22copySwiftV1MangledNamePKcb fun:_ZL22copySwiftV1MangledNamePKcb } { insert_a_suppression_name_here_2 Memcheck:Param socketcall.sendto(msg) fun:sendto ... fun:_libssh2_send fun:_libssh2_transport_send } { Memcheck:Cond fun:BN_bin2bn ... } { Memcheck:Cond fun:BN_num_bits_word ... } { Memcheck:Value8 fun:BN_num_bits_word ... } { Memcheck:Cond fun:BN_mod_exp_mont ... } { Memcheck:Cond fun:BN_mod_exp_mont_word ... } { Memcheck:Cond fun:_platform_memcmp ... fun:int_rsa_verify ... fun:RSA_verify } { Memcheck:Cond fun:aesni_ctr32_encrypt_blocks fun:aes_ctr_cipher } { Memcheck:Cond fun:CRYPTO_ctr128_encrypt_ctr32 } { Memcheck:Cond fun:_libssh2_transport_read } { Memcheck:Cond fun:malloc fun:libssh2_default_alloc fun:_libssh2_transport_read fun:_libssh2_packet_require } { Memcheck:Cond fun:decrypt fun:_libssh2_transport_read fun:_libssh2_packet_require } { Memcheck:Cond fun:__memcpy_chk fun:_libssh2_transport_read fun:_libssh2_packet_require } { Memcheck:Cond fun:_platform_memmove$VARIANT$Haswell fun:__memcpy_chk fun:_libssh2_transport_read fun:_libssh2_packet_require } { Memcheck:Value8 fun:_platform_memmove$VARIANT$Haswell fun:__memcpy_chk fun:_libssh2_transport_read fun:_libssh2_packet_require } { Memcheck:Cond ... fun:mac_method_hmac_sha2_256_hash } { Memcheck:Value8 ... fun:mac_method_hmac_sha2_256_hash } { Memcheck:Cond fun:_platform_memcmp fun:fullpacket fun:_libssh2_transport_read fun:_libssh2_packet_require } { Memcheck:Value8 fun:_platform_memcmp fun:fullpacket fun:_libssh2_transport_read fun:_libssh2_packet_require } { Memcheck:Cond fun:_libssh2_packet_add fun:fullpacket fun:_libssh2_transport_read fun:_libssh2_packet_require } { Memcheck:Cond fun:_libssh2_packet_require fun:session_startup } { Memcheck:Cond fun:_libssh2_packet_ask fun:_libssh2_packet_require } { Memcheck:Cond fun:session_startup fun:libssh2_session_handshake } { Memcheck:Cond fun:_platform_strncmp fun:session_startup fun:libssh2_session_handshake } { Memcheck:Param recvfrom(buf) fun:recvfrom fun:_libssh2_recv fun:_libssh2_transport_read fun:_libssh2_packet_requirev } { Memcheck:Param recvfrom(len) fun:recvfrom fun:_libssh2_recv fun:_libssh2_transport_read fun:_libssh2_packet_requirev } { Memcheck:Cond fun:malloc fun:libssh2_default_alloc fun:_libssh2_transport_read fun:_libssh2_packet_requirev } { Memcheck:Cond fun:decrypt fun:_libssh2_transport_read fun:_libssh2_packet_requirev } { Memcheck:Cond fun:__memcpy_chk fun:_libssh2_transport_read fun:_libssh2_packet_requirev } { Memcheck:Cond fun:_platform_memmove$VARIANT$Haswell fun:__memcpy_chk fun:_libssh2_transport_read fun:_libssh2_packet_requirev } { Memcheck:Value8 fun:_platform_memmove$VARIANT$Haswell fun:__memcpy_chk fun:_libssh2_transport_read fun:_libssh2_packet_requirev } { Memcheck:Cond fun:_platform_memcmp ... fun:_libssh2_transport_read fun:_libssh2_packet_requirev } { Memcheck:Value8 fun:_platform_memcmp ... fun:_libssh2_transport_read fun:_libssh2_packet_requirev } { Memcheck:Cond ... fun:_libssh2_transport_read fun:_libssh2_packet_requirev } { Memcheck:Cond ... fun:_libssh2_packet_requirev } { Memcheck:Cond ... fun:libssh2_userauth_list } { Memcheck:Value8 ... fun:libssh2_userauth_list } { name Memcheck:Cond obj:/usr/local/Cellar/openssl/1.0.2f/lib/libcrypto.1.0.0.dylib } { Memcheck:Cond fun:strlen fun:__vfprintf fun:__v2printf fun:_vsnprintf fun:vsnprintf fun:ssh_log_v fun:ssh_log_debug fun:ssh_connect fun:rbm_ssh_setup fun:rbm_ssh_session_setup fun:main } { Memcheck:Cond fun:strlen fun:__vfprintf fun:__v2printf fun:vfprintf_l fun:printf fun:ssh_log_v fun:ssh_log_debug fun:ssh_connect fun:rbm_ssh_setup fun:rbm_ssh_session_setup fun:main } { Memcheck:Cond ... fun:strstr fun:ssh_connect fun:rbm_ssh_setup fun:rbm_ssh_session_setup fun:main } { Memcheck:Value8 fun:sha256_block_data_order_avx2 obj:* } { Memcheck:Cond fun:_libssh2_userauth_publickey } { Memcheck:Value8 fun:bn_mul4x_mont obj:* } { Memcheck:Value8 fun:bn_mul4x_mont } { Memcheck:Value8 fun:sqr8x_reduction } { Memcheck:Value8 fun:BN_from_montgomery_word } { Memcheck:Value8 fun:bn_mul_recursive } { Memcheck:Param write(buf) fun:write$NOCANCEL fun:_swrite fun:__sflush fun:fflush fun:_ZNSt3__111__stdoutbufIcE4syncEv fun:_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv fun:_ZNSt3__18ios_base4InitD2Ev fun:__cxa_finalize_ranges fun:exit fun:start } ================================================ FILE: src/robomongo/ssh/win.c ================================================ int ssh_win_init() { WSADATA wsadata; err = WSAStartup(MAKEWORD(2,0), &wsadata); if (err != 0) { log_error("WSAStartup failed with error: %d", err); return 1; } } ================================================ FILE: src/robomongo/ssh/win.h ================================================ #define rbm_socket_t SOCKET #define rbm_socket_invalid INVALID_SOCKET ================================================ FILE: src/robomongo/utils/RoboCrypt.cpp ================================================ #include "RoboCrypt.h" #include "robomongo/core/utils/Logger.h" #include #include #include #include #include #include #include #include namespace Robomongo { long long RoboCrypt::_KEY = 0; std::vector RoboCrypt::_roboCryptLogs; void RoboCrypt::initKey() { using MongoSeverity = mongo::logger::LogSeverity; auto addToRoboCryptLogs = [](std::string msg, MongoSeverity severity) { _roboCryptLogs.push_back({ msg, severity }); }; const auto KEY_FILE = QString("%1/.3T/robo-3t/robo3t.key").arg(QDir::homePath()).toStdString(); QString fileContent; QFileInfo const fileInfo{ QString::fromStdString(KEY_FILE) }; if (fileInfo.exists() && fileInfo.isFile()) { // a) Read existing key from file QFile keyFile{ QString::fromStdString(KEY_FILE) }; if (!keyFile.open(QIODevice::ReadOnly)) addToRoboCryptLogs("RoboCrypt: Failed to open key file: " + KEY_FILE, MongoSeverity::Error()); QTextStream in{ &keyFile }; fileContent = in.readAll(); if(fileContent.isEmpty()) addToRoboCryptLogs("RoboCrypt: Key file is empty: " + KEY_FILE, MongoSeverity::Error()); _KEY = fileContent.toLongLong(); } else { // b) Generate a new key and save it into file addToRoboCryptLogs("RoboCrypt: No key found, generating a new key and saving it into file", MongoSeverity::Warning()); // Generate a new key std::random_device randomDevice; std::mt19937_64 engine{ randomDevice() }; std::uniform_int_distribution dist{ std::llround(std::pow(2,61)), std::llround(std::pow(2,62)) }; _KEY = dist(engine); // Save the key into file QFile file{ QString::fromStdString(KEY_FILE) }; if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) addToRoboCryptLogs("RoboCrypt: Failed to save the key into file: " + KEY_FILE, MongoSeverity::Error()); QTextStream out(&file); out << QString::number(_KEY); } } } ================================================ FILE: src/robomongo/utils/RoboCrypt.h ================================================ #include "SimpleCrypt.h" #include #include namespace Robomongo { class RoboCrypt { using LogAndSeverity = std::pair; public: static SimpleCrypt& simpleCrypter() { static SimpleCrypt simpleCrypt(_KEY); return simpleCrypt; } static std::string encrypt(std::string passwd) { return simpleCrypter().encryptToString(QString::fromStdString(passwd)).toStdString(); } static std::string decrypt(std::string cryptedPasswd) { return simpleCrypter().decryptToString(QString::fromStdString(cryptedPasswd)).toStdString(); } // Read key from key file otherwise create a new key and save it into file static void initKey(); static std::vector const& roboCryptLogs() { return _roboCryptLogs; } private: static long long _KEY; static std::vector _roboCryptLogs; }; } ================================================ FILE: src/robomongo/utils/RoboCrypt_test.cpp ================================================ #include "gtest/gtest.h" #include "robomongo/utils/RoboCrypt.h" #include /* Example Test: * * TEST( [Test_Case_Name], [Test_Name] ) * TEST( [Test_Case_Name], [UnitOfWorkName_ScenarioUnderTest_ExpectedBehavior] ) * TEST( StringParserTests, NumberLeftOf_StringWithoutNumber_ReturnsFalse) { // ... } */ TEST(RoboCrypt_CoreTests, encrypt_decrypt) { auto const pwds = { "Tyu_aBq", "_?asdfghjkl;'piop[.,/", ".?/`_@~!#$%^^&&*)_)_+=-", "<>?/.,;':][p{}|\"" }; for (auto const& pwd : pwds) { const std::string encryptedPwd = Robomongo::RoboCrypt::encrypt(pwd); const std::string decryptedPwd = Robomongo::RoboCrypt::decrypt(encryptedPwd); EXPECT_EQ(pwd, decryptedPwd); } } ================================================ FILE: src/robomongo/utils/SimpleCrypt.cpp ================================================ /* Copyright (c) 2011, Andre Somers 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 the Rathenau Instituut, Andre Somers 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 ANDRE SOMERS 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 #######; 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 "SimpleCrypt.h" #include #include #include #include #include #include SimpleCrypt::SimpleCrypt() : m_key(0), m_compressionMode(CompressionAuto), m_protectionMode(ProtectionChecksum), m_lastError(ErrorNoError) { qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); } SimpleCrypt::SimpleCrypt(quint64 key) : m_key(key), m_compressionMode(CompressionAuto), m_protectionMode(ProtectionChecksum), m_lastError(ErrorNoError) { qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); splitKey(); } void SimpleCrypt::setKey(quint64 key) { m_key = key; splitKey(); } void SimpleCrypt::splitKey() { m_keyParts.clear(); m_keyParts.resize(8); for (int i = 0; i<8; i++) { quint64 part = m_key; for (int j = i; j>0; j--) part = part >> 8; part = part & 0xff; m_keyParts[i] = static_cast(part); } } QByteArray SimpleCrypt::encryptToByteArray(const QString& plaintext) { QByteArray plaintextArray = plaintext.toUtf8(); return encryptToByteArray(plaintextArray); } QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) { if (m_keyParts.isEmpty()) { qWarning() << "No key set."; m_lastError = ErrorNoKeySet; return QByteArray(); } QByteArray ba = plaintext; CryptoFlags flags = CryptoFlagNone; if (m_compressionMode == CompressionAlways) { ba = qCompress(ba, 9); //maximum compression flags |= CryptoFlagCompression; } else if (m_compressionMode == CompressionAuto) { QByteArray compressed = qCompress(ba, 9); if (compressed.count() < ba.count()) { ba = compressed; flags |= CryptoFlagCompression; } } QByteArray integrityProtection; if (m_protectionMode == ProtectionChecksum) { flags |= CryptoFlagChecksum; QDataStream s(&integrityProtection, QIODevice::WriteOnly); s << qChecksum(ba.constData(), ba.length()); } else if (m_protectionMode == ProtectionHash) { flags |= CryptoFlagHash; QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(ba); integrityProtection += hash.result(); } //prepend a random char to the string char randomChar = char(qrand() & 0xFF); ba = randomChar + integrityProtection + ba; int pos(0); char lastChar(0); int cnt = ba.count(); while (pos < cnt) { ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; lastChar = ba.at(pos); ++pos; } QByteArray resultArray; resultArray.append(char(0x03)); //version for future updates to algorithm resultArray.append(char(flags)); //encryption flags resultArray.append(ba); m_lastError = ErrorNoError; return resultArray; } QString SimpleCrypt::encryptToString(const QString& plaintext) { QByteArray plaintextArray = plaintext.toUtf8(); QByteArray cypher = encryptToByteArray(plaintextArray); QString cypherString = QString::fromLatin1(cypher.toBase64()); return cypherString; } QString SimpleCrypt::encryptToString(QByteArray plaintext) { QByteArray cypher = encryptToByteArray(plaintext); QString cypherString = QString::fromLatin1(cypher.toBase64()); return cypherString; } QString SimpleCrypt::decryptToString(const QString &cyphertext) { QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); QByteArray plaintextArray = decryptToByteArray(cyphertextArray); QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size()); return plaintext; } QString SimpleCrypt::decryptToString(QByteArray cypher) { QByteArray ba = decryptToByteArray(cypher); QString plaintext = QString::fromUtf8(ba, ba.size()); return plaintext; } QByteArray SimpleCrypt::decryptToByteArray(const QString& cyphertext) { QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); QByteArray ba = decryptToByteArray(cyphertextArray); return ba; } QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) { if (m_keyParts.isEmpty()) { qWarning() << "No key set."; m_lastError = ErrorNoKeySet; return QByteArray(); } QByteArray ba = cypher; if (cypher.count() < 3) return QByteArray(); char version = ba.at(0); if (version != 3) { //we only work with version 3 m_lastError = ErrorUnknownVersion; qWarning() << "Invalid version or not a cyphertext."; return QByteArray(); } CryptoFlags flags = CryptoFlags(ba.at(1)); ba = ba.mid(2); int pos(0); int cnt(ba.count()); char lastChar = 0; while (pos < cnt) { char currentChar = ba[pos]; ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8); lastChar = currentChar; ++pos; } ba = ba.mid(1); //chop off the random number at the start bool integrityOk(true); if (flags.testFlag(CryptoFlagChecksum)) { if (ba.length() < 2) { m_lastError = ErrorIntegrityFailed; return QByteArray(); } quint16 storedChecksum; { QDataStream s(&ba, QIODevice::ReadOnly); s >> storedChecksum; } ba = ba.mid(2); quint16 checksum = qChecksum(ba.constData(), ba.length()); integrityOk = (checksum == storedChecksum); } else if (flags.testFlag(CryptoFlagHash)) { if (ba.length() < 20) { m_lastError = ErrorIntegrityFailed; return QByteArray(); } QByteArray storedHash = ba.left(20); ba = ba.mid(20); QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(ba); integrityOk = (hash.result() == storedHash); } if (!integrityOk) { m_lastError = ErrorIntegrityFailed; return QByteArray(); } if (flags.testFlag(CryptoFlagCompression)) ba = qUncompress(ba); m_lastError = ErrorNoError; return ba; } ================================================ FILE: src/robomongo/utils/SimpleCrypt.h ================================================ /* Copyright (c) 2011, Andre Somers 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 the Rathenau Instituut, Andre Somers 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 ANDRE SOMERS 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 #######; 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. */ #ifndef SIMPLECRYPT_H #define SIMPLECRYPT_H #include #include #include /** @short Simple encryption and decryption of strings and byte arrays This class provides a simple implementation of encryption and decryption of strings and byte arrays. @warning The encryption provided by this class is NOT strong encryption. It may help to shield things from curious eyes, but it will NOT stand up to someone determined to break the encryption. Don't say you were not warned. The class uses a 64 bit key. Simply create an instance of the class, set the key, and use the encryptToString() method to calculate an encrypted version of the input string. To decrypt that string again, use an instance of SimpleCrypt initialized with the same key, and call the decryptToString() method with the encrypted string. If the key matches, the decrypted version of the string will be returned again. If you do not provide a key, or if something else is wrong, the encryption and decryption function will return an empty string or will return a string containing nonsense. lastError() will return a value indicating if the method was succesful, and if not, why not. SimpleCrypt is prepared for the case that the encryption and decryption algorithm is changed in a later version, by prepending a version identifier to the cypertext. */ class SimpleCrypt { public: /** CompressionMode describes if compression will be applied to the data to be encrypted. */ enum CompressionMode { CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */ CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */ CompressionNever /*!< Never apply compression. */ }; /** IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data or wrong decryption keys. Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This increases the length of the resulting cypertext, but makes it possible to check if the plaintext appears to be valid after decryption. */ enum IntegrityProtectionMode { ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */ ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */ ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */ }; /** Error describes the type of error that occured. */ enum Error { ErrorNoError, /*!< No error occurred. */ ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */ ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */ ErrorIntegrityFailed, /*!< The integrity check of the data failed. Perhaps the wrong key was used. */ }; /** Constructor. Constructs a SimpleCrypt instance without a valid key set on it. */ SimpleCrypt(); /** Constructor. Constructs a SimpleCrypt instance and initializes it with the given @arg key. */ explicit SimpleCrypt(quint64 key); /* Robo 1.3 */ void encrypt(std::string passwd); /** (Re-) initializes the key with the given @arg key. */ void setKey(quint64 key); /** Returns true if SimpleCrypt has been initialized with a key. */ bool hasKey() const { return !m_keyParts.isEmpty(); } /** Sets the compression mode to use when encrypting data. The default mode is Auto. Note that decryption is not influenced by this mode, as the decryption recognizes what mode was used when encrypting. */ void setCompressionMode(CompressionMode mode) { m_compressionMode = mode; } /** Returns the CompressionMode that is currently in use. */ CompressionMode compressionMode() const { return m_compressionMode; } /** Sets the integrity mode to use when encrypting data. The default mode is Checksum. Note that decryption is not influenced by this mode, as the decryption recognizes what mode was used when encrypting. */ void setIntegrityProtectionMode(IntegrityProtectionMode mode) { m_protectionMode = mode; } /** Returns the IntegrityProtectionMode that is currently in use. */ IntegrityProtectionMode integrityProtectionMode() const { return m_protectionMode; } /** Returns the last error that occurred. */ Error lastError() const { return m_lastError; } /** Encrypts the @arg plaintext string with the key the class was initialized with, and returns a cyphertext the result. The result is a base64 encoded version of the binary array that is the actual result of the string, so it can be stored easily in a text format. */ QString encryptToString(const QString& plaintext); /** Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns a cyphertext the result. The result is a base64 encoded version of the binary array that is the actual result of the encryption, so it can be stored easily in a text format. */ QString encryptToString(QByteArray plaintext); /** Encrypts the @arg plaintext string with the key the class was initialized with, and returns a binary cyphertext in a QByteArray the result. This method returns a byte array, that is useable for storing a binary format. If you need a string you can store in a text file, use encryptToString() instead. */ QByteArray encryptToByteArray(const QString& plaintext); /** Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns a binary cyphertext in a QByteArray the result. This method returns a byte array, that is useable for storing a binary format. If you need a string you can store in a text file, use encryptToString() instead. */ QByteArray encryptToByteArray(QByteArray plaintext); /** Decrypts a cyphertext string encrypted with this class with the set key back to the plain text version. If an error occured, such as non-matching keys between encryption and decryption, an empty string or a string containing nonsense may be returned. */ QString decryptToString(const QString& cyphertext); /** Decrypts a cyphertext string encrypted with this class with the set key back to the plain text version. If an error occured, such as non-matching keys between encryption and decryption, an empty string or a string containing nonsense may be returned. */ QByteArray decryptToByteArray(const QString& cyphertext); /** Decrypts a cyphertext binary encrypted with this class with the set key back to the plain text version. If an error occured, such as non-matching keys between encryption and decryption, an empty string or a string containing nonsense may be returned. */ QString decryptToString(QByteArray cypher); /** Decrypts a cyphertext binary encrypted with this class with the set key back to the plain text version. If an error occured, such as non-matching keys between encryption and decryption, an empty string or a string containing nonsense may be returned. */ QByteArray decryptToByteArray(QByteArray cypher); //enum to describe options that have been used for the encryption. Currently only one, but //that only leaves room for future extensions like adding a cryptographic hash... enum CryptoFlag { CryptoFlagNone = 0, CryptoFlagCompression = 0x01, CryptoFlagChecksum = 0x02, CryptoFlagHash = 0x04 }; Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag); private: void splitKey(); quint64 m_key; QVector m_keyParts; CompressionMode m_compressionMode; IntegrityProtectionMode m_protectionMode; Error m_lastError; }; Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags) #endif // SimpleCrypt_H ================================================ FILE: src/robomongo/utils/StringOperations.cpp ================================================ #include "robomongo/utils/StringOperations.h" namespace Robomongo { std::string captilizeFirstChar(std::string str) { if (!str.empty()) str[0] = static_cast(toupper(str[0])); return str; } } // end of name space Robomongo ================================================ FILE: src/robomongo/utils/StringOperations.h ================================================ #pragma once #include namespace Robomongo { // Capitalize first char (Mongo errors often come all lower case) std::string captilizeFirstChar(std::string str); } ================================================ FILE: src/robomongo/utils/StringOperations_test.cpp ================================================ #include "gtest/gtest.h" #include "robomongo/utils/StringOperations.h" #include /* Example Test: * * TEST( [Test_Case_Name], [Test_Name] ) * TEST( [Test_Case_Name], [UnitOfWorkName_ScenarioUnderTest_ExpectedBehavior] ) * TEST( StringParserTests, NumberLeftOf_StringWithoutNumber_ReturnsFalse) { // ... } */ TEST(StringOperationsTests, captilizeFirstChar) { // EXPECT_EQ("Abcc", Robomongo::captilizeFirstChar("abc")); // Simulating failing test EXPECT_EQ("Abc", Robomongo::captilizeFirstChar("abc")); // Simulating passing test } ================================================ FILE: src/robomongo/utils/common.cpp ================================================ #include "robomongo/utils/common.h" #include #include #include "robomongo/core/events/MongoEvents.h" #include "robomongo/core/utils/Logger.h" #include "robomongo/core/EventBus.h" #include "robomongo/utils/StringOperations.h" namespace Robomongo { void genericEventErrorHandler(Event *event, const std::string &userFriendlyMessage, EventBus* bus, QObject* sender) { if (!event->isError()) return; std::string errMsg = captilizeFirstChar(event->error().errorMessage()); LOG_MSG(userFriendlyMessage + " " + errMsg, mongo::logger::LogSeverity::Error()); if (bus && sender) bus->publish(new OperationFailedEvent(sender, errMsg, userFriendlyMessage)); else LOG_MSG("Failed to publish OperationFailedEvent.", mongo::logger::LogSeverity::Error()); } bool fileExists(const QString &filePath) { QFileInfo fileInfo(filePath); return fileInfo.exists() && fileInfo.isFile(); } template bool vectorContains(std::vector const& vec, T const& value) { return find(vec.cbegin(), vec.cend(), value) != vec.cend(); } QVariant getSetting(QString const& key) { return QSettings("3T", "Robomongo").value(key).toSize(); } void saveSetting(QString const& key, QVariant const& value) { QSettings("3T", "Robomongo").setValue(key, value); } } // end of name space Robomongo ================================================ FILE: src/robomongo/utils/common.h ================================================ #pragma once // todo: rename to utils.h #include #include namespace Robomongo /* todo ::utils */ { class Event; class EventBus; // Special handler designed to be used in MongoDatanase and MongoServer classes and only for // event->isError() is true case. void genericEventErrorHandler( Event *event, const std::string &userFriendlyMessage, EventBus* bus, QObject* sender); bool fileExists(const QString& filePath); template bool vectorContains(std::vector const& vec, T const& value); QVariant getSetting(QString const& key); void saveSetting(QString const& key, QVariant const& value); } ================================================ FILE: src/robomongo/utils/qzip/qconfig_p.h ================================================ #define QT_FEATURE_alloca_h -1 #define QT_FEATURE_alloca_malloc_h 1 #define QT_FEATURE_alloca 1 #define QT_FEATURE_android_style_assets -1 #define QT_FEATURE_sse2 1 #define QT_FEATURE_private_tests -1 #define QT_FEATURE_dbus 1 #define QT_FEATURE_dbus_linked -1 #define QT_FEATURE_gui 1 #define QT_FEATURE_libudev -1 #define QT_FEATURE_network 1 #define QT_FEATURE_posix_fallocate -1 #define QT_FEATURE_qml_debug 1 #define QT_FEATURE_reduce_exports -1 #define QT_FEATURE_reduce_relocations -1 #define QT_FEATURE_release_tools -1 #define QT_FEATURE_sql 1 #define QT_FEATURE_stack_protector_strong -1 #define QT_FEATURE_system_zlib -1 #define QT_FEATURE_testlib 1 #define QT_FEATURE_widgets 1 #define QT_FEATURE_xml 1 ================================================ FILE: src/robomongo/utils/qzip/qglobal_p.h ================================================ /**************************************************************************** ** ** Copyright (C) 2015 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** 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 QGLOBAL_P_H #define QGLOBAL_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "qglobal.h" #include "qglobal_p.h" // include self to avoid syncqt warning - no-op #ifndef QT_BOOTSTRAPPED #include "qconfig_p.h" #include "qtcore-config_p.h" #endif #endif // QGLOBAL_P_H ================================================ FILE: src/robomongo/utils/qzip/qtcore-config_p.h ================================================ #define QT_FEATURE_clock_gettime -1 #define QT_FEATURE_datetimeparser 1 #define QT_FEATURE_dlopen -1 #define QT_FEATURE_doubleconversion 1 #define QT_FEATURE_glib -1 #define QT_FEATURE_gnu_libiconv -1 #define QT_FEATURE_icu -1 #define QT_FEATURE_posix_libiconv -1 #define QT_FEATURE_iconv -1 #define QT_FEATURE_inotify -1 #define QT_FEATURE_journald -1 #define QT_FEATURE_sha3_fast 1 #define QT_FEATURE_slog2 -1 #define QT_FEATURE_syslog -1 #define QT_FEATURE_system_doubleconversion -1 #define QT_FEATURE_system_pcre2 -1 ================================================ FILE: src/robomongo/utils/qzip/qtgui-config_p.h ================================================ #define QT_FEATURE_xcb -1 #define QT_FEATURE_accessibility_atspi_bridge -1 #define QT_FEATURE_angle_d3d11_qdtd 1 #define QT_FEATURE_direct2d 1 #define QT_FEATURE_directfb -1 #define QT_FEATURE_directwrite 1 #define QT_FEATURE_directwrite2 1 #define QT_FEATURE_egl 1 #define QT_FEATURE_egl_x11 -1 #define QT_FEATURE_eglfs -1 #define QT_FEATURE_eglfs_brcm -1 #define QT_FEATURE_eglfs_egldevice -1 #define QT_FEATURE_eglfs_gbm -1 #define QT_FEATURE_eglfs_mali -1 #define QT_FEATURE_eglfs_viv -1 #define QT_FEATURE_eglfs_viv_wl -1 #define QT_FEATURE_evdev -1 #define QT_FEATURE_fontconfig -1 #define QT_FEATURE_freetype 1 #define QT_FEATURE_gif 1 #define QT_FEATURE_harfbuzz 1 #define QT_FEATURE_ico 1 #define QT_FEATURE_integrityfb -1 #define QT_FEATURE_integrityhid -1 #define QT_FEATURE_jpeg 1 #define QT_FEATURE_kms -1 #define QT_FEATURE_libinput -1 #define QT_FEATURE_libinput_axis_api -1 #define QT_FEATURE_linuxfb -1 #define QT_FEATURE_mirclient -1 #define QT_FEATURE_mtdev -1 #define QT_FEATURE_multiprocess 1 #define QT_FEATURE_png 1 #define QT_FEATURE_system_freetype -1 #define QT_FEATURE_system_harfbuzz -1 #define QT_FEATURE_system_jpeg -1 #define QT_FEATURE_system_png -1 #define QT_FEATURE_system_xcb -1 #define QT_FEATURE_tslib -1 #define QT_FEATURE_vnc -1 #define QT_FEATURE_xkbcommon_evdev -1 #define QT_FEATURE_xlib -1 ================================================ FILE: src/robomongo/utils/qzip/qtguiglobal_p.h ================================================ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** 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 QTGUIGLOBAL_P_H #define QTGUIGLOBAL_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include "qglobal_p.h" #include "qtgui-config_p.h" #endif // QTGUIGLOBAL_P_H ================================================ FILE: src/robomongo/utils/qzip/qzipreader_p.h ================================================ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** 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 QZIPREADER_H #define QZIPREADER_H #include "qtguiglobal_p.h" #include #ifndef QT_NO_TEXTODFWRITER // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists for the convenience // of the QZipReader class. This header file may change from // version to version without notice, or even be removed. // // We mean it. // #include #include #include QT_BEGIN_NAMESPACE class QZipReaderPrivate; class Q_GUI_EXPORT QZipReader { public: explicit QZipReader(const QString &fileName, QIODevice::OpenMode mode = QIODevice::ReadOnly ); explicit QZipReader(QIODevice *device); ~QZipReader(); QIODevice* device() const; bool isReadable() const; bool exists() const; struct FileInfo { FileInfo() Q_DECL_NOTHROW : isDir(false), isFile(false), isSymLink(false), crc(0), size(0) {} bool isValid() const Q_DECL_NOTHROW { return isDir || isFile || isSymLink; } QString filePath; uint isDir : 1; uint isFile : 1; uint isSymLink : 1; QFile::Permissions permissions; uint crc; qint64 size; QDateTime lastModified; }; QVector fileInfoList() const; int count() const; FileInfo entryInfoAt(int index) const; QByteArray fileData(const QString &fileName) const; bool extractAll(const QString &destinationDir) const; enum Status { NoError, FileReadError, FileOpenError, FilePermissionsError, FileError }; Status status() const; void close(); private: QZipReaderPrivate *d; Q_DISABLE_COPY(QZipReader) }; Q_DECLARE_TYPEINFO(QZipReader::FileInfo, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QZipReader::Status, Q_PRIMITIVE_TYPE); QT_END_NAMESPACE #endif // QT_NO_TEXTODFWRITER #endif // QZIPREADER_H ================================================ FILE: src/robomongo-unit-tests/CMakeLists.txt ================================================ ######################################### ### Unit Testing with Google Test ### ######################################### ### --- Important Note if (SYSTEM_LINUX) message("\n *Note: Currently unit testing is disabled for Linux " "due to MongoDB linking problems") return() endif() ### --- Enable and setup gtest enable_testing() include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) ### --- Setup test source files # New test & source file pairs MUST have the following format: # Test file: /path/Foo_test.cpp # Source file: /path/Foo.cpp or /path/Foo.h set(ROBO_SRC_DIR ${CMAKE_HOME_DIRECTORY}/src/robomongo) set(SOURCES_TEST ${ROBO_SRC_DIR}/utils/RoboCrypt_test.cpp ${ROBO_SRC_DIR}/utils/StringOperations_test.cpp ${ROBO_SRC_DIR}/core/HexUtils_test.cpp ) ### --- Setup robo_unit_tests exec. & link ROBO_OBJ_FILES add_executable(robo_unit_tests ${SOURCES_TEST}) add_dependencies(robo_unit_tests robomongo) target_include_directories(robo_unit_tests PRIVATE ${CMAKE_HOME_DIRECTORY}/src) get_target_property(ROBO_SOURCES robomongo SOURCES) list(FILTER ROBO_SOURCES INCLUDE REGEX "cpp") list(FILTER ROBO_SOURCES EXCLUDE REGEX "main.cpp") if(SYSTEM_WINDOWS) set(OBJ_DIR ${CMAKE_BINARY_DIR}/src/robomongo/robomongo.dir/${CMAKE_BUILD_TYPE}/) set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}mocs_compilation.obj;") set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}qrc_gui.obj;") set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}qrc_robo.obj;") foreach(SRC_FILE ${ROBO_SOURCES}) get_filename_component(FILENAME ${SRC_FILE} NAME) string(REPLACE ".cpp" ".obj" FILENAME ${FILENAME}) set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}${FILENAME};") endforeach() elseif(SYSTEM_MACOSX) set(OBJ_DIR ${CMAKE_BINARY_DIR}/src/robomongo/CMakeFiles/robomongo.dir/) set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}/robomongo_autogen/mocs_compilation.cpp.o;") set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}/robomongo_autogen/YHP5W5E6RA/qrc_gui.cpp.o;") set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}/robomongo_autogen/3YJK5W5UP7/qrc_robo.cpp.o;") foreach(SRC_FILE ${ROBO_SOURCES}) string(REPLACE ".cpp" ".cpp.o" OBJ_FILE ${SRC_FILE}) set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}${OBJ_FILE};") endforeach() find_library(SECURITY NAMES Security) find_library(CORE_FOUNDATION NAMES CoreFoundation) set(SSL_LIBRARIES ${SECURITY} ${CORE_FOUNDATION}) target_link_libraries(robo_unit_tests ${SSL_LIBRARIES} -lresolv) # elseif(SYSTEM_LINUX) # set(OBJ_DIR ${CMAKE_BINARY_DIR}/src/robomongo/CMakeFiles/robomongo.dir/) # message("--- OBJ_DIR: " ${OBJ_DIR}) # set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}/robomongo_automoc.cpp.o;") # set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}/robomongo_automoc.dir/gui/resources/qrc_gui.cpp.o;") # set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}/robomongo_automoc.dir/resources/qrc_robo.cpp.o;") # foreach(SRC_FILE ${ROBO_SOURCES}) # string(REPLACE ".cpp" ".cpp.o" OBJ_FILE ${SRC_FILE}) # set(ROBO_OBJ_FILES "${ROBO_OBJ_FILES}${OBJ_DIR}${OBJ_FILE};") # endforeach() endif() # Disable WebEngineWidgets for Linux SET(WebEngineWidgets "Qt5::WebEngineWidgets") if(SYSTEM_LINUX) SET(WebEngineWidgets) endif() target_link_libraries(robo_unit_tests gtest gtest_main Qt5::Widgets Qt5::Network Qt5::Xml ${WebEngineWidgets} qjson qscintilla mongodb ssh Threads::Threads ${ROBO_OBJ_FILES} ) ### --- Install DLLs for Windows if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(D "d") endif() if(SYSTEM_WINDOWS) get_target_property(target_path Qt5::Core LOCATION) get_filename_component(qt_bin_dir ${target_path} DIRECTORY) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5Core${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5Gui${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5Network${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5PrintSupport${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5Widgets${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5Xml${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5Positioning${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5Qml${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5Quick${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5QuickWidgets${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5WebChannel${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5WebEngineCore${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5WebEngineWidgets${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) set(QT_BIN_FILES "${QT_BIN_FILES}" ${qt_bin_dir}/Qt5PrintSupport${D}${CMAKE_SHARED_LIBRARY_SUFFIX};) get_target_property(TESTS_BIN_DIR robo_unit_tests BINARY_DIR) file(COPY ${QT_BIN_FILES} DESTINATION ${TESTS_BIN_DIR}/${CMAKE_BUILD_TYPE}) file(COPY ${OpenSSL_DIR}/libssl-1_1-x64.dll ${OpenSSL_DIR}/libcrypto-1_1-x64.dll DESTINATION ${TESTS_BIN_DIR}/${CMAKE_BUILD_TYPE}) endif() ================================================ FILE: src/robomongo-unit-tests/README.md ================================================ This is just a placeholder for `cmake`. Test source files are located in the same directory alongside the tested file in `robomongo/src/robomongo/` dir and sub-dirs. e.g. ``` ... RoboCrypt.cpp RoboCrypt_test.cpp ... StringOperations.cpp StringOperations_test.cpp ... ``` ================================================ FILE: src/third-party/README.md ================================================ Third-party dependencies ======================== Robomongo automatically compiles and statically links to the following libraries: 1. QJson 2. QScintilla You do not need to build them separately. ================================================ FILE: src/third-party/esprima-2.7.3/README.md ================================================ jquery/esprima ---------------- Esprima (esprima.org, BSD license) is a high performance, standard-compliant ECMAScript parser written in ECMAScript (also popularly known as JavaScript) Origin: https://github.com/jquery/esprima Robo path: https://github.com/Studio3T/robomongo/blob/master/src/robomongo/gui/resources/scripts/esprima.js ================================================ FILE: src/third-party/googletest-1.8.1/.gitignore ================================================ # Ignore CI build directory build/ xcuserdata cmake-build-debug/ .idea/ bazel-bin bazel-genfiles bazel-googletest bazel-out bazel-testlogs # python *.pyc # Visual Studio files *.sdf *.opensdf *.VC.opendb *.suo *.user _ReSharper.Caches/ Win32-Debug/ Win32-Release/ x64-Debug/ x64-Release/ # Ignore autoconf / automake files Makefile.in aclocal.m4 configure build-aux/ autom4te.cache/ googletest/m4/libtool.m4 googletest/m4/ltoptions.m4 googletest/m4/ltsugar.m4 googletest/m4/ltversion.m4 googletest/m4/lt~obsolete.m4 # Ignore generated directories. googlemock/fused-src/ googletest/fused-src/ # macOS files .DS_Store # Ignore cmake generated directories and files. CMakeFiles CTestTestfile.cmake Makefile cmake_install.cmake googlemock/CMakeFiles googlemock/CTestTestfile.cmake googlemock/Makefile googlemock/cmake_install.cmake googlemock/gtest ================================================ FILE: src/third-party/googletest-1.8.1/.travis.yml ================================================ # Build matrix / environment variable are explained on: # https://docs.travis-ci.com/user/customizing-the-build/ # This file can be validated on: # http://lint.travis-ci.org/ sudo: false language: cpp # Define the matrix explicitly, manually expanding the combinations of (os, compiler, env). # It is more tedious, but grants us far more flexibility. matrix: include: - os: linux compiler: gcc sudo : true install: ./ci/install-linux.sh && ./ci/log-config.sh script: ./ci/build-linux-bazel.sh - os: linux compiler: clang sudo : true install: ./ci/install-linux.sh && ./ci/log-config.sh script: ./ci/build-linux-bazel.sh - os: linux group: deprecated-2017Q4 compiler: gcc install: ./ci/install-linux.sh && ./ci/log-config.sh script: ./ci/build-linux-autotools.sh - os: linux group: deprecated-2017Q4 compiler: gcc env: BUILD_TYPE=Debug VERBOSE=1 CXX_FLAGS=-std=c++11 - os: linux group: deprecated-2017Q4 compiler: clang env: BUILD_TYPE=Debug VERBOSE=1 - os: linux group: deprecated-2017Q4 compiler: clang env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 - os: linux compiler: clang env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 NO_EXCEPTION=ON NO_RTTI=ON COMPILER_IS_GNUCXX=ON - os: osx compiler: gcc env: BUILD_TYPE=Debug VERBOSE=1 - os: osx compiler: gcc env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 - os: osx compiler: clang env: BUILD_TYPE=Debug VERBOSE=1 if: type != pull_request - os: osx env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 if: type != pull_request # These are the install and build (script) phases for the most common entries in the matrix. They could be included # in each entry in the matrix, but that is just repetitive. install: - ./ci/install-${TRAVIS_OS_NAME}.sh - . ./ci/env-${TRAVIS_OS_NAME}.sh - ./ci/log-config.sh script: ./ci/travis.sh # For sudo=false builds this section installs the necessary dependencies. addons: apt: # List of whitelisted in travis packages for ubuntu-precise can be found here: # https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise # List of whitelisted in travis apt-sources: # https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json sources: - ubuntu-toolchain-r-test - llvm-toolchain-precise-3.9 packages: - g++-4.9 - clang-3.9 notifications: email: false ================================================ FILE: src/third-party/googletest-1.8.1/BUILD.bazel ================================================ # Copyright 2017 Google Inc. # 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 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. # # Author: misterg@google.com (Gennadiy Civil) # # Bazel Build for Google C++ Testing Framework(Google Test) package(default_visibility = ["//visibility:public"]) licenses(["notice"]) config_setting( name = "windows", values = {"cpu": "x64_windows"}, ) config_setting( name = "windows_msvc", values = {"cpu": "x64_windows_msvc"}, ) config_setting( name = "has_absl", values = {"define": "absl=1"}, ) # Google Test including Google Mock cc_library( name = "gtest", srcs = glob( include = [ "googletest/src/*.cc", "googletest/src/*.h", "googletest/include/gtest/**/*.h", "googlemock/src/*.cc", "googlemock/include/gmock/**/*.h", ], exclude = [ "googletest/src/gtest-all.cc", "googletest/src/gtest_main.cc", "googlemock/src/gmock-all.cc", "googlemock/src/gmock_main.cc", ], ), hdrs = glob([ "googletest/include/gtest/*.h", "googlemock/include/gmock/*.h", ]), copts = select( { ":windows": [], ":windows_msvc": [], "//conditions:default": ["-pthread"], }, ), defines = select( { ":has_absl": [ "GTEST_HAS_ABSL=1", ], "//conditions:default": [], }, ), includes = [ "googlemock", "googlemock/include", "googletest", "googletest/include", ], linkopts = select({ ":windows": [], ":windows_msvc": [], "//conditions:default": [ "-pthread", ], }), deps = select( { ":has_absl": [ "@com_google_absl//absl/debugging:failure_signal_handler", "@com_google_absl//absl/debugging:stacktrace", "@com_google_absl//absl/debugging:symbolize", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:variant", ], "//conditions:default": [], }, ), ) cc_library( name = "gtest_main", srcs = [ "googlemock/src/gmock_main.cc", ], deps = [":gtest"], ) # The following rules build samples of how to use gTest. cc_library( name = "gtest_sample_lib", srcs = [ "googletest/samples/sample1.cc", "googletest/samples/sample2.cc", "googletest/samples/sample4.cc", ], hdrs = [ "googletest/samples/prime_tables.h", "googletest/samples/sample1.h", "googletest/samples/sample2.h", "googletest/samples/sample3-inl.h", "googletest/samples/sample4.h", ], ) cc_test( name = "gtest_samples", size = "small", #All Samples except: #sample9 ( main ) #sample10 (main and takes a command line option and needs to be separate) srcs = [ "googletest/samples/sample1_unittest.cc", "googletest/samples/sample2_unittest.cc", "googletest/samples/sample3_unittest.cc", "googletest/samples/sample4_unittest.cc", "googletest/samples/sample5_unittest.cc", "googletest/samples/sample6_unittest.cc", "googletest/samples/sample7_unittest.cc", "googletest/samples/sample8_unittest.cc", ], deps = [ "gtest_sample_lib", ":gtest_main", ], ) cc_test( name = "sample9_unittest", size = "small", srcs = ["googletest/samples/sample9_unittest.cc"], deps = [":gtest"], ) cc_test( name = "sample10_unittest", size = "small", srcs = ["googletest/samples/sample10_unittest.cc"], deps = [ ":gtest", ], ) ================================================ FILE: src/third-party/googletest-1.8.1/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 2.8.8) if (POLICY CMP0048) cmake_policy(SET CMP0048 NEW) endif (POLICY CMP0048) project(googletest-distribution) set(GOOGLETEST_VERSION 1.9.0) enable_testing() include(CMakeDependentOption) include(GNUInstallDirs) #Note that googlemock target already builds googletest option(BUILD_GMOCK "Builds the googlemock subproject" ON) option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON) if(BUILD_GMOCK) add_subdirectory( googlemock ) else() add_subdirectory( googletest ) endif() ================================================ FILE: src/third-party/googletest-1.8.1/CONTRIBUTING.md ================================================ # How to become a contributor and submit your own code ## Contributor License Agreements We'd love to accept your patches! Before we can take them, we have to jump a couple of legal hurdles. Please fill out either the individual or corporate Contributor License Agreement (CLA). * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual). * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to accept your pull requests. ## Are you a Googler? If you are a Googler, you can either create an internal change or work on GitHub directly. ## Contributing A Patch 1. Submit an issue describing your proposed change to the [issue tracker](https://github.com/google/googletest). 1. Please don't mix more than one logical change per submittal, because it makes the history hard to follow. If you want to make a change that doesn't have a corresponding issue in the issue tracker, please create one. 1. Also, coordinate with team members that are listed on the issue in question. This ensures that work isn't being duplicated and communicating your plan early also generally leads to better patches. 1. If your proposed change is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above). 1. Fork the desired repo, develop and test your code changes. 1. Ensure that your code adheres to the existing style in the sample to which you are contributing. 1. Ensure that your code has an appropriate set of unit tests which all pass. 1. Submit a pull request. ## The Google Test and Google Mock Communities ## The Google Test community exists primarily through the [discussion group](http://groups.google.com/group/googletestframework) and the GitHub repository. Likewise, the Google Mock community exists primarily through their own [discussion group](http://groups.google.com/group/googlemock). You are definitely encouraged to contribute to the discussion and you can also help us to keep the effectiveness of the group high by following and promoting the guidelines listed here. ### Please Be Friendly ### Showing courtesy and respect to others is a vital part of the Google culture, and we strongly encourage everyone participating in Google Test development to join us in accepting nothing less. Of course, being courteous is not the same as failing to constructively disagree with each other, but it does mean that we should be respectful of each other when enumerating the 42 technical reasons that a particular proposal may not be the best choice. There's never a reason to be antagonistic or dismissive toward anyone who is sincerely trying to contribute to a discussion. Sure, C++ testing is serious business and all that, but it's also a lot of fun. Let's keep it that way. Let's strive to be one of the friendliest communities in all of open source. As always, discuss Google Test in the official GoogleTest discussion group. You don't have to actually submit code in order to sign up. Your participation itself is a valuable contribution. ## Style To keep the source consistent, readable, diffable and easy to merge, we use a fairly rigid coding style, as defined by the [google-styleguide](https://github.com/google/styleguide) project. All patches will be expected to conform to the style outlined [here](https://google.github.io/styleguide/cppguide.html). ## Requirements for Contributors ### If you plan to contribute a patch, you need to build Google Test, Google Mock, and their own tests from a git checkout, which has further requirements: * [Python](https://www.python.org/) v2.3 or newer (for running some of the tests and re-generating certain source files from templates) * [CMake](https://cmake.org/) v2.6.4 or newer * [GNU Build System](https://en.wikipedia.org/wiki/GNU_Build_System) including automake (>= 1.9), autoconf (>= 2.59), and libtool / libtoolize. ## Developing Google Test ## This section discusses how to make your own changes to Google Test. ### Testing Google Test Itself ### To make sure your changes work as intended and don't break existing functionality, you'll want to compile and run Google Test's own tests. For that you can use CMake: mkdir mybuild cd mybuild cmake -Dgtest_build_tests=ON ${GTEST_DIR} Make sure you have Python installed, as some of Google Test's tests are written in Python. If the cmake command complains about not being able to find Python (`Could NOT find PythonInterp (missing: PYTHON_EXECUTABLE)`), try telling it explicitly where your Python executable can be found: cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR} Next, you can build Google Test and all of its own tests. On \*nix, this is usually done by 'make'. To run the tests, do make test All tests should pass. ### Regenerating Source Files ## Some of Google Test's source files are generated from templates (not in the C++ sense) using a script. For example, the file include/gtest/internal/gtest-type-util.h.pump is used to generate gtest-type-util.h in the same directory. You don't need to worry about regenerating the source files unless you need to modify them. You would then modify the corresponding `.pump` files and run the '[pump.py](googletest/scripts/pump.py)' generator script. See the [Pump Manual](googletest/docs/PumpManual.md). ## Developing Google Mock ### This section discusses how to make your own changes to Google Mock. #### Testing Google Mock Itself #### To make sure your changes work as intended and don't break existing functionality, you'll want to compile and run Google Test's own tests. For that you'll need Autotools. First, make sure you have followed the instructions above to configure Google Mock. Then, create a build output directory and enter it. Next, ${GMOCK_DIR}/configure # try --help for more info Once you have successfully configured Google Mock, the build steps are standard for GNU-style OSS packages. make # Standard makefile following GNU conventions make check # Builds and runs all tests - all should pass. Note that when building your project against Google Mock, you are building against Google Test as well. There is no need to configure Google Test separately. ================================================ FILE: src/third-party/googletest-1.8.1/LICENSE ================================================ Copyright 2008, Google Inc. 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 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. ================================================ FILE: src/third-party/googletest-1.8.1/Makefile.am ================================================ ## Process this file with automake to produce Makefile.in ACLOCAL_AMFLAGS = -I m4 AUTOMAKE_OPTIONS = foreign # Build . before src so that our all-local and clean-local hooks kicks in at # the right time. SUBDIRS = googletest googlemock EXTRA_DIST = \ BUILD.bazel \ CMakeLists.txt \ README.md \ WORKSPACE ================================================ FILE: src/third-party/googletest-1.8.1/README.md ================================================ # Google Test # [![Build Status](https://travis-ci.org/google/googletest.svg?branch=master)](https://travis-ci.org/google/googletest) [![Build status](https://ci.appveyor.com/api/projects/status/4o38plt0xbo1ubc8/branch/master?svg=true)](https://ci.appveyor.com/project/GoogleTestAppVeyor/googletest/branch/master) **Future Plans**: * 1.8.x Release - the 1.8.x will be the last release that works with pre-C++11 compilers. The 1.8.x will not accept any requests for any new features and any bugfix requests will only be accepted if proven "critical" * Post 1.8.x - work to improve/cleanup/pay technical debt. When this work is completed there will be a 1.9.x tagged release * Post 1.9.x googletest will follow [Abseil Live at Head philosophy](https://abseil.io/about/philosophy) Welcome to **Google Test**, Google's C++ test framework! This repository is a merger of the formerly separate GoogleTest and GoogleMock projects. These were so closely related that it makes sense to maintain and release them together. Please see the project page above for more information as well as the mailing list for questions, discussions, and development. There is also an IRC channel on [OFTC](https://webchat.oftc.net/) (irc.oftc.net) #gtest available. Please join us! Getting started information for **Google Test** is available in the [Google Test Primer](googletest/docs/primer.md) documentation. **Google Mock** is an extension to Google Test for writing and using C++ mock classes. See the separate [Google Mock documentation](googlemock/README.md). More detailed documentation for googletest (including build instructions) are in its interior [googletest/README.md](googletest/README.md) file. ## Features ## * An [xUnit](https://en.wikipedia.org/wiki/XUnit) test framework. * Test discovery. * A rich set of assertions. * User-defined assertions. * Death tests. * Fatal and non-fatal failures. * Value-parameterized tests. * Type-parameterized tests. * Various options for running the tests. * XML test report generation. ## Platforms ## Google test has been used on a variety of platforms: * Linux * Mac OS X * Windows * Cygwin * MinGW * Windows Mobile * Symbian ## Who Is Using Google Test? ## In addition to many internal projects at Google, Google Test is also used by the following notable projects: * The [Chromium projects](http://www.chromium.org/) (behind the Chrome browser and Chrome OS). * The [LLVM](http://llvm.org/) compiler. * [Protocol Buffers](https://github.com/google/protobuf), Google's data interchange format. * The [OpenCV](http://opencv.org/) computer vision library. * [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn): header only, dependency-free deep learning framework in C++11. ## Related Open Source Projects ## [GTest Runner](https://github.com/nholthaus/gtest-runner) is a Qt5 based automated test-runner and Graphical User Interface with powerful features for Windows and Linux platforms. [Google Test UI](https://github.com/ospector/gtest-gbar) is test runner that runs your test binary, allows you to track its progress via a progress bar, and displays a list of test failures. Clicking on one shows failure text. Google Test UI is written in C#. [GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event listener for Google Test that implements the [TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test result output. If your test runner understands TAP, you may find it useful. [gtest-parallel](https://github.com/google/gtest-parallel) is a test runner that runs tests from your binary in parallel to provide significant speed-up. [GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter) is a VS Code extension allowing to view Google Tests in a tree view, and run/debug your tests. ## Requirements ## Google Test is designed to have fairly minimal requirements to build and use with your projects, but there are some. Currently, we support Linux, Windows, Mac OS X, and Cygwin. We will also make our best effort to support other platforms (e.g. Solaris, AIX, and z/OS). However, since core members of the Google Test project have no access to these platforms, Google Test may have outstanding issues there. If you notice any problems on your platform, please notify [googletestframework@googlegroups.com](https://groups.google.com/forum/#!forum/googletestframework). Patches for fixing them are even more welcome! ### Linux Requirements ### These are the base requirements to build and use Google Test from a source package (as described below): * GNU-compatible Make or gmake * POSIX-standard shell * POSIX(-2) Regular Expressions (regex.h) * A C++98-standard-compliant compiler ### Windows Requirements ### * Microsoft Visual C++ 2015 or newer ### Cygwin Requirements ### * Cygwin v1.5.25-14 or newer ### Mac OS X Requirements ### * Mac OS X v10.4 Tiger or newer * Xcode Developer Tools ## Contributing change Please read the [`CONTRIBUTING.md`](CONTRIBUTING.md) for details on how to contribute to this project. Happy testing! ================================================ FILE: src/third-party/googletest-1.8.1/WORKSPACE ================================================ workspace(name = "com_google_googletest") # Abseil http_archive( name = "com_google_absl", urls = ["https://github.com/abseil/abseil-cpp/archive/master.zip"], strip_prefix = "abseil-cpp-master", ) ================================================ FILE: src/third-party/googletest-1.8.1/appveyor.yml ================================================ version: '{build}' os: Visual Studio 2015 environment: matrix: - compiler: msvc-15-seh generator: "Visual Studio 15 2017" APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - compiler: msvc-15-seh generator: "Visual Studio 15 2017 Win64" APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 enabled_on_pr: yes - compiler: msvc-14-seh generator: "Visual Studio 14 2015" enabled_on_pr: yes - compiler: msvc-14-seh generator: "Visual Studio 14 2015 Win64" - compiler: gcc-5.3.0-posix generator: "MinGW Makefiles" cxx_path: 'C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin' - compiler: gcc-6.3.0-posix generator: "MinGW Makefiles" cxx_path: 'C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin' configuration: - Debug build: verbosity: minimal install: - ps: | Write-Output "Compiler: $env:compiler" Write-Output "Generator: $env:generator" if (-not (Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER)) { Write-Output "This is *NOT* a pull request build" } else { Write-Output "This is a pull request build" if (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes") { Write-Output "PR builds are *NOT* explicitly enabled" } } # git bash conflicts with MinGW makefiles if ($env:generator -eq "MinGW Makefiles") { $env:path = $env:path.replace("C:\Program Files\Git\usr\bin;", "") if ($env:cxx_path -ne "") { $env:path += ";$env:cxx_path" } } build_script: - ps: | # Only enable some builds for pull requests, the AppVeyor queue is too long. if ((Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) -And (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes")) { return } md _build -Force | Out-Null cd _build $conf = if ($env:generator -eq "MinGW Makefiles") {"-DCMAKE_BUILD_TYPE=$env:configuration"} else {"-DCMAKE_CONFIGURATION_TYPES=Debug;Release"} # Disable test for MinGW (gtest tests fail, gmock tests can not build) $gtest_build_tests = if ($env:generator -eq "MinGW Makefiles") {"-Dgtest_build_tests=OFF"} else {"-Dgtest_build_tests=ON"} $gmock_build_tests = if ($env:generator -eq "MinGW Makefiles") {"-Dgmock_build_tests=OFF"} else {"-Dgmock_build_tests=ON"} & cmake -G "$env:generator" $conf -Dgtest_build_samples=ON $gtest_build_tests $gmock_build_tests .. if ($LastExitCode -ne 0) { throw "Exec: $ErrorMessage" } $cmake_parallel = if ($env:generator -eq "MinGW Makefiles") {"-j2"} else {"/m"} & cmake --build . --config $env:configuration -- $cmake_parallel if ($LastExitCode -ne 0) { throw "Exec: $ErrorMessage" } skip_commits: files: - '**/*.md' test_script: - ps: | # Only enable some builds for pull requests, the AppVeyor queue is too long. if ((Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) -And (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes")) { return } if ($env:generator -eq "MinGW Makefiles") { return # No test available for MinGW } & ctest -C $env:configuration --timeout 600 --output-on-failure if ($LastExitCode -ne 0) { throw "Exec: $ErrorMessage" } artifacts: - path: '_build/CMakeFiles/*.log' name: logs - path: '_build/Testing/**/*.xml' name: test_results ================================================ FILE: src/third-party/googletest-1.8.1/ci/build-linux-autotools.sh ================================================ #!/usr/bin/env bash # Copyright 2017 Google Inc. # 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 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. set -e . ci/get-nprocessors.sh # Create the configuration script autoreconf -i # Run in a subdirectory to keep the sources clean mkdir build || true cd build ../configure make -j ${NPROCESSORS:-2} ================================================ FILE: src/third-party/googletest-1.8.1/ci/build-linux-bazel.sh ================================================ #!/usr/bin/env bash # Copyright 2017 Google Inc. # 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 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. set -e bazel build --curses=no //...:all bazel test --curses=no //...:all bazel test --curses=no //...:all --define absl=1 ================================================ FILE: src/third-party/googletest-1.8.1/ci/env-linux.sh ================================================ #!/usr/bin/env bash # Copyright 2017 Google Inc. # 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 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. # # This file should be sourced, and not executed as a standalone script. # # TODO() - we can check if this is being sourced using $BASH_VERSION and $BASH_SOURCE[0] != ${0}. if [ "${TRAVIS_OS_NAME}" = "linux" ]; then if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi if [ "$CXX" = "clang++" ]; then export CXX="clang++-3.9" CC="clang-3.9"; fi fi ================================================ FILE: src/third-party/googletest-1.8.1/ci/env-osx.sh ================================================ #!/usr/bin/env bash # Copyright 2017 Google Inc. # 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 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. # # This file should be sourced, and not executed as a standalone script. # # TODO() - we can check if this is being sourced using $BASH_VERSION and $BASH_SOURCE[0] != ${0}. if [ "${TRAVIS_OS_NAME}" = "linux" ]; then if [ "$CXX" = "clang++" ]; then export CXX="clang++-3.9" CC="clang-3.9"; fi fi ================================================ FILE: src/third-party/googletest-1.8.1/ci/get-nprocessors.sh ================================================ #!/usr/bin/env bash # Copyright 2017 Google Inc. # 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 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. # This file is typically sourced by another script. # if possible, ask for the precise number of processors, # otherwise take 2 processors as reasonable default; see # https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization if [ -x /usr/bin/getconf ]; then NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN) else NPROCESSORS=2 fi # as of 2017-09-04 Travis CI reports 32 processors, but GCC build # crashes if parallelized too much (maybe memory consumption problem), # so limit to 4 processors for the time being. if [ $NPROCESSORS -gt 4 ] ; then echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4." NPROCESSORS=4 fi ================================================ FILE: src/third-party/googletest-1.8.1/ci/install-linux.sh ================================================ #!/usr/bin/env bash # Copyright 2017 Google Inc. # 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 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. set -eu if [ "${TRAVIS_OS_NAME}" != linux ]; then echo "Not a Linux build; skipping installation" exit 0 fi if [ "${TRAVIS_SUDO}" = "true" ]; then echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | \ sudo tee /etc/apt/sources.list.d/bazel.list curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add - sudo apt-get update && sudo apt-get install -y bazel gcc-4.9 g++-4.9 clang-3.9 elif [ "${CXX}" = "clang++" ]; then # Use ccache, assuming $HOME/bin is in the path, which is true in the Travis build environment. ln -sf /usr/bin/ccache $HOME/bin/${CXX}; ln -sf /usr/bin/ccache $HOME/bin/${CC}; fi ================================================ FILE: src/third-party/googletest-1.8.1/ci/install-osx.sh ================================================ #!/usr/bin/env bash # Copyright 2017 Google Inc. # 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 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. set -eu if [ "${TRAVIS_OS_NAME}" != "osx" ]; then echo "Not a macOS build; skipping installation" exit 0 fi brew install ccache ================================================ FILE: src/third-party/googletest-1.8.1/ci/log-config.sh ================================================ #!/usr/bin/env bash # Copyright 2017 Google Inc. # 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 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. set -e # ccache on OS X needs installation first # reset ccache statistics ccache --zero-stats echo PATH=${PATH} echo "Compiler configuration:" echo CXX=${CXX} echo CC=${CC} echo CXXFLAGS=${CXXFLAGS} echo "C++ compiler version:" ${CXX} --version || echo "${CXX} does not seem to support the --version flag" ${CXX} -v || echo "${CXX} does not seem to support the -v flag" echo "C compiler version:" ${CC} --version || echo "${CXX} does not seem to support the --version flag" ${CC} -v || echo "${CXX} does not seem to support the -v flag" ================================================ FILE: src/third-party/googletest-1.8.1/ci/travis.sh ================================================ #!/usr/bin/env sh set -evx . ci/get-nprocessors.sh # if possible, ask for the precise number of processors, # otherwise take 2 processors as reasonable default; see # https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization if [ -x /usr/bin/getconf ]; then NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN) else NPROCESSORS=2 fi # as of 2017-09-04 Travis CI reports 32 processors, but GCC build # crashes if parallelized too much (maybe memory consumption problem), # so limit to 4 processors for the time being. if [ $NPROCESSORS -gt 4 ] ; then echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4." NPROCESSORS=4 fi # Tell make to use the processors. No preceding '-' required. MAKEFLAGS="j${NPROCESSORS}" export MAKEFLAGS env | sort # Set default values to OFF for these variables if not specified. : "${NO_EXCEPTION:=OFF}" : "${NO_RTTI:=OFF}" : "${COMPILER_IS_GNUCXX:=OFF}" mkdir build || true cd build cmake -Dgtest_build_samples=ON \ -Dgtest_build_tests=ON \ -Dgmock_build_tests=ON \ -Dcxx_no_exception=$NO_EXCEPTION \ -Dcxx_no_rtti=$NO_RTTI \ -DCMAKE_COMPILER_IS_GNUCXX=$COMPILER_IS_GNUCXX \ -DCMAKE_CXX_FLAGS=$CXX_FLAGS \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ .. make CTEST_OUTPUT_ON_FAILURE=1 make test ================================================ FILE: src/third-party/googletest-1.8.1/configure.ac ================================================ AC_INIT([Google C++ Mocking and Testing Frameworks], [1.8.0], [googlemock@googlegroups.com], [googletest]) # Provide various options to initialize the Autoconf and configure processes. AC_PREREQ([2.59]) AC_CONFIG_SRCDIR([./README.md]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_FILES([Makefile]) AC_CONFIG_SUBDIRS([googletest googlemock]) AM_INIT_AUTOMAKE # Output the generated files. No further autoconf macros may be used. AC_OUTPUT ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/CHANGES ================================================ Changes for 1.7.0: * All new improvements in Google Test 1.7.0. * New feature: matchers DoubleNear(), FloatNear(), NanSensitiveDoubleNear(), NanSensitiveFloatNear(), UnorderedElementsAre(), UnorderedElementsAreArray(), WhenSorted(), WhenSortedBy(), IsEmpty(), and SizeIs(). * Improvement: Google Mock can now be built as a DLL. * Improvement: when compiled by a C++11 compiler, matchers AllOf() and AnyOf() can accept an arbitrary number of matchers. * Improvement: when compiled by a C++11 compiler, matchers ElementsAreArray() can accept an initializer list. * Improvement: when exceptions are enabled, a mock method with no default action now throws instead crashing the test. * Improvement: added class testing::StringMatchResultListener to aid definition of composite matchers. * Improvement: function return types used in MOCK_METHOD*() macros can now contain unprotected commas. * Improvement (potentially breaking): EXPECT_THAT() and ASSERT_THAT() are now more strict in ensuring that the value type and the matcher type are compatible, catching potential bugs in tests. * Improvement: Pointee() now works on an optional. * Improvement: the ElementsAreArray() matcher can now take a vector or iterator range as input, and makes a copy of its input elements before the conversion to a Matcher. * Improvement: the Google Mock Generator can now generate mocks for some class templates. * Bug fix: mock object destruction triggerred by another mock object's destruction no longer hangs. * Improvement: Google Mock Doctor works better with newer Clang and GCC now. * Compatibility fixes. * Bug/warning fixes. Changes for 1.6.0: * Compilation is much faster and uses much less memory, especially when the constructor and destructor of a mock class are moved out of the class body. * New matchers: Pointwise(), Each(). * New actions: ReturnPointee() and ReturnRefOfCopy(). * CMake support. * Project files for Visual Studio 2010. * AllOf() and AnyOf() can handle up-to 10 arguments now. * Google Mock doctor understands Clang error messages now. * SetArgPointee<> now accepts string literals. * gmock_gen.py handles storage specifier macros and template return types now. * Compatibility fixes. * Bug fixes and implementation clean-ups. * Potentially incompatible changes: disables the harmful 'make install' command in autotools. Potentially breaking changes: * The description string for MATCHER*() changes from Python-style interpolation to an ordinary C++ string expression. * SetArgumentPointee is deprecated in favor of SetArgPointee. * Some non-essential project files for Visual Studio 2005 are removed. Changes for 1.5.0: * New feature: Google Mock can be safely used in multi-threaded tests on platforms having pthreads. * New feature: function for printing a value of arbitrary type. * New feature: function ExplainMatchResult() for easy definition of composite matchers. * The new matcher API lets user-defined matchers generate custom explanations more directly and efficiently. * Better failure messages all around. * NotNull() and IsNull() now work with smart pointers. * Field() and Property() now work when the matcher argument is a pointer passed by reference. * Regular expression matchers on all platforms. * Added GCC 4.0 support for Google Mock Doctor. * Added gmock_all_test.cc for compiling most Google Mock tests in a single file. * Significantly cleaned up compiler warnings. * Bug fixes, better test coverage, and implementation clean-ups. Potentially breaking changes: * Custom matchers defined using MatcherInterface or MakePolymorphicMatcher() need to be updated after upgrading to Google Mock 1.5.0; matchers defined using MATCHER or MATCHER_P* aren't affected. * Dropped support for 'make install'. Changes for 1.4.0 (we skipped 1.2.* and 1.3.* to match the version of Google Test): * Works in more environments: Symbian and minGW, Visual C++ 7.1. * Lighter weight: comes with our own implementation of TR1 tuple (no more dependency on Boost!). * New feature: --gmock_catch_leaked_mocks for detecting leaked mocks. * New feature: ACTION_TEMPLATE for defining templatized actions. * New feature: the .After() clause for specifying expectation order. * New feature: the .With() clause for specifying inter-argument constraints. * New feature: actions ReturnArg(), ReturnNew(...), and DeleteArg(). * New feature: matchers Key(), Pair(), Args<...>(), AllArgs(), IsNull(), and Contains(). * New feature: utility class MockFunction, useful for checkpoints, etc. * New feature: functions Value(x, m) and SafeMatcherCast(m). * New feature: copying a mock object is rejected at compile time. * New feature: a script for fusing all Google Mock and Google Test source files for easy deployment. * Improved the Google Mock doctor to diagnose more diseases. * Improved the Google Mock generator script. * Compatibility fixes for Mac OS X and gcc. * Bug fixes and implementation clean-ups. Changes for 1.1.0: * New feature: ability to use Google Mock with any testing framework. * New feature: macros for easily defining new matchers * New feature: macros for easily defining new actions. * New feature: more container matchers. * New feature: actions for accessing function arguments and throwing exceptions. * Improved the Google Mock doctor script for diagnosing compiler errors. * Bug fixes and implementation clean-ups. Changes for 1.0.0: * Initial Open Source release of Google Mock ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/CMakeLists.txt ================================================ ######################################################################## # CMake build script for Google Mock. # # To run the tests for Google Mock itself on Linux, use 'make test' or # ctest. You can select which tests to run using 'ctest -R regex'. # For more options, run 'ctest --help'. option(gmock_build_tests "Build all of Google Mock's own tests." OFF) # A directory to find Google Test sources. if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/gtest/CMakeLists.txt") set(gtest_dir gtest) else() set(gtest_dir ../googletest) endif() # Defines pre_project_set_up_hermetic_build() and set_up_hermetic_build(). include("${gtest_dir}/cmake/hermetic_build.cmake" OPTIONAL) if (COMMAND pre_project_set_up_hermetic_build) # Google Test also calls hermetic setup functions from add_subdirectory, # although its changes will not affect things at the current scope. pre_project_set_up_hermetic_build() endif() ######################################################################## # # Project-wide settings # Name of the project. # # CMake files in this project can refer to the root source directory # as ${gmock_SOURCE_DIR} and to the root binary directory as # ${gmock_BINARY_DIR}. # Language "C" is required for find_package(Threads). if (CMAKE_VERSION VERSION_LESS 3.0) project(gmock CXX C) else() cmake_policy(SET CMP0048 NEW) project(gmock VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C) endif() cmake_minimum_required(VERSION 2.6.4) if (COMMAND set_up_hermetic_build) set_up_hermetic_build() endif() # Instructs CMake to process Google Test's CMakeLists.txt and add its # targets to the current scope. We are placing Google Test's binary # directory in a subdirectory of our own as VC compilation may break # if they are the same (the default). add_subdirectory("${gtest_dir}" "${gmock_BINARY_DIR}/gtest") # These commands only run if this is the main project if(CMAKE_PROJECT_NAME STREQUAL "gmock" OR CMAKE_PROJECT_NAME STREQUAL "googletest-distribution") # BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to # make it prominent in the GUI. option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF) else() mark_as_advanced(gmock_build_tests) endif() # Although Google Test's CMakeLists.txt calls this function, the # changes there don't affect the current scope. Therefore we have to # call it again here. config_compiler_and_linker() # from ${gtest_dir}/cmake/internal_utils.cmake # Adds Google Mock's and Google Test's header directories to the search path. set(gmock_build_include_dirs "${gmock_SOURCE_DIR}/include" "${gmock_SOURCE_DIR}" "${gtest_SOURCE_DIR}/include" # This directory is needed to build directly from Google Test sources. "${gtest_SOURCE_DIR}") include_directories(${gmock_build_include_dirs}) # Summary of tuple support for Microsoft Visual Studio: # Compiler version(MS) version(cmake) Support # ---------- ----------- -------------- ----------------------------- # <= VS 2010 <= 10 <= 1600 Use Google Tests's own tuple. # VS 2012 11 1700 std::tr1::tuple + _VARIADIC_MAX=10 # VS 2013 12 1800 std::tr1::tuple # VS 2015 14 1900 std::tuple # VS 2017 15 >= 1910 std::tuple if (MSVC AND MSVC_VERSION EQUAL 1700) add_definitions(/D _VARIADIC_MAX=10) endif() ######################################################################## # # Defines the gmock & gmock_main libraries. User tests should link # with one of them. # Google Mock libraries. We build them using more strict warnings than what # are used for other targets, to ensure that Google Mock can be compiled by # a user aggressive about warnings. if (MSVC) cxx_library(gmock "${cxx_strict}" "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc) cxx_library(gmock_main "${cxx_strict}" "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) else() cxx_library(gmock "${cxx_strict}" src/gmock-all.cc) target_link_libraries(gmock PUBLIC gtest) cxx_library(gmock_main "${cxx_strict}" src/gmock_main.cc) target_link_libraries(gmock_main PUBLIC gmock) endif() # If the CMake version supports it, attach header directory information # to the targets for when we are part of a parent build (ie being pulled # in via add_subdirectory() rather than being a standalone build). if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11") target_include_directories(gmock SYSTEM INTERFACE "$" "$/${CMAKE_INSTALL_INCLUDEDIR}>") target_include_directories(gmock_main SYSTEM INTERFACE "$" "$/${CMAKE_INSTALL_INCLUDEDIR}>") endif() ######################################################################## # # Install rules install_project(gmock gmock_main) ######################################################################## # # Google Mock's own tests. # # You can skip this section if you aren't interested in testing # Google Mock itself. # # The tests are not built by default. To build them, set the # gmock_build_tests option to ON. You can do it by running ccmake # or specifying the -Dgmock_build_tests=ON flag when running cmake. if (gmock_build_tests) # This must be set in the root directory for the tests to be run by # 'make test' or ctest. enable_testing() ############################################################ # C++ tests built with standard compiler flags. cxx_test(gmock-actions_test gmock_main) cxx_test(gmock-cardinalities_test gmock_main) cxx_test(gmock_ex_test gmock_main) cxx_test(gmock-generated-actions_test gmock_main) cxx_test(gmock-generated-function-mockers_test gmock_main) cxx_test(gmock-generated-internal-utils_test gmock_main) cxx_test(gmock-generated-matchers_test gmock_main) cxx_test(gmock-internal-utils_test gmock_main) cxx_test(gmock-matchers_test gmock_main) cxx_test(gmock-more-actions_test gmock_main) cxx_test(gmock-nice-strict_test gmock_main) cxx_test(gmock-port_test gmock_main) cxx_test(gmock-spec-builders_test gmock_main) cxx_test(gmock_link_test gmock_main test/gmock_link2_test.cc) cxx_test(gmock_test gmock_main) if (DEFINED GTEST_HAS_PTHREAD) cxx_test(gmock_stress_test gmock) endif() # gmock_all_test is commented to save time building and running tests. # Uncomment if necessary. # cxx_test(gmock_all_test gmock_main) ############################################################ # C++ tests built with non-standard compiler flags. if (MSVC) cxx_library(gmock_main_no_exception "${cxx_no_exception}" "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) cxx_library(gmock_main_no_rtti "${cxx_no_rtti}" "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) if (MSVC_VERSION LESS 1600) # 1600 is Visual Studio 2010. # Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that # conflict with our own definitions. Therefore using our own tuple does not # work on those compilers. cxx_library(gmock_main_use_own_tuple "${cxx_use_own_tuple}" "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) cxx_test_with_flags(gmock_use_own_tuple_test "${cxx_use_own_tuple}" gmock_main_use_own_tuple test/gmock-spec-builders_test.cc) endif() else() cxx_library(gmock_main_no_exception "${cxx_no_exception}" src/gmock_main.cc) target_link_libraries(gmock_main_no_exception PUBLIC gmock) cxx_library(gmock_main_no_rtti "${cxx_no_rtti}" src/gmock_main.cc) target_link_libraries(gmock_main_no_rtti PUBLIC gmock) cxx_library(gmock_main_use_own_tuple "${cxx_use_own_tuple}" src/gmock_main.cc) target_link_libraries(gmock_main_use_own_tuple PUBLIC gmock) endif() cxx_test_with_flags(gmock-more-actions_no_exception_test "${cxx_no_exception}" gmock_main_no_exception test/gmock-more-actions_test.cc) cxx_test_with_flags(gmock_no_rtti_test "${cxx_no_rtti}" gmock_main_no_rtti test/gmock-spec-builders_test.cc) cxx_shared_library(shared_gmock_main "${cxx_default}" "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) # Tests that a binary can be built with Google Mock as a shared library. On # some system configurations, it may not possible to run the binary without # knowing more details about the system configurations. We do not try to run # this binary. To get a more robust shared library coverage, configure with # -DBUILD_SHARED_LIBS=ON. cxx_executable_with_flags(shared_gmock_test_ "${cxx_default}" shared_gmock_main test/gmock-spec-builders_test.cc) set_target_properties(shared_gmock_test_ PROPERTIES COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1") ############################################################ # Python tests. cxx_executable(gmock_leak_test_ test gmock_main) py_test(gmock_leak_test) cxx_executable(gmock_output_test_ test gmock) py_test(gmock_output_test) endif() ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/CONTRIBUTORS ================================================ # This file contains a list of people who've made non-trivial # contribution to the Google C++ Mocking Framework project. People # who commit code to the project are encouraged to add their names # here. Please keep the list sorted by first names. Benoit Sigoure Bogdan Piloca Chandler Carruth Dave MacLachlan David Anderson Dean Sturtevant Gene Volovich Hal Burch Jeffrey Yasskin Jim Keller Joe Walnes Jon Wray Keir Mierle Keith Ray Kostya Serebryany Lev Makhlis Manuel Klimek Mario Tanev Mark Paskin Markus Heule Matthew Simmons Mike Bland Neal Norwitz Nermin Ozkiranartli Owen Carlsen Paneendra Ba Paul Menage Piotr Kaminski Russ Rufer Sverre Sundsdal Takeshi Yoshino Vadim Berman Vlad Losev Wolfgang Klier Zhanyong Wan ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/LICENSE ================================================ Copyright 2008, Google Inc. 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 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. ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/Makefile.am ================================================ # Automake file # Nonstandard package files for distribution. EXTRA_DIST = LICENSE # We may need to build our internally packaged gtest. If so, it will be # included in the 'subdirs' variable. SUBDIRS = $(subdirs) # This is generated by the configure script, so clean it for distribution. DISTCLEANFILES = scripts/gmock-config # We define the global AM_CPPFLAGS as everything we compile includes from these # directories. AM_CPPFLAGS = $(GTEST_CPPFLAGS) -I$(srcdir)/include # Modifies compiler and linker flags for pthreads compatibility. if HAVE_PTHREADS AM_CXXFLAGS = @PTHREAD_CFLAGS@ -DGTEST_HAS_PTHREAD=1 AM_LIBS = @PTHREAD_LIBS@ endif # Build rules for libraries. lib_LTLIBRARIES = lib/libgmock.la lib/libgmock_main.la lib_libgmock_la_SOURCES = src/gmock-all.cc pkginclude_HEADERS = \ include/gmock/gmock-actions.h \ include/gmock/gmock-cardinalities.h \ include/gmock/gmock-generated-actions.h \ include/gmock/gmock-generated-function-mockers.h \ include/gmock/gmock-generated-matchers.h \ include/gmock/gmock-generated-nice-strict.h \ include/gmock/gmock-matchers.h \ include/gmock/gmock-more-actions.h \ include/gmock/gmock-more-matchers.h \ include/gmock/gmock-spec-builders.h \ include/gmock/gmock.h pkginclude_internaldir = $(pkgincludedir)/internal pkginclude_internal_HEADERS = \ include/gmock/internal/gmock-generated-internal-utils.h \ include/gmock/internal/gmock-internal-utils.h \ include/gmock/internal/gmock-port.h \ include/gmock/internal/custom/gmock-generated-actions.h \ include/gmock/internal/custom/gmock-matchers.h \ include/gmock/internal/custom/gmock-port.h lib_libgmock_main_la_SOURCES = src/gmock_main.cc lib_libgmock_main_la_LIBADD = lib/libgmock.la # Build rules for tests. Automake's naming for some of these variables isn't # terribly obvious, so this is a brief reference: # # TESTS -- Programs run automatically by "make check" # check_PROGRAMS -- Programs built by "make check" but not necessarily run TESTS= check_PROGRAMS= AM_LDFLAGS = $(GTEST_LDFLAGS) # This exercises all major components of Google Mock. It also # verifies that libgmock works. TESTS += test/gmock-spec-builders_test check_PROGRAMS += test/gmock-spec-builders_test test_gmock_spec_builders_test_SOURCES = test/gmock-spec-builders_test.cc test_gmock_spec_builders_test_LDADD = $(GTEST_LIBS) lib/libgmock.la # This tests using Google Mock in multiple translation units. It also # verifies that libgmock_main and libgmock work. TESTS += test/gmock_link_test check_PROGRAMS += test/gmock_link_test test_gmock_link_test_SOURCES = \ test/gmock_link2_test.cc \ test/gmock_link_test.cc \ test/gmock_link_test.h test_gmock_link_test_LDADD = $(GTEST_LIBS) lib/libgmock_main.la lib/libgmock.la if HAVE_PYTHON # Tests that fused gmock files compile and work. TESTS += test/gmock_fused_test check_PROGRAMS += test/gmock_fused_test test_gmock_fused_test_SOURCES = \ fused-src/gmock-gtest-all.cc \ fused-src/gmock/gmock.h \ fused-src/gmock_main.cc \ fused-src/gtest/gtest.h \ test/gmock_test.cc test_gmock_fused_test_CPPFLAGS = -I"$(srcdir)/fused-src" endif # Google Mock source files that we don't compile directly. GMOCK_SOURCE_INGLUDES = \ src/gmock-cardinalities.cc \ src/gmock-internal-utils.cc \ src/gmock-matchers.cc \ src/gmock-spec-builders.cc \ src/gmock.cc EXTRA_DIST += $(GMOCK_SOURCE_INGLUDES) # C++ tests that we don't compile using autotools. EXTRA_DIST += \ test/gmock-actions_test.cc \ test/gmock_all_test.cc \ test/gmock-cardinalities_test.cc \ test/gmock_ex_test.cc \ test/gmock-generated-actions_test.cc \ test/gmock-generated-function-mockers_test.cc \ test/gmock-generated-internal-utils_test.cc \ test/gmock-generated-matchers_test.cc \ test/gmock-internal-utils_test.cc \ test/gmock-matchers_test.cc \ test/gmock-more-actions_test.cc \ test/gmock-nice-strict_test.cc \ test/gmock-port_test.cc \ test/gmock_stress_test.cc # Python tests, which we don't run using autotools. EXTRA_DIST += \ test/gmock_leak_test.py \ test/gmock_leak_test_.cc \ test/gmock_output_test.py \ test/gmock_output_test_.cc \ test/gmock_output_test_golden.txt \ test/gmock_test_utils.py # Nonstandard package files for distribution. EXTRA_DIST += \ CHANGES \ CONTRIBUTORS \ make/Makefile # Pump scripts for generating Google Mock headers. # TODO(chandlerc@google.com): automate the generation of *.h from *.h.pump. EXTRA_DIST += \ include/gmock/gmock-generated-actions.h.pump \ include/gmock/gmock-generated-function-mockers.h.pump \ include/gmock/gmock-generated-matchers.h.pump \ include/gmock/gmock-generated-nice-strict.h.pump \ include/gmock/internal/gmock-generated-internal-utils.h.pump \ include/gmock/internal/custom/gmock-generated-actions.h.pump # Script for fusing Google Mock and Google Test source files. EXTRA_DIST += scripts/fuse_gmock_files.py # The Google Mock Generator tool from the cppclean project. EXTRA_DIST += \ scripts/generator/LICENSE \ scripts/generator/README \ scripts/generator/README.cppclean \ scripts/generator/cpp/__init__.py \ scripts/generator/cpp/ast.py \ scripts/generator/cpp/gmock_class.py \ scripts/generator/cpp/keywords.py \ scripts/generator/cpp/tokenize.py \ scripts/generator/cpp/utils.py \ scripts/generator/gmock_gen.py # Script for diagnosing compiler errors in programs that use Google # Mock. EXTRA_DIST += scripts/gmock_doctor.py # CMake scripts. EXTRA_DIST += \ CMakeLists.txt # Microsoft Visual Studio 2005 projects. EXTRA_DIST += \ msvc/2005/gmock.sln \ msvc/2005/gmock.vcproj \ msvc/2005/gmock_config.vsprops \ msvc/2005/gmock_main.vcproj \ msvc/2005/gmock_test.vcproj # Microsoft Visual Studio 2010 projects. EXTRA_DIST += \ msvc/2010/gmock.sln \ msvc/2010/gmock.vcxproj \ msvc/2010/gmock_config.props \ msvc/2010/gmock_main.vcxproj \ msvc/2010/gmock_test.vcxproj if HAVE_PYTHON # gmock_test.cc does not really depend on files generated by the # fused-gmock-internal rule. However, gmock_test.o does, and it is # important to include test/gmock_test.cc as part of this rule in order to # prevent compiling gmock_test.o until all dependent files have been # generated. $(test_gmock_fused_test_SOURCES): fused-gmock-internal # TODO(vladl@google.com): Find a way to add Google Tests's sources here. fused-gmock-internal: $(pkginclude_HEADERS) $(pkginclude_internal_HEADERS) \ $(lib_libgmock_la_SOURCES) $(GMOCK_SOURCE_INGLUDES) \ $(lib_libgmock_main_la_SOURCES) \ scripts/fuse_gmock_files.py mkdir -p "$(srcdir)/fused-src" chmod -R u+w "$(srcdir)/fused-src" rm -f "$(srcdir)/fused-src/gtest/gtest.h" rm -f "$(srcdir)/fused-src/gmock/gmock.h" rm -f "$(srcdir)/fused-src/gmock-gtest-all.cc" "$(srcdir)/scripts/fuse_gmock_files.py" "$(srcdir)/fused-src" cp -f "$(srcdir)/src/gmock_main.cc" "$(srcdir)/fused-src" maintainer-clean-local: rm -rf "$(srcdir)/fused-src" endif # Death tests may produce core dumps in the build directory. In case # this happens, clean them to keep distcleancheck happy. CLEANFILES = core # Disables 'make install' as installing a compiled version of Google # Mock can lead to undefined behavior due to violation of the # One-Definition Rule. install-exec-local: echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Mock into your build system." false install-data-local: echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Mock into your build system." false ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/README.md ================================================ ## Google Mock ## The Google C++ mocking framework. ### Overview ### Google's framework for writing and using C++ mock classes. It can help you derive better designs of your system and write better tests. It is inspired by: * [jMock](http://www.jmock.org/), * [EasyMock](http://www.easymock.org/), and * [Hamcrest](http://code.google.com/p/hamcrest/), and designed with C++'s specifics in mind. Google mock: * lets you create mock classes trivially using simple macros. * supports a rich set of matchers and actions. * handles unordered, partially ordered, or completely ordered expectations. * is extensible by users. We hope you find it useful! ### Features ### * Provides a declarative syntax for defining mocks. * Can easily define partial (hybrid) mocks, which are a cross of real and mock objects. * Handles functions of arbitrary types and overloaded functions. * Comes with a rich set of matchers for validating function arguments. * Uses an intuitive syntax for controlling the behavior of a mock. * Does automatic verification of expectations (no record-and-replay needed). * Allows arbitrary (partial) ordering constraints on function calls to be expressed,. * Lets an user extend it by defining new matchers and actions. * Does not use exceptions. * Is easy to learn and use. Please see the project page above for more information as well as the mailing list for questions, discussions, and development. There is also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please join us! Please note that code under [scripts/generator](scripts/generator/) is from [cppclean](http://code.google.com/p/cppclean/) and released under the Apache License, which is different from Google Mock's license. ## Getting Started ## If you are new to the project, we suggest that you read the user documentation in the following order: * Learn the [basics](../../master/googletest/docs/primer.md) of Google Test, if you choose to use Google Mock with it (recommended). * Read [Google Mock for Dummies](../../master/googlemock/docs/ForDummies.md). * Read the instructions below on how to build Google Mock. You can also watch Zhanyong's [talk](http://www.youtube.com/watch?v=sYpCyLI47rM) on Google Mock's usage and implementation. Once you understand the basics, check out the rest of the docs: * [CheatSheet](../../master/googlemock/docs/CheatSheet.md) - all the commonly used stuff at a glance. * [CookBook](../../master/googlemock/docs/CookBook.md) - recipes for getting things done, including advanced techniques. If you need help, please check the [KnownIssues](docs/KnownIssues.md) and [FrequentlyAskedQuestions](docs/FrequentlyAskedQuestions.md) before posting a question on the [discussion group](http://groups.google.com/group/googlemock). ### Using Google Mock Without Google Test ### Google Mock is not a testing framework itself. Instead, it needs a testing framework for writing tests. Google Mock works seamlessly with [Google Test](https://github.com/google/googletest), but you can also use it with [any C++ testing framework](../../master/googlemock/docs/ForDummies.md#using-google-mock-with-any-testing-framework). ### Requirements for End Users ### Google Mock is implemented on top of [Google Test]( http://github.com/google/googletest/), and depends on it. You must use the bundled version of Google Test when using Google Mock. You can also easily configure Google Mock to work with another testing framework, although it will still need Google Test. Please read ["Using_Google_Mock_with_Any_Testing_Framework"]( ../../master/googlemock/docs/ForDummies.md#using-google-mock-with-any-testing-framework) for instructions. Google Mock depends on advanced C++ features and thus requires a more modern compiler. The following are needed to use Google Mock: #### Linux Requirements #### * GNU-compatible Make or "gmake" * POSIX-standard shell * POSIX(-2) Regular Expressions (regex.h) * C++98-standard-compliant compiler (e.g. GCC 3.4 or newer) #### Windows Requirements #### * Microsoft Visual C++ 8.0 SP1 or newer #### Mac OS X Requirements #### * Mac OS X 10.4 Tiger or newer * Developer Tools Installed ### Requirements for Contributors ### We welcome patches. If you plan to contribute a patch, you need to build Google Mock and its tests, which has further requirements: * Automake version 1.9 or newer * Autoconf version 2.59 or newer * Libtool / Libtoolize * Python version 2.3 or newer (for running some of the tests and re-generating certain source files from templates) ### Building Google Mock ### #### Using CMake #### If you have CMake available, it is recommended that you follow the [build instructions][gtest_cmakebuild] as described for Google Test. If are using Google Mock with an existing CMake project, the section [Incorporating Into An Existing CMake Project][gtest_incorpcmake] may be of particular interest. To make it work for Google Mock you will need to change target_link_libraries(example gtest_main) to target_link_libraries(example gmock_main) This works because `gmock_main` library is compiled with Google Test. #### Preparing to Build (Unix only) #### If you are using a Unix system and plan to use the GNU Autotools build system to build Google Mock (described below), you'll need to configure it now. To prepare the Autotools build system: cd googlemock autoreconf -fvi To build Google Mock and your tests that use it, you need to tell your build system where to find its headers and source files. The exact way to do it depends on which build system you use, and is usually straightforward. This section shows how you can integrate Google Mock into your existing build system. Suppose you put Google Mock in directory `${GMOCK_DIR}` and Google Test in `${GTEST_DIR}` (the latter is `${GMOCK_DIR}/gtest` by default). To build Google Mock, create a library build target (or a project as called by Visual Studio and Xcode) to compile ${GTEST_DIR}/src/gtest-all.cc and ${GMOCK_DIR}/src/gmock-all.cc with ${GTEST_DIR}/include and ${GMOCK_DIR}/include in the system header search path, and ${GTEST_DIR} and ${GMOCK_DIR} in the normal header search path. Assuming a Linux-like system and gcc, something like the following will do: g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} \ -pthread -c ${GTEST_DIR}/src/gtest-all.cc g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} \ -pthread -c ${GMOCK_DIR}/src/gmock-all.cc ar -rv libgmock.a gtest-all.o gmock-all.o (We need -pthread as Google Test and Google Mock use threads.) Next, you should compile your test source file with ${GTEST\_DIR}/include and ${GMOCK\_DIR}/include in the header search path, and link it with gmock and any other necessary libraries: g++ -isystem ${GTEST_DIR}/include -isystem ${GMOCK_DIR}/include \ -pthread path/to/your_test.cc libgmock.a -o your_test As an example, the make/ directory contains a Makefile that you can use to build Google Mock on systems where GNU make is available (e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google Mock's own tests. Instead, it just builds the Google Mock library and a sample test. You can use it as a starting point for your own build script. If the default settings are correct for your environment, the following commands should succeed: cd ${GMOCK_DIR}/make make ./gmock_test If you see errors, try to tweak the contents of [make/Makefile](make/Makefile) to make them go away. ### Windows ### The msvc/2005 directory contains VC++ 2005 projects and the msvc/2010 directory contains VC++ 2010 projects for building Google Mock and selected tests. Change to the appropriate directory and run "msbuild gmock.sln" to build the library and tests (or open the gmock.sln in the MSVC IDE). If you want to create your own project to use with Google Mock, you'll have to configure it to use the `gmock_config` propety sheet. For that: * Open the Property Manager window (View | Other Windows | Property Manager) * Right-click on your project and select "Add Existing Property Sheet..." * Navigate to `gmock_config.vsprops` or `gmock_config.props` and select it. * In Project Properties | Configuration Properties | General | Additional Include Directories, type /include. ### Tweaking Google Mock ### Google Mock can be used in diverse environments. The default configuration may not work (or may not work well) out of the box in some environments. However, you can easily tweak Google Mock by defining control macros on the compiler command line. Generally, these macros are named like `GTEST_XYZ` and you define them to either 1 or 0 to enable or disable a certain feature. We list the most frequently used macros below. For a complete list, see file [${GTEST\_DIR}/include/gtest/internal/gtest-port.h]( ../googletest/include/gtest/internal/gtest-port.h). ### Choosing a TR1 Tuple Library ### Google Mock uses the C++ Technical Report 1 (TR1) tuple library heavily. Unfortunately TR1 tuple is not yet widely available with all compilers. The good news is that Google Test 1.4.0+ implements a subset of TR1 tuple that's enough for Google Mock's need. Google Mock will automatically use that implementation when the compiler doesn't provide TR1 tuple. Usually you don't need to care about which tuple library Google Test and Google Mock use. However, if your project already uses TR1 tuple, you need to tell Google Test and Google Mock to use the same TR1 tuple library the rest of your project uses, or the two tuple implementations will clash. To do that, add -DGTEST_USE_OWN_TR1_TUPLE=0 to the compiler flags while compiling Google Test, Google Mock, and your tests. If you want to force Google Test and Google Mock to use their own tuple library, just add -DGTEST_USE_OWN_TR1_TUPLE=1 to the compiler flags instead. If you want to use Boost's TR1 tuple library with Google Mock, please refer to the Boost website (http://www.boost.org/) for how to obtain it and set it up. ### As a Shared Library (DLL) ### Google Mock is compact, so most users can build and link it as a static library for the simplicity. Google Mock can be used as a DLL, but the same DLL must contain Google Test as well. See [Google Test's README][gtest_readme] for instructions on how to set up necessary compiler settings. ### Tweaking Google Mock ### Most of Google Test's control macros apply to Google Mock as well. Please see [Google Test's README][gtest_readme] for how to tweak them. ### Upgrading from an Earlier Version ### We strive to keep Google Mock releases backward compatible. Sometimes, though, we have to make some breaking changes for the users' long-term benefits. This section describes what you'll need to do if you are upgrading from an earlier version of Google Mock. #### Upgrading from 1.1.0 or Earlier #### You may need to explicitly enable or disable Google Test's own TR1 tuple library. See the instructions in section "[Choosing a TR1 Tuple Library](../googletest/#choosing-a-tr1-tuple-library)". #### Upgrading from 1.4.0 or Earlier #### On platforms where the pthread library is available, Google Test and Google Mock use it in order to be thread-safe. For this to work, you may need to tweak your compiler and/or linker flags. Please see the "[Multi-threaded Tests](../googletest#multi-threaded-tests )" section in file Google Test's README for what you may need to do. If you have custom matchers defined using `MatcherInterface` or `MakePolymorphicMatcher()`, you'll need to update their definitions to use the new matcher API ( [monomorphic](./docs/CookBook.md#writing-new-monomorphic-matchers), [polymorphic](./docs/CookBook.md#writing-new-polymorphic-matchers)). Matchers defined using `MATCHER()` or `MATCHER_P*()` aren't affected. Happy testing! [gtest_readme]: ../googletest/README.md "googletest" [gtest_cmakebuild]: ../googletest/README.md#using-cmake "Using CMake" [gtest_incorpcmake]: ../googletest/README.md#incorporating-into-an-existing-cmake-project "Incorporating Into An Existing CMake Project" ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/cmake/gmock.pc.in ================================================ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: gmock Description: GoogleMock (without main() function) Version: @PROJECT_VERSION@ URL: https://github.com/google/googletest Libs: -L${libdir} -lgmock @CMAKE_THREAD_LIBS_INIT@ Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@ @CMAKE_THREAD_LIBS_INIT@ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/cmake/gmock_main.pc.in ================================================ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: gmock_main Description: GoogleMock (with main() function) Version: @PROJECT_VERSION@ URL: https://github.com/google/googletest Libs: -L${libdir} -lgmock_main @CMAKE_THREAD_LIBS_INIT@ Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@ @CMAKE_THREAD_LIBS_INIT@ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/configure.ac ================================================ m4_include(../googletest/m4/acx_pthread.m4) AC_INIT([Google C++ Mocking Framework], [1.8.0], [googlemock@googlegroups.com], [gmock]) # Provide various options to initialize the Autoconf and configure processes. AC_PREREQ([2.59]) AC_CONFIG_SRCDIR([./LICENSE]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADERS([build-aux/config.h]) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([scripts/gmock-config], [chmod +x scripts/gmock-config]) # Initialize Automake with various options. We require at least v1.9, prevent # pedantic complaints about package files, and enable various distribution # targets. AM_INIT_AUTOMAKE([1.9 dist-bzip2 dist-zip foreign subdir-objects]) # Check for programs used in building Google Test. AC_PROG_CC AC_PROG_CXX AC_LANG([C++]) AC_PROG_LIBTOOL # TODO(chandlerc@google.com): Currently we aren't running the Python tests # against the interpreter detected by AM_PATH_PYTHON, and so we condition # HAVE_PYTHON by requiring "python" to be in the PATH, and that interpreter's # version to be >= 2.3. This will allow the scripts to use a "/usr/bin/env" # hashbang. PYTHON= # We *do not* allow the user to specify a python interpreter AC_PATH_PROG([PYTHON],[python],[:]) AS_IF([test "$PYTHON" != ":"], [AM_PYTHON_CHECK_VERSION([$PYTHON],[2.3],[:],[PYTHON=":"])]) AM_CONDITIONAL([HAVE_PYTHON],[test "$PYTHON" != ":"]) # TODO(chandlerc@google.com) Check for the necessary system headers. # Configure pthreads. AC_ARG_WITH([pthreads], [AS_HELP_STRING([--with-pthreads], [use pthreads (default is yes)])], [with_pthreads=$withval], [with_pthreads=check]) have_pthreads=no AS_IF([test "x$with_pthreads" != "xno"], [ACX_PTHREAD( [], [AS_IF([test "x$with_pthreads" != "xcheck"], [AC_MSG_FAILURE( [--with-pthreads was specified, but unable to be used])])]) have_pthreads="$acx_pthread_ok"]) AM_CONDITIONAL([HAVE_PTHREADS],[test "x$have_pthreads" == "xyes"]) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_LIBS) # GoogleMock currently has hard dependencies upon GoogleTest above and beyond # running its own test suite, so we both provide our own version in # a subdirectory and provide some logic to use a custom version or a system # installed version. AC_ARG_WITH([gtest], [AS_HELP_STRING([--with-gtest], [Specifies how to find the gtest package. If no arguments are given, the default behavior, a system installed gtest will be used if present, and an internal version built otherwise. If a path is provided, the gtest built or installed at that prefix will be used.])], [], [with_gtest=yes]) AC_ARG_ENABLE([external-gtest], [AS_HELP_STRING([--disable-external-gtest], [Disables any detection or use of a system installed or user provided gtest. Any option to '--with-gtest' is ignored. (Default is enabled.)]) ], [], [enable_external_gtest=yes]) AS_IF([test "x$with_gtest" == "xno"], [AC_MSG_ERROR([dnl Support for GoogleTest was explicitly disabled. Currently GoogleMock has a hard dependency upon GoogleTest to build, please provide a version, or allow GoogleMock to use any installed version and fall back upon its internal version.])]) # Setup various GTEST variables. TODO(chandlerc@google.com): When these are # used below, they should be used such that any pre-existing values always # trump values we set them to, so that they can be used to selectively override # details of the detection process. AC_ARG_VAR([GTEST_CONFIG], [The exact path of Google Test's 'gtest-config' script.]) AC_ARG_VAR([GTEST_CPPFLAGS], [C-like preprocessor flags for Google Test.]) AC_ARG_VAR([GTEST_CXXFLAGS], [C++ compile flags for Google Test.]) AC_ARG_VAR([GTEST_LDFLAGS], [Linker path and option flags for Google Test.]) AC_ARG_VAR([GTEST_LIBS], [Library linking flags for Google Test.]) AC_ARG_VAR([GTEST_VERSION], [The version of Google Test available.]) HAVE_BUILT_GTEST="no" GTEST_MIN_VERSION="1.8.0" AS_IF([test "x${enable_external_gtest}" = "xyes"], [# Begin filling in variables as we are able. AS_IF([test "x${with_gtest}" != "xyes"], [AS_IF([test -x "${with_gtest}/scripts/gtest-config"], [GTEST_CONFIG="${with_gtest}/scripts/gtest-config"], [GTEST_CONFIG="${with_gtest}/bin/gtest-config"]) AS_IF([test -x "${GTEST_CONFIG}"], [], [AC_MSG_ERROR([dnl Unable to locate either a built or installed Google Test at '${with_gtest}'.]) ])]) AS_IF([test -x "${GTEST_CONFIG}"], [], [AC_PATH_PROG([GTEST_CONFIG], [gtest-config])]) AS_IF([test -x "${GTEST_CONFIG}"], [AC_MSG_CHECKING([for Google Test version >= ${GTEST_MIN_VERSION}]) AS_IF([${GTEST_CONFIG} --min-version=${GTEST_MIN_VERSION}], [AC_MSG_RESULT([yes]) HAVE_BUILT_GTEST="yes"], [AC_MSG_RESULT([no])])])]) AS_IF([test "x${HAVE_BUILT_GTEST}" = "xyes"], [GTEST_CPPFLAGS=`${GTEST_CONFIG} --cppflags` GTEST_CXXFLAGS=`${GTEST_CONFIG} --cxxflags` GTEST_LDFLAGS=`${GTEST_CONFIG} --ldflags` GTEST_LIBS=`${GTEST_CONFIG} --libs` GTEST_VERSION=`${GTEST_CONFIG} --version`], [ # GTEST_CONFIG needs to be executable both in a Makefile environment and # in a shell script environment, so resolve an absolute path for it here. GTEST_CONFIG="`pwd -P`/../googletest/scripts/gtest-config" GTEST_CPPFLAGS='-I$(top_srcdir)/../googletest/include' GTEST_CXXFLAGS='-g' GTEST_LDFLAGS='' GTEST_LIBS='$(top_builddir)/../googletest/lib/libgtest.la' GTEST_VERSION="${GTEST_MIN_VERSION}"]) # TODO(chandlerc@google.com) Check the types, structures, and other compiler # and architecture characteristics. # Output the generated files. No further autoconf macros may be used. AC_OUTPUT ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/docs/CheatSheet.md ================================================ # Defining a Mock Class # ## Mocking a Normal Class ## Given ``` class Foo { ... virtual ~Foo(); virtual int GetSize() const = 0; virtual string Describe(const char* name) = 0; virtual string Describe(int type) = 0; virtual bool Process(Bar elem, int count) = 0; }; ``` (note that `~Foo()` **must** be virtual) we can define its mock as ``` #include "gmock/gmock.h" class MockFoo : public Foo { MOCK_CONST_METHOD0(GetSize, int()); MOCK_METHOD1(Describe, string(const char* name)); MOCK_METHOD1(Describe, string(int type)); MOCK_METHOD2(Process, bool(Bar elem, int count)); }; ``` To create a "nice" mock object which ignores all uninteresting calls, or a "strict" mock object, which treats them as failures: ``` NiceMock nice_foo; // The type is a subclass of MockFoo. StrictMock strict_foo; // The type is a subclass of MockFoo. ``` ## Mocking a Class Template ## To mock ``` template class StackInterface { public: ... virtual ~StackInterface(); virtual int GetSize() const = 0; virtual void Push(const Elem& x) = 0; }; ``` (note that `~StackInterface()` **must** be virtual) just append `_T` to the `MOCK_*` macros: ``` template class MockStack : public StackInterface { public: ... MOCK_CONST_METHOD0_T(GetSize, int()); MOCK_METHOD1_T(Push, void(const Elem& x)); }; ``` ## Specifying Calling Conventions for Mock Functions ## If your mock function doesn't use the default calling convention, you can specify it by appending `_WITH_CALLTYPE` to any of the macros described in the previous two sections and supplying the calling convention as the first argument to the macro. For example, ``` MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int n)); MOCK_CONST_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, Bar, int(double x, double y)); ``` where `STDMETHODCALLTYPE` is defined by `` on Windows. # Using Mocks in Tests # The typical flow is: 1. Import the Google Mock names you need to use. All Google Mock names are in the `testing` namespace unless they are macros or otherwise noted. 1. Create the mock objects. 1. Optionally, set the default actions of the mock objects. 1. Set your expectations on the mock objects (How will they be called? What wil they do?). 1. Exercise code that uses the mock objects; if necessary, check the result using [Google Test](../../googletest/) assertions. 1. When a mock objects is destructed, Google Mock automatically verifies that all expectations on it have been satisfied. Here is an example: ``` using ::testing::Return; // #1 TEST(BarTest, DoesThis) { MockFoo foo; // #2 ON_CALL(foo, GetSize()) // #3 .WillByDefault(Return(1)); // ... other default actions ... EXPECT_CALL(foo, Describe(5)) // #4 .Times(3) .WillRepeatedly(Return("Category 5")); // ... other expectations ... EXPECT_EQ("good", MyProductionFunction(&foo)); // #5 } // #6 ``` # Setting Default Actions # Google Mock has a **built-in default action** for any function that returns `void`, `bool`, a numeric value, or a pointer. To customize the default action for functions with return type `T` globally: ``` using ::testing::DefaultValue; // Sets the default value to be returned. T must be CopyConstructible. DefaultValue::Set(value); // Sets a factory. Will be invoked on demand. T must be MoveConstructible. // T MakeT(); DefaultValue::SetFactory(&MakeT); // ... use the mocks ... // Resets the default value. DefaultValue::Clear(); ``` To customize the default action for a particular method, use `ON_CALL()`: ``` ON_CALL(mock_object, method(matchers)) .With(multi_argument_matcher) ? .WillByDefault(action); ``` # Setting Expectations # `EXPECT_CALL()` sets **expectations** on a mock method (How will it be called? What will it do?): ``` EXPECT_CALL(mock_object, method(matchers)) .With(multi_argument_matcher) ? .Times(cardinality) ? .InSequence(sequences) * .After(expectations) * .WillOnce(action) * .WillRepeatedly(action) ? .RetiresOnSaturation(); ? ``` If `Times()` is omitted, the cardinality is assumed to be: * `Times(1)` when there is neither `WillOnce()` nor `WillRepeatedly()`; * `Times(n)` when there are `n WillOnce()`s but no `WillRepeatedly()`, where `n` >= 1; or * `Times(AtLeast(n))` when there are `n WillOnce()`s and a `WillRepeatedly()`, where `n` >= 0. A method with no `EXPECT_CALL()` is free to be invoked _any number of times_, and the default action will be taken each time. # Matchers # A **matcher** matches a _single_ argument. You can use it inside `ON_CALL()` or `EXPECT_CALL()`, or use it to validate a value directly: | `EXPECT_THAT(value, matcher)` | Asserts that `value` matches `matcher`. | |:------------------------------|:----------------------------------------| | `ASSERT_THAT(value, matcher)` | The same as `EXPECT_THAT(value, matcher)`, except that it generates a **fatal** failure. | Built-in matchers (where `argument` is the function argument) are divided into several categories: ## Wildcard ## |`_`|`argument` can be any value of the correct type.| |:--|:-----------------------------------------------| |`A()` or `An()`|`argument` can be any value of type `type`. | ## Generic Comparison ## |`Eq(value)` or `value`|`argument == value`| |:---------------------|:------------------| |`Ge(value)` |`argument >= value`| |`Gt(value)` |`argument > value` | |`Le(value)` |`argument <= value`| |`Lt(value)` |`argument < value` | |`Ne(value)` |`argument != value`| |`IsNull()` |`argument` is a `NULL` pointer (raw or smart).| |`NotNull()` |`argument` is a non-null pointer (raw or smart).| |`VariantWith(m)` |`argument` is `variant<>` that holds the alternative of type T with a value matching `m`.| |`Ref(variable)` |`argument` is a reference to `variable`.| |`TypedEq(value)`|`argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded.| Except `Ref()`, these matchers make a _copy_ of `value` in case it's modified or destructed later. If the compiler complains that `value` doesn't have a public copy constructor, try wrap it in `ByRef()`, e.g. `Eq(ByRef(non_copyable_value))`. If you do that, make sure `non_copyable_value` is not changed afterwards, or the meaning of your matcher will be changed. ## Floating-Point Matchers ## |`DoubleEq(a_double)`|`argument` is a `double` value approximately equal to `a_double`, treating two NaNs as unequal.| |:-------------------|:----------------------------------------------------------------------------------------------| |`FloatEq(a_float)` |`argument` is a `float` value approximately equal to `a_float`, treating two NaNs as unequal. | |`NanSensitiveDoubleEq(a_double)`|`argument` is a `double` value approximately equal to `a_double`, treating two NaNs as equal. | |`NanSensitiveFloatEq(a_float)`|`argument` is a `float` value approximately equal to `a_float`, treating two NaNs as equal. | The above matchers use ULP-based comparison (the same as used in [Google Test](../../googletest/)). They automatically pick a reasonable error bound based on the absolute value of the expected value. `DoubleEq()` and `FloatEq()` conform to the IEEE standard, which requires comparing two NaNs for equality to return false. The `NanSensitive*` version instead treats two NaNs as equal, which is often what a user wants. |`DoubleNear(a_double, max_abs_error)`|`argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as unequal.| |:------------------------------------|:--------------------------------------------------------------------------------------------------------------------| |`FloatNear(a_float, max_abs_error)` |`argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as unequal. | |`NanSensitiveDoubleNear(a_double, max_abs_error)`|`argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as equal. | |`NanSensitiveFloatNear(a_float, max_abs_error)`|`argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as equal. | ## String Matchers ## The `argument` can be either a C string or a C++ string object: |`ContainsRegex(string)`|`argument` matches the given regular expression.| |:----------------------|:-----------------------------------------------| |`EndsWith(suffix)` |`argument` ends with string `suffix`. | |`HasSubstr(string)` |`argument` contains `string` as a sub-string. | |`MatchesRegex(string)` |`argument` matches the given regular expression with the match starting at the first character and ending at the last character.| |`StartsWith(prefix)` |`argument` starts with string `prefix`. | |`StrCaseEq(string)` |`argument` is equal to `string`, ignoring case. | |`StrCaseNe(string)` |`argument` is not equal to `string`, ignoring case.| |`StrEq(string)` |`argument` is equal to `string`. | |`StrNe(string)` |`argument` is not equal to `string`. | `ContainsRegex()` and `MatchesRegex()` use the regular expression syntax defined [here](../../googletest/docs/advanced.md#regular-expression-syntax). `StrCaseEq()`, `StrCaseNe()`, `StrEq()`, and `StrNe()` work for wide strings as well. ## Container Matchers ## Most STL-style containers support `==`, so you can use `Eq(expected_container)` or simply `expected_container` to match a container exactly. If you want to write the elements in-line, match them more flexibly, or get more informative messages, you can use: | `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. | |:-------------------------|:---------------------------------------------------------------------------------------------------------------------------------| | `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. | | `Each(e)` | `argument` is a container where _every_ element matches `e`, which can be either a value or a matcher. | | `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the i-th element matches `ei`, which can be a value or a matcher. 0 to 10 arguments are allowed. | | `ElementsAreArray({ e0, e1, ..., en })`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, or C-style array. | | `IsEmpty()` | `argument` is an empty container (`container.empty()`). | | `Pointwise(m, container)` | `argument` contains the same number of elements as in `container`, and for all i, (the i-th element in `argument`, the i-th element in `container`) match `m`, which is a matcher on 2-tuples. E.g. `Pointwise(Le(), upper_bounds)` verifies that each element in `argument` doesn't exceed the corresponding element in `upper_bounds`. See more detail below. | | `SizeIs(m)` | `argument` is a container whose size matches `m`. E.g. `SizeIs(2)` or `SizeIs(Lt(2))`. | | `UnorderedElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, and under some permutation each element matches an `ei` (for a different `i`), which can be a value or a matcher. 0 to 10 arguments are allowed. | | `UnorderedElementsAreArray({ e0, e1, ..., en })`, `UnorderedElementsAreArray(array)`, or `UnorderedElementsAreArray(array, count)` | The same as `UnorderedElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, or C-style array. | | `WhenSorted(m)` | When `argument` is sorted using the `<` operator, it matches container matcher `m`. E.g. `WhenSorted(ElementsAre(1, 2, 3))` verifies that `argument` contains elements `1`, `2`, and `3`, ignoring order. | | `WhenSortedBy(comparator, m)` | The same as `WhenSorted(m)`, except that the given comparator instead of `<` is used to sort `argument`. E.g. `WhenSortedBy(std::greater(), ElementsAre(3, 2, 1))`. | Notes: * These matchers can also match: 1. a native array passed by reference (e.g. in `Foo(const int (&a)[5])`), and 1. an array passed as a pointer and a count (e.g. in `Bar(const T* buffer, int len)` -- see [Multi-argument Matchers](#Multiargument_Matchers.md)). * The array being matched may be multi-dimensional (i.e. its elements can be arrays). * `m` in `Pointwise(m, ...)` should be a matcher for `::testing::tuple` where `T` and `U` are the element type of the actual container and the expected container, respectively. For example, to compare two `Foo` containers where `Foo` doesn't support `operator==` but has an `Equals()` method, one might write: ``` using ::testing::get; MATCHER(FooEq, "") { return get<0>(arg).Equals(get<1>(arg)); } ... EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos)); ``` ## Member Matchers ## |`Field(&class::field, m)`|`argument.field` (or `argument->field` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_.| |:------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------| |`Key(e)` |`argument.first` matches `e`, which can be either a value or a matcher. E.g. `Contains(Key(Le(5)))` can verify that a `map` contains a key `<= 5`.| |`Pair(m1, m2)` |`argument` is an `std::pair` whose `first` field matches `m1` and `second` field matches `m2`. | |`Property(&class::property, m)`|`argument.property()` (or `argument->property()` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_.| ## Matching the Result of a Function or Functor ## |`ResultOf(f, m)`|`f(argument)` matches matcher `m`, where `f` is a function or functor.| |:---------------|:---------------------------------------------------------------------| ## Pointer Matchers ## |`Pointee(m)`|`argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`.| |:-----------|:-----------------------------------------------------------------------------------------------| |`WhenDynamicCastTo(m)`| when `argument` is passed through `dynamic_cast()`, it matches matcher `m`. | ## Multiargument Matchers ## Technically, all matchers match a _single_ value. A "multi-argument" matcher is just one that matches a _tuple_. The following matchers can be used to match a tuple `(x, y)`: |`Eq()`|`x == y`| |:-----|:-------| |`Ge()`|`x >= y`| |`Gt()`|`x > y` | |`Le()`|`x <= y`| |`Lt()`|`x < y` | |`Ne()`|`x != y`| You can use the following selectors to pick a subset of the arguments (or reorder them) to participate in the matching: |`AllArgs(m)`|Equivalent to `m`. Useful as syntactic sugar in `.With(AllArgs(m))`.| |:-----------|:-------------------------------------------------------------------| |`Args(m)`|The tuple of the `k` selected (using 0-based indices) arguments matches `m`, e.g. `Args<1, 2>(Eq())`.| ## Composite Matchers ## You can make a matcher from one or more other matchers: |`AllOf(m1, m2, ..., mn)`|`argument` matches all of the matchers `m1` to `mn`.| |:-----------------------|:---------------------------------------------------| |`AnyOf(m1, m2, ..., mn)`|`argument` matches at least one of the matchers `m1` to `mn`.| |`Not(m)` |`argument` doesn't match matcher `m`. | ## Adapters for Matchers ## |`MatcherCast(m)`|casts matcher `m` to type `Matcher`.| |:------------------|:--------------------------------------| |`SafeMatcherCast(m)`| [safely casts](CookBook.md#casting-matchers) matcher `m` to type `Matcher`. | |`Truly(predicate)` |`predicate(argument)` returns something considered by C++ to be true, where `predicate` is a function or functor.| ## Matchers as Predicates ## |`Matches(m)(value)`|evaluates to `true` if `value` matches `m`. You can use `Matches(m)` alone as a unary functor.| |:------------------|:---------------------------------------------------------------------------------------------| |`ExplainMatchResult(m, value, result_listener)`|evaluates to `true` if `value` matches `m`, explaining the result to `result_listener`. | |`Value(value, m)` |evaluates to `true` if `value` matches `m`. | ## Defining Matchers ## | `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. | |:-------------------------------------------------|:------------------------------------------------------| | `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a macher `IsDivisibleBy(n)` to match a number divisible by `n`. | | `MATCHER_P2(IsBetween, a, b, std::string(negation ? "isn't" : "is") + " between " + PrintToString(a) + " and " + PrintToString(b)) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. | **Notes:** 1. The `MATCHER*` macros cannot be used inside a function or class. 1. The matcher body must be _purely functional_ (i.e. it cannot have any side effect, and the result must not depend on anything other than the value being matched and the matcher parameters). 1. You can use `PrintToString(x)` to convert a value `x` of any type to a string. ## Matchers as Test Assertions ## |`ASSERT_THAT(expression, m)`|Generates a [fatal failure](../../googletest/docs/primer.md#assertions) if the value of `expression` doesn't match matcher `m`.| |:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------| |`EXPECT_THAT(expression, m)`|Generates a non-fatal failure if the value of `expression` doesn't match matcher `m`. | # Actions # **Actions** specify what a mock function should do when invoked. ## Returning a Value ## |`Return()`|Return from a `void` mock function.| |:---------|:----------------------------------| |`Return(value)`|Return `value`. If the type of `value` is different to the mock function's return type, `value` is converted to the latter type at the time the expectation is set, not when the action is executed.| |`ReturnArg()`|Return the `N`-th (0-based) argument.| |`ReturnNew(a1, ..., ak)`|Return `new T(a1, ..., ak)`; a different object is created each time.| |`ReturnNull()`|Return a null pointer. | |`ReturnPointee(ptr)`|Return the value pointed to by `ptr`.| |`ReturnRef(variable)`|Return a reference to `variable`. | |`ReturnRefOfCopy(value)`|Return a reference to a copy of `value`; the copy lives as long as the action.| ## Side Effects ## |`Assign(&variable, value)`|Assign `value` to variable.| |:-------------------------|:--------------------------| | `DeleteArg()` | Delete the `N`-th (0-based) argument, which must be a pointer. | | `SaveArg(pointer)` | Save the `N`-th (0-based) argument to `*pointer`. | | `SaveArgPointee(pointer)` | Save the value pointed to by the `N`-th (0-based) argument to `*pointer`. | | `SetArgReferee(value)` | Assign value to the variable referenced by the `N`-th (0-based) argument. | |`SetArgPointee(value)` |Assign `value` to the variable pointed by the `N`-th (0-based) argument.| |`SetArgumentPointee(value)`|Same as `SetArgPointee(value)`. Deprecated. Will be removed in v1.7.0.| |`SetArrayArgument(first, last)`|Copies the elements in source range [`first`, `last`) to the array pointed to by the `N`-th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range.| |`SetErrnoAndReturn(error, value)`|Set `errno` to `error` and return `value`.| |`Throw(exception)` |Throws the given exception, which can be any copyable value. Available since v1.1.0.| ## Using a Function or a Functor as an Action ## |`Invoke(f)`|Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor.| |:----------|:-----------------------------------------------------------------------------------------------------------------| |`Invoke(object_pointer, &class::method)`|Invoke the {method on the object with the arguments passed to the mock function. | |`InvokeWithoutArgs(f)`|Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. | |`InvokeWithoutArgs(object_pointer, &class::method)`|Invoke the method on the object, which takes no arguments. | |`InvokeArgument(arg1, arg2, ..., argk)`|Invoke the mock function's `N`-th (0-based) argument, which must be a function or a functor, with the `k` arguments.| The return value of the invoked function is used as the return value of the action. When defining a function or functor to be used with `Invoke*()`, you can declare any unused parameters as `Unused`: ``` double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); } ... EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance)); ``` In `InvokeArgument(...)`, if an argument needs to be passed by reference, wrap it inside `ByRef()`. For example, ``` InvokeArgument<2>(5, string("Hi"), ByRef(foo)) ``` calls the mock function's #2 argument, passing to it `5` and `string("Hi")` by value, and `foo` by reference. ## Default Action ## |`DoDefault()`|Do the default action (specified by `ON_CALL()` or the built-in one).| |:------------|:--------------------------------------------------------------------| **Note:** due to technical reasons, `DoDefault()` cannot be used inside a composite action - trying to do so will result in a run-time error. ## Composite Actions ## |`DoAll(a1, a2, ..., an)`|Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void. | |:-----------------------|:-----------------------------------------------------------------------------------------------------------------------------| |`IgnoreResult(a)` |Perform action `a` and ignore its result. `a` must not return void. | |`WithArg(a)` |Pass the `N`-th (0-based) argument of the mock function to action `a` and perform it. | |`WithArgs(a)`|Pass the selected (0-based) arguments of the mock function to action `a` and perform it. | |`WithoutArgs(a)` |Perform action `a` without any arguments. | ## Defining Actions ## | `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. | |:--------------------------------------|:---------------------------------------------------------------------------------------| | `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. | | `ACTION_Pk(Foo, p1, ..., pk) { statements; }` | Defines a parameterized action `Foo(p1, ..., pk)` to execute the given `statements`. | The `ACTION*` macros cannot be used inside a function or class. # Cardinalities # These are used in `Times()` to specify how many times a mock function will be called: |`AnyNumber()`|The function can be called any number of times.| |:------------|:----------------------------------------------| |`AtLeast(n)` |The call is expected at least `n` times. | |`AtMost(n)` |The call is expected at most `n` times. | |`Between(m, n)`|The call is expected between `m` and `n` (inclusive) times.| |`Exactly(n) or n`|The call is expected exactly `n` times. In particular, the call should never happen when `n` is 0.| # Expectation Order # By default, the expectations can be matched in _any_ order. If some or all expectations must be matched in a given order, there are two ways to specify it. They can be used either independently or together. ## The After Clause ## ``` using ::testing::Expectation; ... Expectation init_x = EXPECT_CALL(foo, InitX()); Expectation init_y = EXPECT_CALL(foo, InitY()); EXPECT_CALL(foo, Bar()) .After(init_x, init_y); ``` says that `Bar()` can be called only after both `InitX()` and `InitY()` have been called. If you don't know how many pre-requisites an expectation has when you write it, you can use an `ExpectationSet` to collect them: ``` using ::testing::ExpectationSet; ... ExpectationSet all_inits; for (int i = 0; i < element_count; i++) { all_inits += EXPECT_CALL(foo, InitElement(i)); } EXPECT_CALL(foo, Bar()) .After(all_inits); ``` says that `Bar()` can be called only after all elements have been initialized (but we don't care about which elements get initialized before the others). Modifying an `ExpectationSet` after using it in an `.After()` doesn't affect the meaning of the `.After()`. ## Sequences ## When you have a long chain of sequential expectations, it's easier to specify the order using **sequences**, which don't require you to given each expectation in the chain a different name. All expected
calls
in the same sequence must occur in the order they are specified. ``` using ::testing::Sequence; Sequence s1, s2; ... EXPECT_CALL(foo, Reset()) .InSequence(s1, s2) .WillOnce(Return(true)); EXPECT_CALL(foo, GetSize()) .InSequence(s1) .WillOnce(Return(1)); EXPECT_CALL(foo, Describe(A())) .InSequence(s2) .WillOnce(Return("dummy")); ``` says that `Reset()` must be called before _both_ `GetSize()` _and_ `Describe()`, and the latter two can occur in any order. To put many expectations in a sequence conveniently: ``` using ::testing::InSequence; { InSequence dummy; EXPECT_CALL(...)...; EXPECT_CALL(...)...; ... EXPECT_CALL(...)...; } ``` says that all expected calls in the scope of `dummy` must occur in strict order. The name `dummy` is irrelevant.) # Verifying and Resetting a Mock # Google Mock will verify the expectations on a mock object when it is destructed, or you can do it earlier: ``` using ::testing::Mock; ... // Verifies and removes the expectations on mock_obj; // returns true iff successful. Mock::VerifyAndClearExpectations(&mock_obj); ... // Verifies and removes the expectations on mock_obj; // also removes the default actions set by ON_CALL(); // returns true iff successful. Mock::VerifyAndClear(&mock_obj); ``` You can also tell Google Mock that a mock object can be leaked and doesn't need to be verified: ``` Mock::AllowLeak(&mock_obj); ``` # Mock Classes # Google Mock defines a convenient mock class template ``` class MockFunction { public: MOCK_METHODn(Call, R(A1, ..., An)); }; ``` See this [recipe](CookBook.md#using-check-points) for one application of it. # Flags # | `--gmock_catch_leaked_mocks=0` | Don't report leaked mock objects as failures. | |:-------------------------------|:----------------------------------------------| | `--gmock_verbose=LEVEL` | Sets the default verbosity level (`info`, `warning`, or `error`) of Google Mock messages. | ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/docs/CookBook.md ================================================ You can find recipes for using Google Mock here. If you haven't yet, please read the [ForDummies](ForDummies.md) document first to make sure you understand the basics. **Note:** Google Mock lives in the `testing` name space. For readability, it is recommended to write `using ::testing::Foo;` once in your file before using the name `Foo` defined by Google Mock. We omit such `using` statements in this page for brevity, but you should do it in your own code. # Creating Mock Classes # ## Mocking Private or Protected Methods ## You must always put a mock method definition (`MOCK_METHOD*`) in a `public:` section of the mock class, regardless of the method being mocked being `public`, `protected`, or `private` in the base class. This allows `ON_CALL` and `EXPECT_CALL` to reference the mock function from outside of the mock class. (Yes, C++ allows a subclass to specify a different access level than the base class on a virtual function.) Example: ``` class Foo { public: ... virtual bool Transform(Gadget* g) = 0; protected: virtual void Resume(); private: virtual int GetTimeOut(); }; class MockFoo : public Foo { public: ... MOCK_METHOD1(Transform, bool(Gadget* g)); // The following must be in the public section, even though the // methods are protected or private in the base class. MOCK_METHOD0(Resume, void()); MOCK_METHOD0(GetTimeOut, int()); }; ``` ## Mocking Overloaded Methods ## You can mock overloaded functions as usual. No special attention is required: ``` class Foo { ... // Must be virtual as we'll inherit from Foo. virtual ~Foo(); // Overloaded on the types and/or numbers of arguments. virtual int Add(Element x); virtual int Add(int times, Element x); // Overloaded on the const-ness of this object. virtual Bar& GetBar(); virtual const Bar& GetBar() const; }; class MockFoo : public Foo { ... MOCK_METHOD1(Add, int(Element x)); MOCK_METHOD2(Add, int(int times, Element x); MOCK_METHOD0(GetBar, Bar&()); MOCK_CONST_METHOD0(GetBar, const Bar&()); }; ``` **Note:** if you don't mock all versions of the overloaded method, the compiler will give you a warning about some methods in the base class being hidden. To fix that, use `using` to bring them in scope: ``` class MockFoo : public Foo { ... using Foo::Add; MOCK_METHOD1(Add, int(Element x)); // We don't want to mock int Add(int times, Element x); ... }; ``` ## Mocking Class Templates ## To mock a class template, append `_T` to the `MOCK_*` macros: ``` template class StackInterface { ... // Must be virtual as we'll inherit from StackInterface. virtual ~StackInterface(); virtual int GetSize() const = 0; virtual void Push(const Elem& x) = 0; }; template class MockStack : public StackInterface { ... MOCK_CONST_METHOD0_T(GetSize, int()); MOCK_METHOD1_T(Push, void(const Elem& x)); }; ``` ## Mocking Nonvirtual Methods ## Google Mock can mock non-virtual functions to be used in what we call _hi-perf dependency injection_. In this case, instead of sharing a common base class with the real class, your mock class will be _unrelated_ to the real class, but contain methods with the same signatures. The syntax for mocking non-virtual methods is the _same_ as mocking virtual methods: ``` // A simple packet stream class. None of its members is virtual. class ConcretePacketStream { public: void AppendPacket(Packet* new_packet); const Packet* GetPacket(size_t packet_number) const; size_t NumberOfPackets() const; ... }; // A mock packet stream class. It inherits from no other, but defines // GetPacket() and NumberOfPackets(). class MockPacketStream { public: MOCK_CONST_METHOD1(GetPacket, const Packet*(size_t packet_number)); MOCK_CONST_METHOD0(NumberOfPackets, size_t()); ... }; ``` Note that the mock class doesn't define `AppendPacket()`, unlike the real class. That's fine as long as the test doesn't need to call it. Next, you need a way to say that you want to use `ConcretePacketStream` in production code and to use `MockPacketStream` in tests. Since the functions are not virtual and the two classes are unrelated, you must specify your choice at _compile time_ (as opposed to run time). One way to do it is to templatize your code that needs to use a packet stream. More specifically, you will give your code a template type argument for the type of the packet stream. In production, you will instantiate your template with `ConcretePacketStream` as the type argument. In tests, you will instantiate the same template with `MockPacketStream`. For example, you may write: ``` template void CreateConnection(PacketStream* stream) { ... } template class PacketReader { public: void ReadPackets(PacketStream* stream, size_t packet_num); }; ``` Then you can use `CreateConnection()` and `PacketReader` in production code, and use `CreateConnection()` and `PacketReader` in tests. ``` MockPacketStream mock_stream; EXPECT_CALL(mock_stream, ...)...; .. set more expectations on mock_stream ... PacketReader reader(&mock_stream); ... exercise reader ... ``` ## Mocking Free Functions ## It's possible to use Google Mock to mock a free function (i.e. a C-style function or a static method). You just need to rewrite your code to use an interface (abstract class). Instead of calling a free function (say, `OpenFile`) directly, introduce an interface for it and have a concrete subclass that calls the free function: ``` class FileInterface { public: ... virtual bool Open(const char* path, const char* mode) = 0; }; class File : public FileInterface { public: ... virtual bool Open(const char* path, const char* mode) { return OpenFile(path, mode); } }; ``` Your code should talk to `FileInterface` to open a file. Now it's easy to mock out the function. This may seem much hassle, but in practice you often have multiple related functions that you can put in the same interface, so the per-function syntactic overhead will be much lower. If you are concerned about the performance overhead incurred by virtual functions, and profiling confirms your concern, you can combine this with the recipe for [mocking non-virtual methods](#mocking-nonvirtual-methods). ## The Nice, the Strict, and the Naggy ## If a mock method has no `EXPECT_CALL` spec but is called, Google Mock will print a warning about the "uninteresting call". The rationale is: * New methods may be added to an interface after a test is written. We shouldn't fail a test just because a method it doesn't know about is called. * However, this may also mean there's a bug in the test, so Google Mock shouldn't be silent either. If the user believes these calls are harmless, they can add an `EXPECT_CALL()` to suppress the warning. However, sometimes you may want to suppress all "uninteresting call" warnings, while sometimes you may want the opposite, i.e. to treat all of them as errors. Google Mock lets you make the decision on a per-mock-object basis. Suppose your test uses a mock class `MockFoo`: ``` TEST(...) { MockFoo mock_foo; EXPECT_CALL(mock_foo, DoThis()); ... code that uses mock_foo ... } ``` If a method of `mock_foo` other than `DoThis()` is called, it will be reported by Google Mock as a warning. However, if you rewrite your test to use `NiceMock` instead, the warning will be gone, resulting in a cleaner test output: ``` using ::testing::NiceMock; TEST(...) { NiceMock mock_foo; EXPECT_CALL(mock_foo, DoThis()); ... code that uses mock_foo ... } ``` `NiceMock` is a subclass of `MockFoo`, so it can be used wherever `MockFoo` is accepted. It also works if `MockFoo`'s constructor takes some arguments, as `NiceMock` "inherits" `MockFoo`'s constructors: ``` using ::testing::NiceMock; TEST(...) { NiceMock mock_foo(5, "hi"); // Calls MockFoo(5, "hi"). EXPECT_CALL(mock_foo, DoThis()); ... code that uses mock_foo ... } ``` The usage of `StrictMock` is similar, except that it makes all uninteresting calls failures: ``` using ::testing::StrictMock; TEST(...) { StrictMock mock_foo; EXPECT_CALL(mock_foo, DoThis()); ... code that uses mock_foo ... // The test will fail if a method of mock_foo other than DoThis() // is called. } ``` There are some caveats though (I don't like them just as much as the next guy, but sadly they are side effects of C++'s limitations): 1. `NiceMock` and `StrictMock` only work for mock methods defined using the `MOCK_METHOD*` family of macros **directly** in the `MockFoo` class. If a mock method is defined in a **base class** of `MockFoo`, the "nice" or "strict" modifier may not affect it, depending on the compiler. In particular, nesting `NiceMock` and `StrictMock` (e.g. `NiceMock >`) is **not** supported. 1. The constructors of the base mock (`MockFoo`) cannot have arguments passed by non-const reference, which happens to be banned by the [Google C++ style guide](https://google.github.io/styleguide/cppguide.html). 1. During the constructor or destructor of `MockFoo`, the mock object is _not_ nice or strict. This may cause surprises if the constructor or destructor calls a mock method on `this` object. (This behavior, however, is consistent with C++'s general rule: if a constructor or destructor calls a virtual method of `this` object, that method is treated as non-virtual. In other words, to the base class's constructor or destructor, `this` object behaves like an instance of the base class, not the derived class. This rule is required for safety. Otherwise a base constructor may use members of a derived class before they are initialized, or a base destructor may use members of a derived class after they have been destroyed.) Finally, you should be **very cautious** about when to use naggy or strict mocks, as they tend to make tests more brittle and harder to maintain. When you refactor your code without changing its externally visible behavior, ideally you should't need to update any tests. If your code interacts with a naggy mock, however, you may start to get spammed with warnings as the result of your change. Worse, if your code interacts with a strict mock, your tests may start to fail and you'll be forced to fix them. Our general recommendation is to use nice mocks (not yet the default) most of the time, use naggy mocks (the current default) when developing or debugging tests, and use strict mocks only as the last resort. ## Simplifying the Interface without Breaking Existing Code ## Sometimes a method has a long list of arguments that is mostly uninteresting. For example, ``` class LogSink { public: ... virtual void send(LogSeverity severity, const char* full_filename, const char* base_filename, int line, const struct tm* tm_time, const char* message, size_t message_len) = 0; }; ``` This method's argument list is lengthy and hard to work with (let's say that the `message` argument is not even 0-terminated). If we mock it as is, using the mock will be awkward. If, however, we try to simplify this interface, we'll need to fix all clients depending on it, which is often infeasible. The trick is to re-dispatch the method in the mock class: ``` class ScopedMockLog : public LogSink { public: ... virtual void send(LogSeverity severity, const char* full_filename, const char* base_filename, int line, const tm* tm_time, const char* message, size_t message_len) { // We are only interested in the log severity, full file name, and // log message. Log(severity, full_filename, std::string(message, message_len)); } // Implements the mock method: // // void Log(LogSeverity severity, // const string& file_path, // const string& message); MOCK_METHOD3(Log, void(LogSeverity severity, const string& file_path, const string& message)); }; ``` By defining a new mock method with a trimmed argument list, we make the mock class much more user-friendly. ## Alternative to Mocking Concrete Classes ## Often you may find yourself using classes that don't implement interfaces. In order to test your code that uses such a class (let's call it `Concrete`), you may be tempted to make the methods of `Concrete` virtual and then mock it. Try not to do that. Making a non-virtual function virtual is a big decision. It creates an extension point where subclasses can tweak your class' behavior. This weakens your control on the class because now it's harder to maintain the class' invariants. You should make a function virtual only when there is a valid reason for a subclass to override it. Mocking concrete classes directly is problematic as it creates a tight coupling between the class and the tests - any small change in the class may invalidate your tests and make test maintenance a pain. To avoid such problems, many programmers have been practicing "coding to interfaces": instead of talking to the `Concrete` class, your code would define an interface and talk to it. Then you implement that interface as an adaptor on top of `Concrete`. In tests, you can easily mock that interface to observe how your code is doing. This technique incurs some overhead: * You pay the cost of virtual function calls (usually not a problem). * There is more abstraction for the programmers to learn. However, it can also bring significant benefits in addition to better testability: * `Concrete`'s API may not fit your problem domain very well, as you may not be the only client it tries to serve. By designing your own interface, you have a chance to tailor it to your need - you may add higher-level functionalities, rename stuff, etc instead of just trimming the class. This allows you to write your code (user of the interface) in a more natural way, which means it will be more readable, more maintainable, and you'll be more productive. * If `Concrete`'s implementation ever has to change, you don't have to rewrite everywhere it is used. Instead, you can absorb the change in your implementation of the interface, and your other code and tests will be insulated from this change. Some people worry that if everyone is practicing this technique, they will end up writing lots of redundant code. This concern is totally understandable. However, there are two reasons why it may not be the case: * Different projects may need to use `Concrete` in different ways, so the best interfaces for them will be different. Therefore, each of them will have its own domain-specific interface on top of `Concrete`, and they will not be the same code. * If enough projects want to use the same interface, they can always share it, just like they have been sharing `Concrete`. You can check in the interface and the adaptor somewhere near `Concrete` (perhaps in a `contrib` sub-directory) and let many projects use it. You need to weigh the pros and cons carefully for your particular problem, but I'd like to assure you that the Java community has been practicing this for a long time and it's a proven effective technique applicable in a wide variety of situations. :-) ## Delegating Calls to a Fake ## Some times you have a non-trivial fake implementation of an interface. For example: ``` class Foo { public: virtual ~Foo() {} virtual char DoThis(int n) = 0; virtual void DoThat(const char* s, int* p) = 0; }; class FakeFoo : public Foo { public: virtual char DoThis(int n) { return (n > 0) ? '+' : (n < 0) ? '-' : '0'; } virtual void DoThat(const char* s, int* p) { *p = strlen(s); } }; ``` Now you want to mock this interface such that you can set expectations on it. However, you also want to use `FakeFoo` for the default behavior, as duplicating it in the mock object is, well, a lot of work. When you define the mock class using Google Mock, you can have it delegate its default action to a fake class you already have, using this pattern: ``` using ::testing::_; using ::testing::Invoke; class MockFoo : public Foo { public: // Normal mock method definitions using Google Mock. MOCK_METHOD1(DoThis, char(int n)); MOCK_METHOD2(DoThat, void(const char* s, int* p)); // Delegates the default actions of the methods to a FakeFoo object. // This must be called *before* the custom ON_CALL() statements. void DelegateToFake() { ON_CALL(*this, DoThis(_)) .WillByDefault(Invoke(&fake_, &FakeFoo::DoThis)); ON_CALL(*this, DoThat(_, _)) .WillByDefault(Invoke(&fake_, &FakeFoo::DoThat)); } private: FakeFoo fake_; // Keeps an instance of the fake in the mock. }; ``` With that, you can use `MockFoo` in your tests as usual. Just remember that if you don't explicitly set an action in an `ON_CALL()` or `EXPECT_CALL()`, the fake will be called upon to do it: ``` using ::testing::_; TEST(AbcTest, Xyz) { MockFoo foo; foo.DelegateToFake(); // Enables the fake for delegation. // Put your ON_CALL(foo, ...)s here, if any. // No action specified, meaning to use the default action. EXPECT_CALL(foo, DoThis(5)); EXPECT_CALL(foo, DoThat(_, _)); int n = 0; EXPECT_EQ('+', foo.DoThis(5)); // FakeFoo::DoThis() is invoked. foo.DoThat("Hi", &n); // FakeFoo::DoThat() is invoked. EXPECT_EQ(2, n); } ``` **Some tips:** * If you want, you can still override the default action by providing your own `ON_CALL()` or using `.WillOnce()` / `.WillRepeatedly()` in `EXPECT_CALL()`. * In `DelegateToFake()`, you only need to delegate the methods whose fake implementation you intend to use. * The general technique discussed here works for overloaded methods, but you'll need to tell the compiler which version you mean. To disambiguate a mock function (the one you specify inside the parentheses of `ON_CALL()`), see the "Selecting Between Overloaded Functions" section on this page; to disambiguate a fake function (the one you place inside `Invoke()`), use a `static_cast` to specify the function's type. For instance, if class `Foo` has methods `char DoThis(int n)` and `bool DoThis(double x) const`, and you want to invoke the latter, you need to write `Invoke(&fake_, static_cast(&FakeFoo::DoThis))` instead of `Invoke(&fake_, &FakeFoo::DoThis)` (The strange-looking thing inside the angled brackets of `static_cast` is the type of a function pointer to the second `DoThis()` method.). * Having to mix a mock and a fake is often a sign of something gone wrong. Perhaps you haven't got used to the interaction-based way of testing yet. Or perhaps your interface is taking on too many roles and should be split up. Therefore, **don't abuse this**. We would only recommend to do it as an intermediate step when you are refactoring your code. Regarding the tip on mixing a mock and a fake, here's an example on why it may be a bad sign: Suppose you have a class `System` for low-level system operations. In particular, it does file and I/O operations. And suppose you want to test how your code uses `System` to do I/O, and you just want the file operations to work normally. If you mock out the entire `System` class, you'll have to provide a fake implementation for the file operation part, which suggests that `System` is taking on too many roles. Instead, you can define a `FileOps` interface and an `IOOps` interface and split `System`'s functionalities into the two. Then you can mock `IOOps` without mocking `FileOps`. ## Delegating Calls to a Real Object ## When using testing doubles (mocks, fakes, stubs, and etc), sometimes their behaviors will differ from those of the real objects. This difference could be either intentional (as in simulating an error such that you can test the error handling code) or unintentional. If your mocks have different behaviors than the real objects by mistake, you could end up with code that passes the tests but fails in production. You can use the _delegating-to-real_ technique to ensure that your mock has the same behavior as the real object while retaining the ability to validate calls. This technique is very similar to the delegating-to-fake technique, the difference being that we use a real object instead of a fake. Here's an example: ``` using ::testing::_; using ::testing::AtLeast; using ::testing::Invoke; class MockFoo : public Foo { public: MockFoo() { // By default, all calls are delegated to the real object. ON_CALL(*this, DoThis()) .WillByDefault(Invoke(&real_, &Foo::DoThis)); ON_CALL(*this, DoThat(_)) .WillByDefault(Invoke(&real_, &Foo::DoThat)); ... } MOCK_METHOD0(DoThis, ...); MOCK_METHOD1(DoThat, ...); ... private: Foo real_; }; ... MockFoo mock; EXPECT_CALL(mock, DoThis()) .Times(3); EXPECT_CALL(mock, DoThat("Hi")) .Times(AtLeast(1)); ... use mock in test ... ``` With this, Google Mock will verify that your code made the right calls (with the right arguments, in the right order, called the right number of times, etc), and a real object will answer the calls (so the behavior will be the same as in production). This gives you the best of both worlds. ## Delegating Calls to a Parent Class ## Ideally, you should code to interfaces, whose methods are all pure virtual. In reality, sometimes you do need to mock a virtual method that is not pure (i.e, it already has an implementation). For example: ``` class Foo { public: virtual ~Foo(); virtual void Pure(int n) = 0; virtual int Concrete(const char* str) { ... } }; class MockFoo : public Foo { public: // Mocking a pure method. MOCK_METHOD1(Pure, void(int n)); // Mocking a concrete method. Foo::Concrete() is shadowed. MOCK_METHOD1(Concrete, int(const char* str)); }; ``` Sometimes you may want to call `Foo::Concrete()` instead of `MockFoo::Concrete()`. Perhaps you want to do it as part of a stub action, or perhaps your test doesn't need to mock `Concrete()` at all (but it would be oh-so painful to have to define a new mock class whenever you don't need to mock one of its methods). The trick is to leave a back door in your mock class for accessing the real methods in the base class: ``` class MockFoo : public Foo { public: // Mocking a pure method. MOCK_METHOD1(Pure, void(int n)); // Mocking a concrete method. Foo::Concrete() is shadowed. MOCK_METHOD1(Concrete, int(const char* str)); // Use this to call Concrete() defined in Foo. int FooConcrete(const char* str) { return Foo::Concrete(str); } }; ``` Now, you can call `Foo::Concrete()` inside an action by: ``` using ::testing::_; using ::testing::Invoke; ... EXPECT_CALL(foo, Concrete(_)) .WillOnce(Invoke(&foo, &MockFoo::FooConcrete)); ``` or tell the mock object that you don't want to mock `Concrete()`: ``` using ::testing::Invoke; ... ON_CALL(foo, Concrete(_)) .WillByDefault(Invoke(&foo, &MockFoo::FooConcrete)); ``` (Why don't we just write `Invoke(&foo, &Foo::Concrete)`? If you do that, `MockFoo::Concrete()` will be called (and cause an infinite recursion) since `Foo::Concrete()` is virtual. That's just how C++ works.) # Using Matchers # ## Matching Argument Values Exactly ## You can specify exactly which arguments a mock method is expecting: ``` using ::testing::Return; ... EXPECT_CALL(foo, DoThis(5)) .WillOnce(Return('a')); EXPECT_CALL(foo, DoThat("Hello", bar)); ``` ## Using Simple Matchers ## You can use matchers to match arguments that have a certain property: ``` using ::testing::Ge; using ::testing::NotNull; using ::testing::Return; ... EXPECT_CALL(foo, DoThis(Ge(5))) // The argument must be >= 5. .WillOnce(Return('a')); EXPECT_CALL(foo, DoThat("Hello", NotNull())); // The second argument must not be NULL. ``` A frequently used matcher is `_`, which matches anything: ``` using ::testing::_; using ::testing::NotNull; ... EXPECT_CALL(foo, DoThat(_, NotNull())); ``` ## Combining Matchers ## You can build complex matchers from existing ones using `AllOf()`, `AnyOf()`, and `Not()`: ``` using ::testing::AllOf; using ::testing::Gt; using ::testing::HasSubstr; using ::testing::Ne; using ::testing::Not; ... // The argument must be > 5 and != 10. EXPECT_CALL(foo, DoThis(AllOf(Gt(5), Ne(10)))); // The first argument must not contain sub-string "blah". EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), NULL)); ``` ## Casting Matchers ## Google Mock matchers are statically typed, meaning that the compiler can catch your mistake if you use a matcher of the wrong type (for example, if you use `Eq(5)` to match a `string` argument). Good for you! Sometimes, however, you know what you're doing and want the compiler to give you some slack. One example is that you have a matcher for `long` and the argument you want to match is `int`. While the two types aren't exactly the same, there is nothing really wrong with using a `Matcher` to match an `int` - after all, we can first convert the `int` argument to a `long` before giving it to the matcher. To support this need, Google Mock gives you the `SafeMatcherCast(m)` function. It casts a matcher `m` to type `Matcher`. To ensure safety, Google Mock checks that (let `U` be the type `m` accepts): 1. Type `T` can be implicitly cast to type `U`; 1. When both `T` and `U` are built-in arithmetic types (`bool`, integers, and floating-point numbers), the conversion from `T` to `U` is not lossy (in other words, any value representable by `T` can also be represented by `U`); and 1. When `U` is a reference, `T` must also be a reference (as the underlying matcher may be interested in the address of the `U` value). The code won't compile if any of these conditions aren't met. Here's one example: ``` using ::testing::SafeMatcherCast; // A base class and a child class. class Base { ... }; class Derived : public Base { ... }; class MockFoo : public Foo { public: MOCK_METHOD1(DoThis, void(Derived* derived)); }; ... MockFoo foo; // m is a Matcher we got from somewhere. EXPECT_CALL(foo, DoThis(SafeMatcherCast(m))); ``` If you find `SafeMatcherCast(m)` too limiting, you can use a similar function `MatcherCast(m)`. The difference is that `MatcherCast` works as long as you can `static_cast` type `T` to type `U`. `MatcherCast` essentially lets you bypass C++'s type system (`static_cast` isn't always safe as it could throw away information, for example), so be careful not to misuse/abuse it. ## Selecting Between Overloaded Functions ## If you expect an overloaded function to be called, the compiler may need some help on which overloaded version it is. To disambiguate functions overloaded on the const-ness of this object, use the `Const()` argument wrapper. ``` using ::testing::ReturnRef; class MockFoo : public Foo { ... MOCK_METHOD0(GetBar, Bar&()); MOCK_CONST_METHOD0(GetBar, const Bar&()); }; ... MockFoo foo; Bar bar1, bar2; EXPECT_CALL(foo, GetBar()) // The non-const GetBar(). .WillOnce(ReturnRef(bar1)); EXPECT_CALL(Const(foo), GetBar()) // The const GetBar(). .WillOnce(ReturnRef(bar2)); ``` (`Const()` is defined by Google Mock and returns a `const` reference to its argument.) To disambiguate overloaded functions with the same number of arguments but different argument types, you may need to specify the exact type of a matcher, either by wrapping your matcher in `Matcher()`, or using a matcher whose type is fixed (`TypedEq`, `An()`, etc): ``` using ::testing::An; using ::testing::Lt; using ::testing::Matcher; using ::testing::TypedEq; class MockPrinter : public Printer { public: MOCK_METHOD1(Print, void(int n)); MOCK_METHOD1(Print, void(char c)); }; TEST(PrinterTest, Print) { MockPrinter printer; EXPECT_CALL(printer, Print(An())); // void Print(int); EXPECT_CALL(printer, Print(Matcher(Lt(5)))); // void Print(int); EXPECT_CALL(printer, Print(TypedEq('a'))); // void Print(char); printer.Print(3); printer.Print(6); printer.Print('a'); } ``` ## Performing Different Actions Based on the Arguments ## When a mock method is called, the _last_ matching expectation that's still active will be selected (think "newer overrides older"). So, you can make a method do different things depending on its argument values like this: ``` using ::testing::_; using ::testing::Lt; using ::testing::Return; ... // The default case. EXPECT_CALL(foo, DoThis(_)) .WillRepeatedly(Return('b')); // The more specific case. EXPECT_CALL(foo, DoThis(Lt(5))) .WillRepeatedly(Return('a')); ``` Now, if `foo.DoThis()` is called with a value less than 5, `'a'` will be returned; otherwise `'b'` will be returned. ## Matching Multiple Arguments as a Whole ## Sometimes it's not enough to match the arguments individually. For example, we may want to say that the first argument must be less than the second argument. The `With()` clause allows us to match all arguments of a mock function as a whole. For example, ``` using ::testing::_; using ::testing::Lt; using ::testing::Ne; ... EXPECT_CALL(foo, InRange(Ne(0), _)) .With(Lt()); ``` says that the first argument of `InRange()` must not be 0, and must be less than the second argument. The expression inside `With()` must be a matcher of type `Matcher< ::testing::tuple >`, where `A1`, ..., `An` are the types of the function arguments. You can also write `AllArgs(m)` instead of `m` inside `.With()`. The two forms are equivalent, but `.With(AllArgs(Lt()))` is more readable than `.With(Lt())`. You can use `Args(m)` to match the `n` selected arguments (as a tuple) against `m`. For example, ``` using ::testing::_; using ::testing::AllOf; using ::testing::Args; using ::testing::Lt; ... EXPECT_CALL(foo, Blah(_, _, _)) .With(AllOf(Args<0, 1>(Lt()), Args<1, 2>(Lt()))); ``` says that `Blah()` will be called with arguments `x`, `y`, and `z` where `x < y < z`. As a convenience and example, Google Mock provides some matchers for 2-tuples, including the `Lt()` matcher above. See the [CheatSheet](CheatSheet.md) for the complete list. Note that if you want to pass the arguments to a predicate of your own (e.g. `.With(Args<0, 1>(Truly(&MyPredicate)))`), that predicate MUST be written to take a `::testing::tuple` as its argument; Google Mock will pass the `n` selected arguments as _one_ single tuple to the predicate. ## Using Matchers as Predicates ## Have you noticed that a matcher is just a fancy predicate that also knows how to describe itself? Many existing algorithms take predicates as arguments (e.g. those defined in STL's `` header), and it would be a shame if Google Mock matchers are not allowed to participate. Luckily, you can use a matcher where a unary predicate functor is expected by wrapping it inside the `Matches()` function. For example, ``` #include #include std::vector v; ... // How many elements in v are >= 10? const int count = count_if(v.begin(), v.end(), Matches(Ge(10))); ``` Since you can build complex matchers from simpler ones easily using Google Mock, this gives you a way to conveniently construct composite predicates (doing the same using STL's `` header is just painful). For example, here's a predicate that's satisfied by any number that is >= 0, <= 100, and != 50: ``` Matches(AllOf(Ge(0), Le(100), Ne(50))) ``` ## Using Matchers in Google Test Assertions ## Since matchers are basically predicates that also know how to describe themselves, there is a way to take advantage of them in [Google Test](../../googletest/) assertions. It's called `ASSERT_THAT` and `EXPECT_THAT`: ``` ASSERT_THAT(value, matcher); // Asserts that value matches matcher. EXPECT_THAT(value, matcher); // The non-fatal version. ``` For example, in a Google Test test you can write: ``` #include "gmock/gmock.h" using ::testing::AllOf; using ::testing::Ge; using ::testing::Le; using ::testing::MatchesRegex; using ::testing::StartsWith; ... EXPECT_THAT(Foo(), StartsWith("Hello")); EXPECT_THAT(Bar(), MatchesRegex("Line \\d+")); ASSERT_THAT(Baz(), AllOf(Ge(5), Le(10))); ``` which (as you can probably guess) executes `Foo()`, `Bar()`, and `Baz()`, and verifies that: * `Foo()` returns a string that starts with `"Hello"`. * `Bar()` returns a string that matches regular expression `"Line \\d+"`. * `Baz()` returns a number in the range [5, 10]. The nice thing about these macros is that _they read like English_. They generate informative messages too. For example, if the first `EXPECT_THAT()` above fails, the message will be something like: ``` Value of: Foo() Actual: "Hi, world!" Expected: starts with "Hello" ``` **Credit:** The idea of `(ASSERT|EXPECT)_THAT` was stolen from the [Hamcrest](https://github.com/hamcrest/) project, which adds `assertThat()` to JUnit. ## Using Predicates as Matchers ## Google Mock provides a built-in set of matchers. In case you find them lacking, you can use an arbitray unary predicate function or functor as a matcher - as long as the predicate accepts a value of the type you want. You do this by wrapping the predicate inside the `Truly()` function, for example: ``` using ::testing::Truly; int IsEven(int n) { return (n % 2) == 0 ? 1 : 0; } ... // Bar() must be called with an even number. EXPECT_CALL(foo, Bar(Truly(IsEven))); ``` Note that the predicate function / functor doesn't have to return `bool`. It works as long as the return value can be used as the condition in statement `if (condition) ...`. ## Matching Arguments that Are Not Copyable ## When you do an `EXPECT_CALL(mock_obj, Foo(bar))`, Google Mock saves away a copy of `bar`. When `Foo()` is called later, Google Mock compares the argument to `Foo()` with the saved copy of `bar`. This way, you don't need to worry about `bar` being modified or destroyed after the `EXPECT_CALL()` is executed. The same is true when you use matchers like `Eq(bar)`, `Le(bar)`, and so on. But what if `bar` cannot be copied (i.e. has no copy constructor)? You could define your own matcher function and use it with `Truly()`, as the previous couple of recipes have shown. Or, you may be able to get away from it if you can guarantee that `bar` won't be changed after the `EXPECT_CALL()` is executed. Just tell Google Mock that it should save a reference to `bar`, instead of a copy of it. Here's how: ``` using ::testing::Eq; using ::testing::ByRef; using ::testing::Lt; ... // Expects that Foo()'s argument == bar. EXPECT_CALL(mock_obj, Foo(Eq(ByRef(bar)))); // Expects that Foo()'s argument < bar. EXPECT_CALL(mock_obj, Foo(Lt(ByRef(bar)))); ``` Remember: if you do this, don't change `bar` after the `EXPECT_CALL()`, or the result is undefined. ## Validating a Member of an Object ## Often a mock function takes a reference to object as an argument. When matching the argument, you may not want to compare the entire object against a fixed object, as that may be over-specification. Instead, you may need to validate a certain member variable or the result of a certain getter method of the object. You can do this with `Field()` and `Property()`. More specifically, ``` Field(&Foo::bar, m) ``` is a matcher that matches a `Foo` object whose `bar` member variable satisfies matcher `m`. ``` Property(&Foo::baz, m) ``` is a matcher that matches a `Foo` object whose `baz()` method returns a value that satisfies matcher `m`. For example: | Expression | Description | |:-----------------------------|:-----------------------------------| | `Field(&Foo::number, Ge(3))` | Matches `x` where `x.number >= 3`. | | `Property(&Foo::name, StartsWith("John "))` | Matches `x` where `x.name()` starts with `"John "`. | Note that in `Property(&Foo::baz, ...)`, method `baz()` must take no argument and be declared as `const`. BTW, `Field()` and `Property()` can also match plain pointers to objects. For instance, ``` Field(&Foo::number, Ge(3)) ``` matches a plain pointer `p` where `p->number >= 3`. If `p` is `NULL`, the match will always fail regardless of the inner matcher. What if you want to validate more than one members at the same time? Remember that there is `AllOf()`. ## Validating the Value Pointed to by a Pointer Argument ## C++ functions often take pointers as arguments. You can use matchers like `IsNull()`, `NotNull()`, and other comparison matchers to match a pointer, but what if you want to make sure the value _pointed to_ by the pointer, instead of the pointer itself, has a certain property? Well, you can use the `Pointee(m)` matcher. `Pointee(m)` matches a pointer iff `m` matches the value the pointer points to. For example: ``` using ::testing::Ge; using ::testing::Pointee; ... EXPECT_CALL(foo, Bar(Pointee(Ge(3)))); ``` expects `foo.Bar()` to be called with a pointer that points to a value greater than or equal to 3. One nice thing about `Pointee()` is that it treats a `NULL` pointer as a match failure, so you can write `Pointee(m)` instead of ``` AllOf(NotNull(), Pointee(m)) ``` without worrying that a `NULL` pointer will crash your test. Also, did we tell you that `Pointee()` works with both raw pointers **and** smart pointers (`linked_ptr`, `shared_ptr`, `scoped_ptr`, and etc)? What if you have a pointer to pointer? You guessed it - you can use nested `Pointee()` to probe deeper inside the value. For example, `Pointee(Pointee(Lt(3)))` matches a pointer that points to a pointer that points to a number less than 3 (what a mouthful...). ## Testing a Certain Property of an Object ## Sometimes you want to specify that an object argument has a certain property, but there is no existing matcher that does this. If you want good error messages, you should define a matcher. If you want to do it quick and dirty, you could get away with writing an ordinary function. Let's say you have a mock function that takes an object of type `Foo`, which has an `int bar()` method and an `int baz()` method, and you want to constrain that the argument's `bar()` value plus its `baz()` value is a given number. Here's how you can define a matcher to do it: ``` using ::testing::MatcherInterface; using ::testing::MatchResultListener; class BarPlusBazEqMatcher : public MatcherInterface { public: explicit BarPlusBazEqMatcher(int expected_sum) : expected_sum_(expected_sum) {} virtual bool MatchAndExplain(const Foo& foo, MatchResultListener* listener) const { return (foo.bar() + foo.baz()) == expected_sum_; } virtual void DescribeTo(::std::ostream* os) const { *os << "bar() + baz() equals " << expected_sum_; } virtual void DescribeNegationTo(::std::ostream* os) const { *os << "bar() + baz() does not equal " << expected_sum_; } private: const int expected_sum_; }; inline Matcher BarPlusBazEq(int expected_sum) { return MakeMatcher(new BarPlusBazEqMatcher(expected_sum)); } ... EXPECT_CALL(..., DoThis(BarPlusBazEq(5)))...; ``` ## Matching Containers ## Sometimes an STL container (e.g. list, vector, map, ...) is passed to a mock function and you may want to validate it. Since most STL containers support the `==` operator, you can write `Eq(expected_container)` or simply `expected_container` to match a container exactly. Sometimes, though, you may want to be more flexible (for example, the first element must be an exact match, but the second element can be any positive number, and so on). Also, containers used in tests often have a small number of elements, and having to define the expected container out-of-line is a bit of a hassle. You can use the `ElementsAre()` or `UnorderedElementsAre()` matcher in such cases: ``` using ::testing::_; using ::testing::ElementsAre; using ::testing::Gt; ... MOCK_METHOD1(Foo, void(const vector& numbers)); ... EXPECT_CALL(mock, Foo(ElementsAre(1, Gt(0), _, 5))); ``` The above matcher says that the container must have 4 elements, which must be 1, greater than 0, anything, and 5 respectively. If you instead write: ``` using ::testing::_; using ::testing::Gt; using ::testing::UnorderedElementsAre; ... MOCK_METHOD1(Foo, void(const vector& numbers)); ... EXPECT_CALL(mock, Foo(UnorderedElementsAre(1, Gt(0), _, 5))); ``` It means that the container must have 4 elements, which under some permutation must be 1, greater than 0, anything, and 5 respectively. `ElementsAre()` and `UnorderedElementsAre()` are overloaded to take 0 to 10 arguments. If more are needed, you can place them in a C-style array and use `ElementsAreArray()` or `UnorderedElementsAreArray()` instead: ``` using ::testing::ElementsAreArray; ... // ElementsAreArray accepts an array of element values. const int expected_vector1[] = { 1, 5, 2, 4, ... }; EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector1))); // Or, an array of element matchers. Matcher expected_vector2 = { 1, Gt(2), _, 3, ... }; EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector2))); ``` In case the array needs to be dynamically created (and therefore the array size cannot be inferred by the compiler), you can give `ElementsAreArray()` an additional argument to specify the array size: ``` using ::testing::ElementsAreArray; ... int* const expected_vector3 = new int[count]; ... fill expected_vector3 with values ... EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector3, count))); ``` **Tips:** * `ElementsAre*()` can be used to match _any_ container that implements the STL iterator pattern (i.e. it has a `const_iterator` type and supports `begin()/end()`), not just the ones defined in STL. It will even work with container types yet to be written - as long as they follows the above pattern. * You can use nested `ElementsAre*()` to match nested (multi-dimensional) containers. * If the container is passed by pointer instead of by reference, just write `Pointee(ElementsAre*(...))`. * The order of elements _matters_ for `ElementsAre*()`. Therefore don't use it with containers whose element order is undefined (e.g. `hash_map`). ## Sharing Matchers ## Under the hood, a Google Mock matcher object consists of a pointer to a ref-counted implementation object. Copying matchers is allowed and very efficient, as only the pointer is copied. When the last matcher that references the implementation object dies, the implementation object will be deleted. Therefore, if you have some complex matcher that you want to use again and again, there is no need to build it every time. Just assign it to a matcher variable and use that variable repeatedly! For example, ``` Matcher in_range = AllOf(Gt(5), Le(10)); ... use in_range as a matcher in multiple EXPECT_CALLs ... ``` # Setting Expectations # ## Knowing When to Expect ## `ON_CALL` is likely the single most under-utilized construct in Google Mock. There are basically two constructs for defining the behavior of a mock object: `ON_CALL` and `EXPECT_CALL`. The difference? `ON_CALL` defines what happens when a mock method is called, but _doesn't imply any expectation on the method being called._ `EXPECT_CALL` not only defines the behavior, but also sets an expectation that _the method will be called with the given arguments, for the given number of times_ (and _in the given order_ when you specify the order too). Since `EXPECT_CALL` does more, isn't it better than `ON_CALL`? Not really. Every `EXPECT_CALL` adds a constraint on the behavior of the code under test. Having more constraints than necessary is _baaad_ - even worse than not having enough constraints. This may be counter-intuitive. How could tests that verify more be worse than tests that verify less? Isn't verification the whole point of tests? The answer, lies in _what_ a test should verify. **A good test verifies the contract of the code.** If a test over-specifies, it doesn't leave enough freedom to the implementation. As a result, changing the implementation without breaking the contract (e.g. refactoring and optimization), which should be perfectly fine to do, can break such tests. Then you have to spend time fixing them, only to see them broken again the next time the implementation is changed. Keep in mind that one doesn't have to verify more than one property in one test. In fact, **it's a good style to verify only one thing in one test.** If you do that, a bug will likely break only one or two tests instead of dozens (which case would you rather debug?). If you are also in the habit of giving tests descriptive names that tell what they verify, you can often easily guess what's wrong just from the test log itself. So use `ON_CALL` by default, and only use `EXPECT_CALL` when you actually intend to verify that the call is made. For example, you may have a bunch of `ON_CALL`s in your test fixture to set the common mock behavior shared by all tests in the same group, and write (scarcely) different `EXPECT_CALL`s in different `TEST_F`s to verify different aspects of the code's behavior. Compared with the style where each `TEST` has many `EXPECT_CALL`s, this leads to tests that are more resilient to implementational changes (and thus less likely to require maintenance) and makes the intent of the tests more obvious (so they are easier to maintain when you do need to maintain them). If you are bothered by the "Uninteresting mock function call" message printed when a mock method without an `EXPECT_CALL` is called, you may use a `NiceMock` instead to suppress all such messages for the mock object, or suppress the message for specific methods by adding `EXPECT_CALL(...).Times(AnyNumber())`. DO NOT suppress it by blindly adding an `EXPECT_CALL(...)`, or you'll have a test that's a pain to maintain. ## Ignoring Uninteresting Calls ## If you are not interested in how a mock method is called, just don't say anything about it. In this case, if the method is ever called, Google Mock will perform its default action to allow the test program to continue. If you are not happy with the default action taken by Google Mock, you can override it using `DefaultValue::Set()` (described later in this document) or `ON_CALL()`. Please note that once you expressed interest in a particular mock method (via `EXPECT_CALL()`), all invocations to it must match some expectation. If this function is called but the arguments don't match any `EXPECT_CALL()` statement, it will be an error. ## Disallowing Unexpected Calls ## If a mock method shouldn't be called at all, explicitly say so: ``` using ::testing::_; ... EXPECT_CALL(foo, Bar(_)) .Times(0); ``` If some calls to the method are allowed, but the rest are not, just list all the expected calls: ``` using ::testing::AnyNumber; using ::testing::Gt; ... EXPECT_CALL(foo, Bar(5)); EXPECT_CALL(foo, Bar(Gt(10))) .Times(AnyNumber()); ``` A call to `foo.Bar()` that doesn't match any of the `EXPECT_CALL()` statements will be an error. ## Understanding Uninteresting vs Unexpected Calls ## _Uninteresting_ calls and _unexpected_ calls are different concepts in Google Mock. _Very_ different. A call `x.Y(...)` is **uninteresting** if there's _not even a single_ `EXPECT_CALL(x, Y(...))` set. In other words, the test isn't interested in the `x.Y()` method at all, as evident in that the test doesn't care to say anything about it. A call `x.Y(...)` is **unexpected** if there are some `EXPECT_CALL(x, Y(...))s` set, but none of them matches the call. Put another way, the test is interested in the `x.Y()` method (therefore it _explicitly_ sets some `EXPECT_CALL` to verify how it's called); however, the verification fails as the test doesn't expect this particular call to happen. **An unexpected call is always an error,** as the code under test doesn't behave the way the test expects it to behave. **By default, an uninteresting call is not an error,** as it violates no constraint specified by the test. (Google Mock's philosophy is that saying nothing means there is no constraint.) However, it leads to a warning, as it _might_ indicate a problem (e.g. the test author might have forgotten to specify a constraint). In Google Mock, `NiceMock` and `StrictMock` can be used to make a mock class "nice" or "strict". How does this affect uninteresting calls and unexpected calls? A **nice mock** suppresses uninteresting call warnings. It is less chatty than the default mock, but otherwise is the same. If a test fails with a default mock, it will also fail using a nice mock instead. And vice versa. Don't expect making a mock nice to change the test's result. A **strict mock** turns uninteresting call warnings into errors. So making a mock strict may change the test's result. Let's look at an example: ``` TEST(...) { NiceMock mock_registry; EXPECT_CALL(mock_registry, GetDomainOwner("google.com")) .WillRepeatedly(Return("Larry Page")); // Use mock_registry in code under test. ... &mock_registry ... } ``` The sole `EXPECT_CALL` here says that all calls to `GetDomainOwner()` must have `"google.com"` as the argument. If `GetDomainOwner("yahoo.com")` is called, it will be an unexpected call, and thus an error. Having a nice mock doesn't change the severity of an unexpected call. So how do we tell Google Mock that `GetDomainOwner()` can be called with some other arguments as well? The standard technique is to add a "catch all" `EXPECT_CALL`: ``` EXPECT_CALL(mock_registry, GetDomainOwner(_)) .Times(AnyNumber()); // catches all other calls to this method. EXPECT_CALL(mock_registry, GetDomainOwner("google.com")) .WillRepeatedly(Return("Larry Page")); ``` Remember that `_` is the wildcard matcher that matches anything. With this, if `GetDomainOwner("google.com")` is called, it will do what the second `EXPECT_CALL` says; if it is called with a different argument, it will do what the first `EXPECT_CALL` says. Note that the order of the two `EXPECT_CALLs` is important, as a newer `EXPECT_CALL` takes precedence over an older one. For more on uninteresting calls, nice mocks, and strict mocks, read ["The Nice, the Strict, and the Naggy"](#the-nice-the-strict-and-the-naggy). ## Expecting Ordered Calls ## Although an `EXPECT_CALL()` statement defined earlier takes precedence when Google Mock tries to match a function call with an expectation, by default calls don't have to happen in the order `EXPECT_CALL()` statements are written. For example, if the arguments match the matchers in the third `EXPECT_CALL()`, but not those in the first two, then the third expectation will be used. If you would rather have all calls occur in the order of the expectations, put the `EXPECT_CALL()` statements in a block where you define a variable of type `InSequence`: ``` using ::testing::_; using ::testing::InSequence; { InSequence s; EXPECT_CALL(foo, DoThis(5)); EXPECT_CALL(bar, DoThat(_)) .Times(2); EXPECT_CALL(foo, DoThis(6)); } ``` In this example, we expect a call to `foo.DoThis(5)`, followed by two calls to `bar.DoThat()` where the argument can be anything, which are in turn followed by a call to `foo.DoThis(6)`. If a call occurred out-of-order, Google Mock will report an error. ## Expecting Partially Ordered Calls ## Sometimes requiring everything to occur in a predetermined order can lead to brittle tests. For example, we may care about `A` occurring before both `B` and `C`, but aren't interested in the relative order of `B` and `C`. In this case, the test should reflect our real intent, instead of being overly constraining. Google Mock allows you to impose an arbitrary DAG (directed acyclic graph) on the calls. One way to express the DAG is to use the [After](CheatSheet.md#the-after-clause) clause of `EXPECT_CALL`. Another way is via the `InSequence()` clause (not the same as the `InSequence` class), which we borrowed from jMock 2. It's less flexible than `After()`, but more convenient when you have long chains of sequential calls, as it doesn't require you to come up with different names for the expectations in the chains. Here's how it works: If we view `EXPECT_CALL()` statements as nodes in a graph, and add an edge from node A to node B wherever A must occur before B, we can get a DAG. We use the term "sequence" to mean a directed path in this DAG. Now, if we decompose the DAG into sequences, we just need to know which sequences each `EXPECT_CALL()` belongs to in order to be able to reconstruct the original DAG. So, to specify the partial order on the expectations we need to do two things: first to define some `Sequence` objects, and then for each `EXPECT_CALL()` say which `Sequence` objects it is part of. Expectations in the same sequence must occur in the order they are written. For example, ``` using ::testing::Sequence; Sequence s1, s2; EXPECT_CALL(foo, A()) .InSequence(s1, s2); EXPECT_CALL(bar, B()) .InSequence(s1); EXPECT_CALL(bar, C()) .InSequence(s2); EXPECT_CALL(foo, D()) .InSequence(s2); ``` specifies the following DAG (where `s1` is `A -> B`, and `s2` is `A -> C -> D`): ``` +---> B | A ---| | +---> C ---> D ``` This means that A must occur before B and C, and C must occur before D. There's no restriction about the order other than these. ## Controlling When an Expectation Retires ## When a mock method is called, Google Mock only consider expectations that are still active. An expectation is active when created, and becomes inactive (aka _retires_) when a call that has to occur later has occurred. For example, in ``` using ::testing::_; using ::testing::Sequence; Sequence s1, s2; EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #1 .Times(AnyNumber()) .InSequence(s1, s2); EXPECT_CALL(log, Log(WARNING, _, "Data set is empty.")) // #2 .InSequence(s1); EXPECT_CALL(log, Log(WARNING, _, "User not found.")) // #3 .InSequence(s2); ``` as soon as either #2 or #3 is matched, #1 will retire. If a warning `"File too large."` is logged after this, it will be an error. Note that an expectation doesn't retire automatically when it's saturated. For example, ``` using ::testing::_; ... EXPECT_CALL(log, Log(WARNING, _, _)); // #1 EXPECT_CALL(log, Log(WARNING, _, "File too large.")); // #2 ``` says that there will be exactly one warning with the message `"File too large."`. If the second warning contains this message too, #2 will match again and result in an upper-bound-violated error. If this is not what you want, you can ask an expectation to retire as soon as it becomes saturated: ``` using ::testing::_; ... EXPECT_CALL(log, Log(WARNING, _, _)); // #1 EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #2 .RetiresOnSaturation(); ``` Here #2 can be used only once, so if you have two warnings with the message `"File too large."`, the first will match #2 and the second will match #1 - there will be no error. # Using Actions # ## Returning References from Mock Methods ## If a mock function's return type is a reference, you need to use `ReturnRef()` instead of `Return()` to return a result: ``` using ::testing::ReturnRef; class MockFoo : public Foo { public: MOCK_METHOD0(GetBar, Bar&()); }; ... MockFoo foo; Bar bar; EXPECT_CALL(foo, GetBar()) .WillOnce(ReturnRef(bar)); ``` ## Returning Live Values from Mock Methods ## The `Return(x)` action saves a copy of `x` when the action is _created_, and always returns the same value whenever it's executed. Sometimes you may want to instead return the _live_ value of `x` (i.e. its value at the time when the action is _executed_.). If the mock function's return type is a reference, you can do it using `ReturnRef(x)`, as shown in the previous recipe ("Returning References from Mock Methods"). However, Google Mock doesn't let you use `ReturnRef()` in a mock function whose return type is not a reference, as doing that usually indicates a user error. So, what shall you do? You may be tempted to try `ByRef()`: ``` using testing::ByRef; using testing::Return; class MockFoo : public Foo { public: MOCK_METHOD0(GetValue, int()); }; ... int x = 0; MockFoo foo; EXPECT_CALL(foo, GetValue()) .WillRepeatedly(Return(ByRef(x))); x = 42; EXPECT_EQ(42, foo.GetValue()); ``` Unfortunately, it doesn't work here. The above code will fail with error: ``` Value of: foo.GetValue() Actual: 0 Expected: 42 ``` The reason is that `Return(value)` converts `value` to the actual return type of the mock function at the time when the action is _created_, not when it is _executed_. (This behavior was chosen for the action to be safe when `value` is a proxy object that references some temporary objects.) As a result, `ByRef(x)` is converted to an `int` value (instead of a `const int&`) when the expectation is set, and `Return(ByRef(x))` will always return 0. `ReturnPointee(pointer)` was provided to solve this problem specifically. It returns the value pointed to by `pointer` at the time the action is _executed_: ``` using testing::ReturnPointee; ... int x = 0; MockFoo foo; EXPECT_CALL(foo, GetValue()) .WillRepeatedly(ReturnPointee(&x)); // Note the & here. x = 42; EXPECT_EQ(42, foo.GetValue()); // This will succeed now. ``` ## Combining Actions ## Want to do more than one thing when a function is called? That's fine. `DoAll()` allow you to do sequence of actions every time. Only the return value of the last action in the sequence will be used. ``` using ::testing::DoAll; class MockFoo : public Foo { public: MOCK_METHOD1(Bar, bool(int n)); }; ... EXPECT_CALL(foo, Bar(_)) .WillOnce(DoAll(action_1, action_2, ... action_n)); ``` ## Mocking Side Effects ## Sometimes a method exhibits its effect not via returning a value but via side effects. For example, it may change some global state or modify an output argument. To mock side effects, in general you can define your own action by implementing `::testing::ActionInterface`. If all you need to do is to change an output argument, the built-in `SetArgPointee()` action is convenient: ``` using ::testing::SetArgPointee; class MockMutator : public Mutator { public: MOCK_METHOD2(Mutate, void(bool mutate, int* value)); ... }; ... MockMutator mutator; EXPECT_CALL(mutator, Mutate(true, _)) .WillOnce(SetArgPointee<1>(5)); ``` In this example, when `mutator.Mutate()` is called, we will assign 5 to the `int` variable pointed to by argument #1 (0-based). `SetArgPointee()` conveniently makes an internal copy of the value you pass to it, removing the need to keep the value in scope and alive. The implication however is that the value must have a copy constructor and assignment operator. If the mock method also needs to return a value as well, you can chain `SetArgPointee()` with `Return()` using `DoAll()`: ``` using ::testing::_; using ::testing::Return; using ::testing::SetArgPointee; class MockMutator : public Mutator { public: ... MOCK_METHOD1(MutateInt, bool(int* value)); }; ... MockMutator mutator; EXPECT_CALL(mutator, MutateInt(_)) .WillOnce(DoAll(SetArgPointee<0>(5), Return(true))); ``` If the output argument is an array, use the `SetArrayArgument(first, last)` action instead. It copies the elements in source range `[first, last)` to the array pointed to by the `N`-th (0-based) argument: ``` using ::testing::NotNull; using ::testing::SetArrayArgument; class MockArrayMutator : public ArrayMutator { public: MOCK_METHOD2(Mutate, void(int* values, int num_values)); ... }; ... MockArrayMutator mutator; int values[5] = { 1, 2, 3, 4, 5 }; EXPECT_CALL(mutator, Mutate(NotNull(), 5)) .WillOnce(SetArrayArgument<0>(values, values + 5)); ``` This also works when the argument is an output iterator: ``` using ::testing::_; using ::testing::SetArrayArgument; class MockRolodex : public Rolodex { public: MOCK_METHOD1(GetNames, void(std::back_insert_iterator >)); ... }; ... MockRolodex rolodex; vector names; names.push_back("George"); names.push_back("John"); names.push_back("Thomas"); EXPECT_CALL(rolodex, GetNames(_)) .WillOnce(SetArrayArgument<0>(names.begin(), names.end())); ``` ## Changing a Mock Object's Behavior Based on the State ## If you expect a call to change the behavior of a mock object, you can use `::testing::InSequence` to specify different behaviors before and after the call: ``` using ::testing::InSequence; using ::testing::Return; ... { InSequence seq; EXPECT_CALL(my_mock, IsDirty()) .WillRepeatedly(Return(true)); EXPECT_CALL(my_mock, Flush()); EXPECT_CALL(my_mock, IsDirty()) .WillRepeatedly(Return(false)); } my_mock.FlushIfDirty(); ``` This makes `my_mock.IsDirty()` return `true` before `my_mock.Flush()` is called and return `false` afterwards. If the behavior change is more complex, you can store the effects in a variable and make a mock method get its return value from that variable: ``` using ::testing::_; using ::testing::SaveArg; using ::testing::Return; ACTION_P(ReturnPointee, p) { return *p; } ... int previous_value = 0; EXPECT_CALL(my_mock, GetPrevValue()) .WillRepeatedly(ReturnPointee(&previous_value)); EXPECT_CALL(my_mock, UpdateValue(_)) .WillRepeatedly(SaveArg<0>(&previous_value)); my_mock.DoSomethingToUpdateValue(); ``` Here `my_mock.GetPrevValue()` will always return the argument of the last `UpdateValue()` call. ## Setting the Default Value for a Return Type ## If a mock method's return type is a built-in C++ type or pointer, by default it will return 0 when invoked. Also, in C++ 11 and above, a mock method whose return type has a default constructor will return a default-constructed value by default. You only need to specify an action if this default value doesn't work for you. Sometimes, you may want to change this default value, or you may want to specify a default value for types Google Mock doesn't know about. You can do this using the `::testing::DefaultValue` class template: ``` class MockFoo : public Foo { public: MOCK_METHOD0(CalculateBar, Bar()); }; ... Bar default_bar; // Sets the default return value for type Bar. DefaultValue::Set(default_bar); MockFoo foo; // We don't need to specify an action here, as the default // return value works for us. EXPECT_CALL(foo, CalculateBar()); foo.CalculateBar(); // This should return default_bar. // Unsets the default return value. DefaultValue::Clear(); ``` Please note that changing the default value for a type can make you tests hard to understand. We recommend you to use this feature judiciously. For example, you may want to make sure the `Set()` and `Clear()` calls are right next to the code that uses your mock. ## Setting the Default Actions for a Mock Method ## You've learned how to change the default value of a given type. However, this may be too coarse for your purpose: perhaps you have two mock methods with the same return type and you want them to have different behaviors. The `ON_CALL()` macro allows you to customize your mock's behavior at the method level: ``` using ::testing::_; using ::testing::AnyNumber; using ::testing::Gt; using ::testing::Return; ... ON_CALL(foo, Sign(_)) .WillByDefault(Return(-1)); ON_CALL(foo, Sign(0)) .WillByDefault(Return(0)); ON_CALL(foo, Sign(Gt(0))) .WillByDefault(Return(1)); EXPECT_CALL(foo, Sign(_)) .Times(AnyNumber()); foo.Sign(5); // This should return 1. foo.Sign(-9); // This should return -1. foo.Sign(0); // This should return 0. ``` As you may have guessed, when there are more than one `ON_CALL()` statements, the news order take precedence over the older ones. In other words, the **last** one that matches the function arguments will be used. This matching order allows you to set up the common behavior in a mock object's constructor or the test fixture's set-up phase and specialize the mock's behavior later. ## Using Functions/Methods/Functors as Actions ## If the built-in actions don't suit you, you can easily use an existing function, method, or functor as an action: ``` using ::testing::_; using ::testing::Invoke; class MockFoo : public Foo { public: MOCK_METHOD2(Sum, int(int x, int y)); MOCK_METHOD1(ComplexJob, bool(int x)); }; int CalculateSum(int x, int y) { return x + y; } class Helper { public: bool ComplexJob(int x); }; ... MockFoo foo; Helper helper; EXPECT_CALL(foo, Sum(_, _)) .WillOnce(Invoke(CalculateSum)); EXPECT_CALL(foo, ComplexJob(_)) .WillOnce(Invoke(&helper, &Helper::ComplexJob)); foo.Sum(5, 6); // Invokes CalculateSum(5, 6). foo.ComplexJob(10); // Invokes helper.ComplexJob(10); ``` The only requirement is that the type of the function, etc must be _compatible_ with the signature of the mock function, meaning that the latter's arguments can be implicitly converted to the corresponding arguments of the former, and the former's return type can be implicitly converted to that of the latter. So, you can invoke something whose type is _not_ exactly the same as the mock function, as long as it's safe to do so - nice, huh? ## Invoking a Function/Method/Functor Without Arguments ## `Invoke()` is very useful for doing actions that are more complex. It passes the mock function's arguments to the function or functor being invoked such that the callee has the full context of the call to work with. If the invoked function is not interested in some or all of the arguments, it can simply ignore them. Yet, a common pattern is that a test author wants to invoke a function without the arguments of the mock function. `Invoke()` allows her to do that using a wrapper function that throws away the arguments before invoking an underlining nullary function. Needless to say, this can be tedious and obscures the intent of the test. `InvokeWithoutArgs()` solves this problem. It's like `Invoke()` except that it doesn't pass the mock function's arguments to the callee. Here's an example: ``` using ::testing::_; using ::testing::InvokeWithoutArgs; class MockFoo : public Foo { public: MOCK_METHOD1(ComplexJob, bool(int n)); }; bool Job1() { ... } ... MockFoo foo; EXPECT_CALL(foo, ComplexJob(_)) .WillOnce(InvokeWithoutArgs(Job1)); foo.ComplexJob(10); // Invokes Job1(). ``` ## Invoking an Argument of the Mock Function ## Sometimes a mock function will receive a function pointer or a functor (in other words, a "callable") as an argument, e.g. ``` class MockFoo : public Foo { public: MOCK_METHOD2(DoThis, bool(int n, bool (*fp)(int))); }; ``` and you may want to invoke this callable argument: ``` using ::testing::_; ... MockFoo foo; EXPECT_CALL(foo, DoThis(_, _)) .WillOnce(...); // Will execute (*fp)(5), where fp is the // second argument DoThis() receives. ``` Arghh, you need to refer to a mock function argument but your version of C++ has no lambdas, so you have to define your own action. :-( Or do you really? Well, Google Mock has an action to solve _exactly_ this problem: ``` InvokeArgument(arg_1, arg_2, ..., arg_m) ``` will invoke the `N`-th (0-based) argument the mock function receives, with `arg_1`, `arg_2`, ..., and `arg_m`. No matter if the argument is a function pointer or a functor, Google Mock handles them both. With that, you could write: ``` using ::testing::_; using ::testing::InvokeArgument; ... EXPECT_CALL(foo, DoThis(_, _)) .WillOnce(InvokeArgument<1>(5)); // Will execute (*fp)(5), where fp is the // second argument DoThis() receives. ``` What if the callable takes an argument by reference? No problem - just wrap it inside `ByRef()`: ``` ... MOCK_METHOD1(Bar, bool(bool (*fp)(int, const Helper&))); ... using ::testing::_; using ::testing::ByRef; using ::testing::InvokeArgument; ... MockFoo foo; Helper helper; ... EXPECT_CALL(foo, Bar(_)) .WillOnce(InvokeArgument<0>(5, ByRef(helper))); // ByRef(helper) guarantees that a reference to helper, not a copy of it, // will be passed to the callable. ``` What if the callable takes an argument by reference and we do **not** wrap the argument in `ByRef()`? Then `InvokeArgument()` will _make a copy_ of the argument, and pass a _reference to the copy_, instead of a reference to the original value, to the callable. This is especially handy when the argument is a temporary value: ``` ... MOCK_METHOD1(DoThat, bool(bool (*f)(const double& x, const string& s))); ... using ::testing::_; using ::testing::InvokeArgument; ... MockFoo foo; ... EXPECT_CALL(foo, DoThat(_)) .WillOnce(InvokeArgument<0>(5.0, string("Hi"))); // Will execute (*f)(5.0, string("Hi")), where f is the function pointer // DoThat() receives. Note that the values 5.0 and string("Hi") are // temporary and dead once the EXPECT_CALL() statement finishes. Yet // it's fine to perform this action later, since a copy of the values // are kept inside the InvokeArgument action. ``` ## Ignoring an Action's Result ## Sometimes you have an action that returns _something_, but you need an action that returns `void` (perhaps you want to use it in a mock function that returns `void`, or perhaps it needs to be used in `DoAll()` and it's not the last in the list). `IgnoreResult()` lets you do that. For example: ``` using ::testing::_; using ::testing::Invoke; using ::testing::Return; int Process(const MyData& data); string DoSomething(); class MockFoo : public Foo { public: MOCK_METHOD1(Abc, void(const MyData& data)); MOCK_METHOD0(Xyz, bool()); }; ... MockFoo foo; EXPECT_CALL(foo, Abc(_)) // .WillOnce(Invoke(Process)); // The above line won't compile as Process() returns int but Abc() needs // to return void. .WillOnce(IgnoreResult(Invoke(Process))); EXPECT_CALL(foo, Xyz()) .WillOnce(DoAll(IgnoreResult(Invoke(DoSomething)), // Ignores the string DoSomething() returns. Return(true))); ``` Note that you **cannot** use `IgnoreResult()` on an action that already returns `void`. Doing so will lead to ugly compiler errors. ## Selecting an Action's Arguments ## Say you have a mock function `Foo()` that takes seven arguments, and you have a custom action that you want to invoke when `Foo()` is called. Trouble is, the custom action only wants three arguments: ``` using ::testing::_; using ::testing::Invoke; ... MOCK_METHOD7(Foo, bool(bool visible, const string& name, int x, int y, const map, double>& weight, double min_weight, double max_wight)); ... bool IsVisibleInQuadrant1(bool visible, int x, int y) { return visible && x >= 0 && y >= 0; } ... EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) .WillOnce(Invoke(IsVisibleInQuadrant1)); // Uh, won't compile. :-( ``` To please the compiler God, you can to define an "adaptor" that has the same signature as `Foo()` and calls the custom action with the right arguments: ``` using ::testing::_; using ::testing::Invoke; bool MyIsVisibleInQuadrant1(bool visible, const string& name, int x, int y, const map, double>& weight, double min_weight, double max_wight) { return IsVisibleInQuadrant1(visible, x, y); } ... EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) .WillOnce(Invoke(MyIsVisibleInQuadrant1)); // Now it works. ``` But isn't this awkward? Google Mock provides a generic _action adaptor_, so you can spend your time minding more important business than writing your own adaptors. Here's the syntax: ``` WithArgs(action) ``` creates an action that passes the arguments of the mock function at the given indices (0-based) to the inner `action` and performs it. Using `WithArgs`, our original example can be written as: ``` using ::testing::_; using ::testing::Invoke; using ::testing::WithArgs; ... EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _)) .WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1))); // No need to define your own adaptor. ``` For better readability, Google Mock also gives you: * `WithoutArgs(action)` when the inner `action` takes _no_ argument, and * `WithArg(action)` (no `s` after `Arg`) when the inner `action` takes _one_ argument. As you may have realized, `InvokeWithoutArgs(...)` is just syntactic sugar for `WithoutArgs(Invoke(...))`. Here are more tips: * The inner action used in `WithArgs` and friends does not have to be `Invoke()` -- it can be anything. * You can repeat an argument in the argument list if necessary, e.g. `WithArgs<2, 3, 3, 5>(...)`. * You can change the order of the arguments, e.g. `WithArgs<3, 2, 1>(...)`. * The types of the selected arguments do _not_ have to match the signature of the inner action exactly. It works as long as they can be implicitly converted to the corresponding arguments of the inner action. For example, if the 4-th argument of the mock function is an `int` and `my_action` takes a `double`, `WithArg<4>(my_action)` will work. ## Ignoring Arguments in Action Functions ## The selecting-an-action's-arguments recipe showed us one way to make a mock function and an action with incompatible argument lists fit together. The downside is that wrapping the action in `WithArgs<...>()` can get tedious for people writing the tests. If you are defining a function, method, or functor to be used with `Invoke*()`, and you are not interested in some of its arguments, an alternative to `WithArgs` is to declare the uninteresting arguments as `Unused`. This makes the definition less cluttered and less fragile in case the types of the uninteresting arguments change. It could also increase the chance the action function can be reused. For example, given ``` MOCK_METHOD3(Foo, double(const string& label, double x, double y)); MOCK_METHOD3(Bar, double(int index, double x, double y)); ``` instead of ``` using ::testing::_; using ::testing::Invoke; double DistanceToOriginWithLabel(const string& label, double x, double y) { return sqrt(x*x + y*y); } double DistanceToOriginWithIndex(int index, double x, double y) { return sqrt(x*x + y*y); } ... EXEPCT_CALL(mock, Foo("abc", _, _)) .WillOnce(Invoke(DistanceToOriginWithLabel)); EXEPCT_CALL(mock, Bar(5, _, _)) .WillOnce(Invoke(DistanceToOriginWithIndex)); ``` you could write ``` using ::testing::_; using ::testing::Invoke; using ::testing::Unused; double DistanceToOrigin(Unused, double x, double y) { return sqrt(x*x + y*y); } ... EXEPCT_CALL(mock, Foo("abc", _, _)) .WillOnce(Invoke(DistanceToOrigin)); EXEPCT_CALL(mock, Bar(5, _, _)) .WillOnce(Invoke(DistanceToOrigin)); ``` ## Sharing Actions ## Just like matchers, a Google Mock action object consists of a pointer to a ref-counted implementation object. Therefore copying actions is also allowed and very efficient. When the last action that references the implementation object dies, the implementation object will be deleted. If you have some complex action that you want to use again and again, you may not have to build it from scratch every time. If the action doesn't have an internal state (i.e. if it always does the same thing no matter how many times it has been called), you can assign it to an action variable and use that variable repeatedly. For example: ``` Action set_flag = DoAll(SetArgPointee<0>(5), Return(true)); ... use set_flag in .WillOnce() and .WillRepeatedly() ... ``` However, if the action has its own state, you may be surprised if you share the action object. Suppose you have an action factory `IncrementCounter(init)` which creates an action that increments and returns a counter whose initial value is `init`, using two actions created from the same expression and using a shared action will exihibit different behaviors. Example: ``` EXPECT_CALL(foo, DoThis()) .WillRepeatedly(IncrementCounter(0)); EXPECT_CALL(foo, DoThat()) .WillRepeatedly(IncrementCounter(0)); foo.DoThis(); // Returns 1. foo.DoThis(); // Returns 2. foo.DoThat(); // Returns 1 - Blah() uses a different // counter than Bar()'s. ``` versus ``` Action increment = IncrementCounter(0); EXPECT_CALL(foo, DoThis()) .WillRepeatedly(increment); EXPECT_CALL(foo, DoThat()) .WillRepeatedly(increment); foo.DoThis(); // Returns 1. foo.DoThis(); // Returns 2. foo.DoThat(); // Returns 3 - the counter is shared. ``` # Misc Recipes on Using Google Mock # ## Mocking Methods That Use Move-Only Types ## C++11 introduced *move-only types*. A move-only-typed value can be moved from one object to another, but cannot be copied. `std::unique_ptr` is probably the most commonly used move-only type. Mocking a method that takes and/or returns move-only types presents some challenges, but nothing insurmountable. This recipe shows you how you can do it. Note that the support for move-only method arguments was only introduced to gMock in April 2017; in older code, you may find more complex [workarounds](#LegacyMoveOnly) for lack of this feature. Let’s say we are working on a fictional project that lets one post and share snippets called “buzzes”. Your code uses these types: ```cpp enum class AccessLevel { kInternal, kPublic }; class Buzz { public: explicit Buzz(AccessLevel access) { ... } ... }; class Buzzer { public: virtual ~Buzzer() {} virtual std::unique_ptr MakeBuzz(StringPiece text) = 0; virtual bool ShareBuzz(std::unique_ptr buzz, int64_t timestamp) = 0; ... }; ``` A `Buzz` object represents a snippet being posted. A class that implements the `Buzzer` interface is capable of creating and sharing `Buzz`es. Methods in `Buzzer` may return a `unique_ptr` or take a `unique_ptr`. Now we need to mock `Buzzer` in our tests. To mock a method that accepts or returns move-only types, you just use the familiar `MOCK_METHOD` syntax as usual: ```cpp class MockBuzzer : public Buzzer { public: MOCK_METHOD1(MakeBuzz, std::unique_ptr(StringPiece text)); MOCK_METHOD2(ShareBuzz, bool(std::unique_ptr buzz, int64_t timestamp)); }; ``` Now that we have the mock class defined, we can use it in tests. In the following code examples, we assume that we have defined a `MockBuzzer` object named `mock_buzzer_`: ```cpp MockBuzzer mock_buzzer_; ``` First let’s see how we can set expectations on the `MakeBuzz()` method, which returns a `unique_ptr`. As usual, if you set an expectation without an action (i.e. the `.WillOnce()` or `.WillRepeated()` clause), when that expectation fires, the default action for that method will be taken. Since `unique_ptr<>` has a default constructor that returns a null `unique_ptr`, that’s what you’ll get if you don’t specify an action: ```cpp // Use the default action. EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")); // Triggers the previous EXPECT_CALL. EXPECT_EQ(nullptr, mock_buzzer_.MakeBuzz("hello")); ``` If you are not happy with the default action, you can tweak it as usual; see [Setting Default Actions](#OnCall). If you just need to return a pre-defined move-only value, you can use the `Return(ByMove(...))` action: ```cpp // When this fires, the unique_ptr<> specified by ByMove(...) will // be returned. EXPECT_CALL(mock_buzzer_, MakeBuzz("world")) .WillOnce(Return(ByMove(MakeUnique(AccessLevel::kInternal)))); EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("world")); ``` Note that `ByMove()` is essential here - if you drop it, the code won’t compile. Quiz time! What do you think will happen if a `Return(ByMove(...))` action is performed more than once (e.g. you write `.WillRepeatedly(Return(ByMove(...)));`)? Come think of it, after the first time the action runs, the source value will be consumed (since it’s a move-only value), so the next time around, there’s no value to move from -- you’ll get a run-time error that `Return(ByMove(...))` can only be run once. If you need your mock method to do more than just moving a pre-defined value, remember that you can always use a lambda or a callable object, which can do pretty much anything you want: ```cpp EXPECT_CALL(mock_buzzer_, MakeBuzz("x")) .WillRepeatedly([](StringPiece text) { return MakeUnique(AccessLevel::kInternal); }); EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x")); EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x")); ``` Every time this `EXPECT_CALL` fires, a new `unique_ptr` will be created and returned. You cannot do this with `Return(ByMove(...))`. That covers returning move-only values; but how do we work with methods accepting move-only arguments? The answer is that they work normally, although some actions will not compile when any of method's arguments are move-only. You can always use `Return`, or a [lambda or functor](#FunctionsAsActions): ```cpp using ::testing::Unused; EXPECT_CALL(mock_buzzer_, ShareBuzz(NotNull(), _)) .WillOnce(Return(true)); EXPECT_TRUE(mock_buzzer_.ShareBuzz(MakeUnique(AccessLevel::kInternal)), 0); EXPECT_CALL(mock_buzzer_, ShareBuzz(_, _)) .WillOnce( [](std::unique_ptr buzz, Unused) { return buzz != nullptr; }); EXPECT_FALSE(mock_buzzer_.ShareBuzz(nullptr, 0)); ``` Many built-in actions (`WithArgs`, `WithoutArgs`,`DeleteArg`, `SaveArg`, ...) could in principle support move-only arguments, but the support for this is not implemented yet. If this is blocking you, please file a bug. A few actions (e.g. `DoAll`) copy their arguments internally, so they can never work with non-copyable objects; you'll have to use functors instead. ##### Legacy workarounds for move-only types {#LegacyMoveOnly} Support for move-only function arguments was only introduced to gMock in April 2017. In older code, you may encounter the following workaround for the lack of this feature (it is no longer necessary - we're including it just for reference): ```cpp class MockBuzzer : public Buzzer { public: MOCK_METHOD2(DoShareBuzz, bool(Buzz* buzz, Time timestamp)); bool ShareBuzz(std::unique_ptr buzz, Time timestamp) override { return DoShareBuzz(buzz.get(), timestamp); } }; ``` The trick is to delegate the `ShareBuzz()` method to a mock method (let’s call it `DoShareBuzz()`) that does not take move-only parameters. Then, instead of setting expectations on `ShareBuzz()`, you set them on the `DoShareBuzz()` mock method: ```cpp MockBuzzer mock_buzzer_; EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _)); // When one calls ShareBuzz() on the MockBuzzer like this, the call is // forwarded to DoShareBuzz(), which is mocked. Therefore this statement // will trigger the above EXPECT_CALL. mock_buzzer_.ShareBuzz(MakeUnique(AccessLevel::kInternal), 0); ``` ## Making the Compilation Faster ## Believe it or not, the _vast majority_ of the time spent on compiling a mock class is in generating its constructor and destructor, as they perform non-trivial tasks (e.g. verification of the expectations). What's more, mock methods with different signatures have different types and thus their constructors/destructors need to be generated by the compiler separately. As a result, if you mock many different types of methods, compiling your mock class can get really slow. If you are experiencing slow compilation, you can move the definition of your mock class' constructor and destructor out of the class body and into a `.cpp` file. This way, even if you `#include` your mock class in N files, the compiler only needs to generate its constructor and destructor once, resulting in a much faster compilation. Let's illustrate the idea using an example. Here's the definition of a mock class before applying this recipe: ``` // File mock_foo.h. ... class MockFoo : public Foo { public: // Since we don't declare the constructor or the destructor, // the compiler will generate them in every translation unit // where this mock class is used. MOCK_METHOD0(DoThis, int()); MOCK_METHOD1(DoThat, bool(const char* str)); ... more mock methods ... }; ``` After the change, it would look like: ``` // File mock_foo.h. ... class MockFoo : public Foo { public: // The constructor and destructor are declared, but not defined, here. MockFoo(); virtual ~MockFoo(); MOCK_METHOD0(DoThis, int()); MOCK_METHOD1(DoThat, bool(const char* str)); ... more mock methods ... }; ``` and ``` // File mock_foo.cpp. #include "path/to/mock_foo.h" // The definitions may appear trivial, but the functions actually do a // lot of things through the constructors/destructors of the member // variables used to implement the mock methods. MockFoo::MockFoo() {} MockFoo::~MockFoo() {} ``` ## Forcing a Verification ## When it's being destroyed, your friendly mock object will automatically verify that all expectations on it have been satisfied, and will generate [Google Test](../../googletest/) failures if not. This is convenient as it leaves you with one less thing to worry about. That is, unless you are not sure if your mock object will be destroyed. How could it be that your mock object won't eventually be destroyed? Well, it might be created on the heap and owned by the code you are testing. Suppose there's a bug in that code and it doesn't delete the mock object properly - you could end up with a passing test when there's actually a bug. Using a heap checker is a good idea and can alleviate the concern, but its implementation may not be 100% reliable. So, sometimes you do want to _force_ Google Mock to verify a mock object before it is (hopefully) destructed. You can do this with `Mock::VerifyAndClearExpectations(&mock_object)`: ``` TEST(MyServerTest, ProcessesRequest) { using ::testing::Mock; MockFoo* const foo = new MockFoo; EXPECT_CALL(*foo, ...)...; // ... other expectations ... // server now owns foo. MyServer server(foo); server.ProcessRequest(...); // In case that server's destructor will forget to delete foo, // this will verify the expectations anyway. Mock::VerifyAndClearExpectations(foo); } // server is destroyed when it goes out of scope here. ``` **Tip:** The `Mock::VerifyAndClearExpectations()` function returns a `bool` to indicate whether the verification was successful (`true` for yes), so you can wrap that function call inside a `ASSERT_TRUE()` if there is no point going further when the verification has failed. ## Using Check Points ## Sometimes you may want to "reset" a mock object at various check points in your test: at each check point, you verify that all existing expectations on the mock object have been satisfied, and then you set some new expectations on it as if it's newly created. This allows you to work with a mock object in "phases" whose sizes are each manageable. One such scenario is that in your test's `SetUp()` function, you may want to put the object you are testing into a certain state, with the help from a mock object. Once in the desired state, you want to clear all expectations on the mock, such that in the `TEST_F` body you can set fresh expectations on it. As you may have figured out, the `Mock::VerifyAndClearExpectations()` function we saw in the previous recipe can help you here. Or, if you are using `ON_CALL()` to set default actions on the mock object and want to clear the default actions as well, use `Mock::VerifyAndClear(&mock_object)` instead. This function does what `Mock::VerifyAndClearExpectations(&mock_object)` does and returns the same `bool`, **plus** it clears the `ON_CALL()` statements on `mock_object` too. Another trick you can use to achieve the same effect is to put the expectations in sequences and insert calls to a dummy "check-point" function at specific places. Then you can verify that the mock function calls do happen at the right time. For example, if you are exercising code: ``` Foo(1); Foo(2); Foo(3); ``` and want to verify that `Foo(1)` and `Foo(3)` both invoke `mock.Bar("a")`, but `Foo(2)` doesn't invoke anything. You can write: ``` using ::testing::MockFunction; TEST(FooTest, InvokesBarCorrectly) { MyMock mock; // Class MockFunction has exactly one mock method. It is named // Call() and has type F. MockFunction check; { InSequence s; EXPECT_CALL(mock, Bar("a")); EXPECT_CALL(check, Call("1")); EXPECT_CALL(check, Call("2")); EXPECT_CALL(mock, Bar("a")); } Foo(1); check.Call("1"); Foo(2); check.Call("2"); Foo(3); } ``` The expectation spec says that the first `Bar("a")` must happen before check point "1", the second `Bar("a")` must happen after check point "2", and nothing should happen between the two check points. The explicit check points make it easy to tell which `Bar("a")` is called by which call to `Foo()`. ## Mocking Destructors ## Sometimes you want to make sure a mock object is destructed at the right time, e.g. after `bar->A()` is called but before `bar->B()` is called. We already know that you can specify constraints on the order of mock function calls, so all we need to do is to mock the destructor of the mock function. This sounds simple, except for one problem: a destructor is a special function with special syntax and special semantics, and the `MOCK_METHOD0` macro doesn't work for it: ``` MOCK_METHOD0(~MockFoo, void()); // Won't compile! ``` The good news is that you can use a simple pattern to achieve the same effect. First, add a mock function `Die()` to your mock class and call it in the destructor, like this: ``` class MockFoo : public Foo { ... // Add the following two lines to the mock class. MOCK_METHOD0(Die, void()); virtual ~MockFoo() { Die(); } }; ``` (If the name `Die()` clashes with an existing symbol, choose another name.) Now, we have translated the problem of testing when a `MockFoo` object dies to testing when its `Die()` method is called: ``` MockFoo* foo = new MockFoo; MockBar* bar = new MockBar; ... { InSequence s; // Expects *foo to die after bar->A() and before bar->B(). EXPECT_CALL(*bar, A()); EXPECT_CALL(*foo, Die()); EXPECT_CALL(*bar, B()); } ``` And that's that. ## Using Google Mock and Threads ## **IMPORTANT NOTE:** What we describe in this recipe is **ONLY** true on platforms where Google Mock is thread-safe. Currently these are only platforms that support the pthreads library (this includes Linux and Mac). To make it thread-safe on other platforms we only need to implement some synchronization operations in `"gtest/internal/gtest-port.h"`. In a **unit** test, it's best if you could isolate and test a piece of code in a single-threaded context. That avoids race conditions and dead locks, and makes debugging your test much easier. Yet many programs are multi-threaded, and sometimes to test something we need to pound on it from more than one thread. Google Mock works for this purpose too. Remember the steps for using a mock: 1. Create a mock object `foo`. 1. Set its default actions and expectations using `ON_CALL()` and `EXPECT_CALL()`. 1. The code under test calls methods of `foo`. 1. Optionally, verify and reset the mock. 1. Destroy the mock yourself, or let the code under test destroy it. The destructor will automatically verify it. If you follow the following simple rules, your mocks and threads can live happily together: * Execute your _test code_ (as opposed to the code being tested) in _one_ thread. This makes your test easy to follow. * Obviously, you can do step #1 without locking. * When doing step #2 and #5, make sure no other thread is accessing `foo`. Obvious too, huh? * #3 and #4 can be done either in one thread or in multiple threads - anyway you want. Google Mock takes care of the locking, so you don't have to do any - unless required by your test logic. If you violate the rules (for example, if you set expectations on a mock while another thread is calling its methods), you get undefined behavior. That's not fun, so don't do it. Google Mock guarantees that the action for a mock function is done in the same thread that called the mock function. For example, in ``` EXPECT_CALL(mock, Foo(1)) .WillOnce(action1); EXPECT_CALL(mock, Foo(2)) .WillOnce(action2); ``` if `Foo(1)` is called in thread 1 and `Foo(2)` is called in thread 2, Google Mock will execute `action1` in thread 1 and `action2` in thread 2. Google Mock does _not_ impose a sequence on actions performed in different threads (doing so may create deadlocks as the actions may need to cooperate). This means that the execution of `action1` and `action2` in the above example _may_ interleave. If this is a problem, you should add proper synchronization logic to `action1` and `action2` to make the test thread-safe. Also, remember that `DefaultValue` is a global resource that potentially affects _all_ living mock objects in your program. Naturally, you won't want to mess with it from multiple threads or when there still are mocks in action. ## Controlling How Much Information Google Mock Prints ## When Google Mock sees something that has the potential of being an error (e.g. a mock function with no expectation is called, a.k.a. an uninteresting call, which is allowed but perhaps you forgot to explicitly ban the call), it prints some warning messages, including the arguments of the function and the return value. Hopefully this will remind you to take a look and see if there is indeed a problem. Sometimes you are confident that your tests are correct and may not appreciate such friendly messages. Some other times, you are debugging your tests or learning about the behavior of the code you are testing, and wish you could observe every mock call that happens (including argument values and the return value). Clearly, one size doesn't fit all. You can control how much Google Mock tells you using the `--gmock_verbose=LEVEL` command-line flag, where `LEVEL` is a string with three possible values: * `info`: Google Mock will print all informational messages, warnings, and errors (most verbose). At this setting, Google Mock will also log any calls to the `ON_CALL/EXPECT_CALL` macros. * `warning`: Google Mock will print both warnings and errors (less verbose). This is the default. * `error`: Google Mock will print errors only (least verbose). Alternatively, you can adjust the value of that flag from within your tests like so: ``` ::testing::FLAGS_gmock_verbose = "error"; ``` Now, judiciously use the right flag to enable Google Mock serve you better! ## Gaining Super Vision into Mock Calls ## You have a test using Google Mock. It fails: Google Mock tells you that some expectations aren't satisfied. However, you aren't sure why: Is there a typo somewhere in the matchers? Did you mess up the order of the `EXPECT_CALL`s? Or is the code under test doing something wrong? How can you find out the cause? Won't it be nice if you have X-ray vision and can actually see the trace of all `EXPECT_CALL`s and mock method calls as they are made? For each call, would you like to see its actual argument values and which `EXPECT_CALL` Google Mock thinks it matches? You can unlock this power by running your test with the `--gmock_verbose=info` flag. For example, given the test program: ``` using testing::_; using testing::HasSubstr; using testing::Return; class MockFoo { public: MOCK_METHOD2(F, void(const string& x, const string& y)); }; TEST(Foo, Bar) { MockFoo mock; EXPECT_CALL(mock, F(_, _)).WillRepeatedly(Return()); EXPECT_CALL(mock, F("a", "b")); EXPECT_CALL(mock, F("c", HasSubstr("d"))); mock.F("a", "good"); mock.F("a", "b"); } ``` if you run it with `--gmock_verbose=info`, you will see this output: ``` [ RUN ] Foo.Bar foo_test.cc:14: EXPECT_CALL(mock, F(_, _)) invoked foo_test.cc:15: EXPECT_CALL(mock, F("a", "b")) invoked foo_test.cc:16: EXPECT_CALL(mock, F("c", HasSubstr("d"))) invoked foo_test.cc:14: Mock function call matches EXPECT_CALL(mock, F(_, _))... Function call: F(@0x7fff7c8dad40"a", @0x7fff7c8dad10"good") foo_test.cc:15: Mock function call matches EXPECT_CALL(mock, F("a", "b"))... Function call: F(@0x7fff7c8dada0"a", @0x7fff7c8dad70"b") foo_test.cc:16: Failure Actual function call count doesn't match EXPECT_CALL(mock, F("c", HasSubstr("d")))... Expected: to be called once Actual: never called - unsatisfied and active [ FAILED ] Foo.Bar ``` Suppose the bug is that the `"c"` in the third `EXPECT_CALL` is a typo and should actually be `"a"`. With the above message, you should see that the actual `F("a", "good")` call is matched by the first `EXPECT_CALL`, not the third as you thought. From that it should be obvious that the third `EXPECT_CALL` is written wrong. Case solved. ## Running Tests in Emacs ## If you build and run your tests in Emacs, the source file locations of Google Mock and [Google Test](../../googletest/) errors will be highlighted. Just press `` on one of them and you'll be taken to the offending line. Or, you can just type `C-x `` to jump to the next error. To make it even easier, you can add the following lines to your `~/.emacs` file: ``` (global-set-key "\M-m" 'compile) ; m is for make (global-set-key [M-down] 'next-error) (global-set-key [M-up] '(lambda () (interactive) (next-error -1))) ``` Then you can type `M-m` to start a build, or `M-up`/`M-down` to move back and forth between errors. ## Fusing Google Mock Source Files ## Google Mock's implementation consists of dozens of files (excluding its own tests). Sometimes you may want them to be packaged up in fewer files instead, such that you can easily copy them to a new machine and start hacking there. For this we provide an experimental Python script `fuse_gmock_files.py` in the `scripts/` directory (starting with release 1.2.0). Assuming you have Python 2.4 or above installed on your machine, just go to that directory and run ``` python fuse_gmock_files.py OUTPUT_DIR ``` and you should see an `OUTPUT_DIR` directory being created with files `gtest/gtest.h`, `gmock/gmock.h`, and `gmock-gtest-all.cc` in it. These three files contain everything you need to use Google Mock (and Google Test). Just copy them to anywhere you want and you are ready to write tests and use mocks. You can use the [scrpts/test/Makefile](../scripts/test/Makefile) file as an example on how to compile your tests against them. # Extending Google Mock # ## Writing New Matchers Quickly ## The `MATCHER*` family of macros can be used to define custom matchers easily. The syntax: ``` MATCHER(name, description_string_expression) { statements; } ``` will define a matcher with the given name that executes the statements, which must return a `bool` to indicate if the match succeeds. Inside the statements, you can refer to the value being matched by `arg`, and refer to its type by `arg_type`. The description string is a `string`-typed expression that documents what the matcher does, and is used to generate the failure message when the match fails. It can (and should) reference the special `bool` variable `negation`, and should evaluate to the description of the matcher when `negation` is `false`, or that of the matcher's negation when `negation` is `true`. For convenience, we allow the description string to be empty (`""`), in which case Google Mock will use the sequence of words in the matcher name as the description. For example: ``` MATCHER(IsDivisibleBy7, "") { return (arg % 7) == 0; } ``` allows you to write ``` // Expects mock_foo.Bar(n) to be called where n is divisible by 7. EXPECT_CALL(mock_foo, Bar(IsDivisibleBy7())); ``` or, ``` using ::testing::Not; ... EXPECT_THAT(some_expression, IsDivisibleBy7()); EXPECT_THAT(some_other_expression, Not(IsDivisibleBy7())); ``` If the above assertions fail, they will print something like: ``` Value of: some_expression Expected: is divisible by 7 Actual: 27 ... Value of: some_other_expression Expected: not (is divisible by 7) Actual: 21 ``` where the descriptions `"is divisible by 7"` and `"not (is divisible by 7)"` are automatically calculated from the matcher name `IsDivisibleBy7`. As you may have noticed, the auto-generated descriptions (especially those for the negation) may not be so great. You can always override them with a string expression of your own: ``` MATCHER(IsDivisibleBy7, std::string(negation ? "isn't" : "is") + " divisible by 7") { return (arg % 7) == 0; } ``` Optionally, you can stream additional information to a hidden argument named `result_listener` to explain the match result. For example, a better definition of `IsDivisibleBy7` is: ``` MATCHER(IsDivisibleBy7, "") { if ((arg % 7) == 0) return true; *result_listener << "the remainder is " << (arg % 7); return false; } ``` With this definition, the above assertion will give a better message: ``` Value of: some_expression Expected: is divisible by 7 Actual: 27 (the remainder is 6) ``` You should let `MatchAndExplain()` print _any additional information_ that can help a user understand the match result. Note that it should explain why the match succeeds in case of a success (unless it's obvious) - this is useful when the matcher is used inside `Not()`. There is no need to print the argument value itself, as Google Mock already prints it for you. **Notes:** 1. The type of the value being matched (`arg_type`) is determined by the context in which you use the matcher and is supplied to you by the compiler, so you don't need to worry about declaring it (nor can you). This allows the matcher to be polymorphic. For example, `IsDivisibleBy7()` can be used to match any type where the value of `(arg % 7) == 0` can be implicitly converted to a `bool`. In the `Bar(IsDivisibleBy7())` example above, if method `Bar()` takes an `int`, `arg_type` will be `int`; if it takes an `unsigned long`, `arg_type` will be `unsigned long`; and so on. 1. Google Mock doesn't guarantee when or how many times a matcher will be invoked. Therefore the matcher logic must be _purely functional_ (i.e. it cannot have any side effect, and the result must not depend on anything other than the value being matched and the matcher parameters). This requirement must be satisfied no matter how you define the matcher (e.g. using one of the methods described in the following recipes). In particular, a matcher can never call a mock function, as that will affect the state of the mock object and Google Mock. ## Writing New Parameterized Matchers Quickly ## Sometimes you'll want to define a matcher that has parameters. For that you can use the macro: ``` MATCHER_P(name, param_name, description_string) { statements; } ``` where the description string can be either `""` or a string expression that references `negation` and `param_name`. For example: ``` MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } ``` will allow you to write: ``` EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); ``` which may lead to this message (assuming `n` is 10): ``` Value of: Blah("a") Expected: has absolute value 10 Actual: -9 ``` Note that both the matcher description and its parameter are printed, making the message human-friendly. In the matcher definition body, you can write `foo_type` to reference the type of a parameter named `foo`. For example, in the body of `MATCHER_P(HasAbsoluteValue, value)` above, you can write `value_type` to refer to the type of `value`. Google Mock also provides `MATCHER_P2`, `MATCHER_P3`, ..., up to `MATCHER_P10` to support multi-parameter matchers: ``` MATCHER_Pk(name, param_1, ..., param_k, description_string) { statements; } ``` Please note that the custom description string is for a particular **instance** of the matcher, where the parameters have been bound to actual values. Therefore usually you'll want the parameter values to be part of the description. Google Mock lets you do that by referencing the matcher parameters in the description string expression. For example, ``` using ::testing::PrintToString; MATCHER_P2(InClosedRange, low, hi, std::string(negation ? "isn't" : "is") + " in range [" + PrintToString(low) + ", " + PrintToString(hi) + "]") { return low <= arg && arg <= hi; } ... EXPECT_THAT(3, InClosedRange(4, 6)); ``` would generate a failure that contains the message: ``` Expected: is in range [4, 6] ``` If you specify `""` as the description, the failure message will contain the sequence of words in the matcher name followed by the parameter values printed as a tuple. For example, ``` MATCHER_P2(InClosedRange, low, hi, "") { ... } ... EXPECT_THAT(3, InClosedRange(4, 6)); ``` would generate a failure that contains the text: ``` Expected: in closed range (4, 6) ``` For the purpose of typing, you can view ``` MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } ``` as shorthand for ``` template FooMatcherPk Foo(p1_type p1, ..., pk_type pk) { ... } ``` When you write `Foo(v1, ..., vk)`, the compiler infers the types of the parameters `v1`, ..., and `vk` for you. If you are not happy with the result of the type inference, you can specify the types by explicitly instantiating the template, as in `Foo(5, false)`. As said earlier, you don't get to (or need to) specify `arg_type` as that's determined by the context in which the matcher is used. You can assign the result of expression `Foo(p1, ..., pk)` to a variable of type `FooMatcherPk`. This can be useful when composing matchers. Matchers that don't have a parameter or have only one parameter have special types: you can assign `Foo()` to a `FooMatcher`-typed variable, and assign `Foo(p)` to a `FooMatcherP`-typed variable. While you can instantiate a matcher template with reference types, passing the parameters by pointer usually makes your code more readable. If, however, you still want to pass a parameter by reference, be aware that in the failure message generated by the matcher you will see the value of the referenced object but not its address. You can overload matchers with different numbers of parameters: ``` MATCHER_P(Blah, a, description_string_1) { ... } MATCHER_P2(Blah, a, b, description_string_2) { ... } ``` While it's tempting to always use the `MATCHER*` macros when defining a new matcher, you should also consider implementing `MatcherInterface` or using `MakePolymorphicMatcher()` instead (see the recipes that follow), especially if you need to use the matcher a lot. While these approaches require more work, they give you more control on the types of the value being matched and the matcher parameters, which in general leads to better compiler error messages that pay off in the long run. They also allow overloading matchers based on parameter types (as opposed to just based on the number of parameters). ## Writing New Monomorphic Matchers ## A matcher of argument type `T` implements `::testing::MatcherInterface` and does two things: it tests whether a value of type `T` matches the matcher, and can describe what kind of values it matches. The latter ability is used for generating readable error messages when expectations are violated. The interface looks like this: ``` class MatchResultListener { public: ... // Streams x to the underlying ostream; does nothing if the ostream // is NULL. template MatchResultListener& operator<<(const T& x); // Returns the underlying ostream. ::std::ostream* stream(); }; template class MatcherInterface { public: virtual ~MatcherInterface(); // Returns true iff the matcher matches x; also explains the match // result to 'listener'. virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; // Describes this matcher to an ostream. virtual void DescribeTo(::std::ostream* os) const = 0; // Describes the negation of this matcher to an ostream. virtual void DescribeNegationTo(::std::ostream* os) const; }; ``` If you need a custom matcher but `Truly()` is not a good option (for example, you may not be happy with the way `Truly(predicate)` describes itself, or you may want your matcher to be polymorphic as `Eq(value)` is), you can define a matcher to do whatever you want in two steps: first implement the matcher interface, and then define a factory function to create a matcher instance. The second step is not strictly needed but it makes the syntax of using the matcher nicer. For example, you can define a matcher to test whether an `int` is divisible by 7 and then use it like this: ``` using ::testing::MakeMatcher; using ::testing::Matcher; using ::testing::MatcherInterface; using ::testing::MatchResultListener; class DivisibleBy7Matcher : public MatcherInterface { public: virtual bool MatchAndExplain(int n, MatchResultListener* listener) const { return (n % 7) == 0; } virtual void DescribeTo(::std::ostream* os) const { *os << "is divisible by 7"; } virtual void DescribeNegationTo(::std::ostream* os) const { *os << "is not divisible by 7"; } }; inline Matcher DivisibleBy7() { return MakeMatcher(new DivisibleBy7Matcher); } ... EXPECT_CALL(foo, Bar(DivisibleBy7())); ``` You may improve the matcher message by streaming additional information to the `listener` argument in `MatchAndExplain()`: ``` class DivisibleBy7Matcher : public MatcherInterface { public: virtual bool MatchAndExplain(int n, MatchResultListener* listener) const { const int remainder = n % 7; if (remainder != 0) { *listener << "the remainder is " << remainder; } return remainder == 0; } ... }; ``` Then, `EXPECT_THAT(x, DivisibleBy7());` may general a message like this: ``` Value of: x Expected: is divisible by 7 Actual: 23 (the remainder is 2) ``` ## Writing New Polymorphic Matchers ## You've learned how to write your own matchers in the previous recipe. Just one problem: a matcher created using `MakeMatcher()` only works for one particular type of arguments. If you want a _polymorphic_ matcher that works with arguments of several types (for instance, `Eq(x)` can be used to match a `value` as long as `value` == `x` compiles -- `value` and `x` don't have to share the same type), you can learn the trick from `"gmock/gmock-matchers.h"` but it's a bit involved. Fortunately, most of the time you can define a polymorphic matcher easily with the help of `MakePolymorphicMatcher()`. Here's how you can define `NotNull()` as an example: ``` using ::testing::MakePolymorphicMatcher; using ::testing::MatchResultListener; using ::testing::NotNull; using ::testing::PolymorphicMatcher; class NotNullMatcher { public: // To implement a polymorphic matcher, first define a COPYABLE class // that has three members MatchAndExplain(), DescribeTo(), and // DescribeNegationTo(), like the following. // In this example, we want to use NotNull() with any pointer, so // MatchAndExplain() accepts a pointer of any type as its first argument. // In general, you can define MatchAndExplain() as an ordinary method or // a method template, or even overload it. template bool MatchAndExplain(T* p, MatchResultListener* /* listener */) const { return p != NULL; } // Describes the property of a value matching this matcher. void DescribeTo(::std::ostream* os) const { *os << "is not NULL"; } // Describes the property of a value NOT matching this matcher. void DescribeNegationTo(::std::ostream* os) const { *os << "is NULL"; } }; // To construct a polymorphic matcher, pass an instance of the class // to MakePolymorphicMatcher(). Note the return type. inline PolymorphicMatcher NotNull() { return MakePolymorphicMatcher(NotNullMatcher()); } ... EXPECT_CALL(foo, Bar(NotNull())); // The argument must be a non-NULL pointer. ``` **Note:** Your polymorphic matcher class does **not** need to inherit from `MatcherInterface` or any other class, and its methods do **not** need to be virtual. Like in a monomorphic matcher, you may explain the match result by streaming additional information to the `listener` argument in `MatchAndExplain()`. ## Writing New Cardinalities ## A cardinality is used in `Times()` to tell Google Mock how many times you expect a call to occur. It doesn't have to be exact. For example, you can say `AtLeast(5)` or `Between(2, 4)`. If the built-in set of cardinalities doesn't suit you, you are free to define your own by implementing the following interface (in namespace `testing`): ``` class CardinalityInterface { public: virtual ~CardinalityInterface(); // Returns true iff call_count calls will satisfy this cardinality. virtual bool IsSatisfiedByCallCount(int call_count) const = 0; // Returns true iff call_count calls will saturate this cardinality. virtual bool IsSaturatedByCallCount(int call_count) const = 0; // Describes self to an ostream. virtual void DescribeTo(::std::ostream* os) const = 0; }; ``` For example, to specify that a call must occur even number of times, you can write ``` using ::testing::Cardinality; using ::testing::CardinalityInterface; using ::testing::MakeCardinality; class EvenNumberCardinality : public CardinalityInterface { public: virtual bool IsSatisfiedByCallCount(int call_count) const { return (call_count % 2) == 0; } virtual bool IsSaturatedByCallCount(int call_count) const { return false; } virtual void DescribeTo(::std::ostream* os) const { *os << "called even number of times"; } }; Cardinality EvenNumber() { return MakeCardinality(new EvenNumberCardinality); } ... EXPECT_CALL(foo, Bar(3)) .Times(EvenNumber()); ``` ## Writing New Actions Quickly ## If the built-in actions don't work for you, and you find it inconvenient to use `Invoke()`, you can use a macro from the `ACTION*` family to quickly define a new action that can be used in your code as if it's a built-in action. By writing ``` ACTION(name) { statements; } ``` in a namespace scope (i.e. not inside a class or function), you will define an action with the given name that executes the statements. The value returned by `statements` will be used as the return value of the action. Inside the statements, you can refer to the K-th (0-based) argument of the mock function as `argK`. For example: ``` ACTION(IncrementArg1) { return ++(*arg1); } ``` allows you to write ``` ... WillOnce(IncrementArg1()); ``` Note that you don't need to specify the types of the mock function arguments. Rest assured that your code is type-safe though: you'll get a compiler error if `*arg1` doesn't support the `++` operator, or if the type of `++(*arg1)` isn't compatible with the mock function's return type. Another example: ``` ACTION(Foo) { (*arg2)(5); Blah(); *arg1 = 0; return arg0; } ``` defines an action `Foo()` that invokes argument #2 (a function pointer) with 5, calls function `Blah()`, sets the value pointed to by argument #1 to 0, and returns argument #0. For more convenience and flexibility, you can also use the following pre-defined symbols in the body of `ACTION`: | `argK_type` | The type of the K-th (0-based) argument of the mock function | |:------------|:-------------------------------------------------------------| | `args` | All arguments of the mock function as a tuple | | `args_type` | The type of all arguments of the mock function as a tuple | | `return_type` | The return type of the mock function | | `function_type` | The type of the mock function | For example, when using an `ACTION` as a stub action for mock function: ``` int DoSomething(bool flag, int* ptr); ``` we have: | **Pre-defined Symbol** | **Is Bound To** | |:-----------------------|:----------------| | `arg0` | the value of `flag` | | `arg0_type` | the type `bool` | | `arg1` | the value of `ptr` | | `arg1_type` | the type `int*` | | `args` | the tuple `(flag, ptr)` | | `args_type` | the type `::testing::tuple` | | `return_type` | the type `int` | | `function_type` | the type `int(bool, int*)` | ## Writing New Parameterized Actions Quickly ## Sometimes you'll want to parameterize an action you define. For that we have another macro ``` ACTION_P(name, param) { statements; } ``` For example, ``` ACTION_P(Add, n) { return arg0 + n; } ``` will allow you to write ``` // Returns argument #0 + 5. ... WillOnce(Add(5)); ``` For convenience, we use the term _arguments_ for the values used to invoke the mock function, and the term _parameters_ for the values used to instantiate an action. Note that you don't need to provide the type of the parameter either. Suppose the parameter is named `param`, you can also use the Google-Mock-defined symbol `param_type` to refer to the type of the parameter as inferred by the compiler. For example, in the body of `ACTION_P(Add, n)` above, you can write `n_type` for the type of `n`. Google Mock also provides `ACTION_P2`, `ACTION_P3`, and etc to support multi-parameter actions. For example, ``` ACTION_P2(ReturnDistanceTo, x, y) { double dx = arg0 - x; double dy = arg1 - y; return sqrt(dx*dx + dy*dy); } ``` lets you write ``` ... WillOnce(ReturnDistanceTo(5.0, 26.5)); ``` You can view `ACTION` as a degenerated parameterized action where the number of parameters is 0. You can also easily define actions overloaded on the number of parameters: ``` ACTION_P(Plus, a) { ... } ACTION_P2(Plus, a, b) { ... } ``` ## Restricting the Type of an Argument or Parameter in an ACTION ## For maximum brevity and reusability, the `ACTION*` macros don't ask you to provide the types of the mock function arguments and the action parameters. Instead, we let the compiler infer the types for us. Sometimes, however, we may want to be more explicit about the types. There are several tricks to do that. For example: ``` ACTION(Foo) { // Makes sure arg0 can be converted to int. int n = arg0; ... use n instead of arg0 here ... } ACTION_P(Bar, param) { // Makes sure the type of arg1 is const char*. ::testing::StaticAssertTypeEq(); // Makes sure param can be converted to bool. bool flag = param; } ``` where `StaticAssertTypeEq` is a compile-time assertion in Google Test that verifies two types are the same. ## Writing New Action Templates Quickly ## Sometimes you want to give an action explicit template parameters that cannot be inferred from its value parameters. `ACTION_TEMPLATE()` supports that and can be viewed as an extension to `ACTION()` and `ACTION_P*()`. The syntax: ``` ACTION_TEMPLATE(ActionName, HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } ``` defines an action template that takes _m_ explicit template parameters and _n_ value parameters, where _m_ is between 1 and 10, and _n_ is between 0 and 10. `name_i` is the name of the i-th template parameter, and `kind_i` specifies whether it's a `typename`, an integral constant, or a template. `p_i` is the name of the i-th value parameter. Example: ``` // DuplicateArg(output) converts the k-th argument of the mock // function to type T and copies it to *output. ACTION_TEMPLATE(DuplicateArg, // Note the comma between int and k: HAS_2_TEMPLATE_PARAMS(int, k, typename, T), AND_1_VALUE_PARAMS(output)) { *output = T(::testing::get(args)); } ``` To create an instance of an action template, write: ``` ActionName(v1, ..., v_n) ``` where the `t`s are the template arguments and the `v`s are the value arguments. The value argument types are inferred by the compiler. For example: ``` using ::testing::_; ... int n; EXPECT_CALL(mock, Foo(_, _)) .WillOnce(DuplicateArg<1, unsigned char>(&n)); ``` If you want to explicitly specify the value argument types, you can provide additional template arguments: ``` ActionName(v1, ..., v_n) ``` where `u_i` is the desired type of `v_i`. `ACTION_TEMPLATE` and `ACTION`/`ACTION_P*` can be overloaded on the number of value parameters, but not on the number of template parameters. Without the restriction, the meaning of the following is unclear: ``` OverloadedAction(x); ``` Are we using a single-template-parameter action where `bool` refers to the type of `x`, or a two-template-parameter action where the compiler is asked to infer the type of `x`? ## Using the ACTION Object's Type ## If you are writing a function that returns an `ACTION` object, you'll need to know its type. The type depends on the macro used to define the action and the parameter types. The rule is relatively simple: | **Given Definition** | **Expression** | **Has Type** | |:---------------------|:---------------|:-------------| | `ACTION(Foo)` | `Foo()` | `FooAction` | | `ACTION_TEMPLATE(Foo, HAS_m_TEMPLATE_PARAMS(...), AND_0_VALUE_PARAMS())` | `Foo()` | `FooAction` | | `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP` | | `ACTION_TEMPLATE(Bar, HAS_m_TEMPLATE_PARAMS(...), AND_1_VALUE_PARAMS(p1))` | `Bar(int_value)` | `FooActionP` | | `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2` | | `ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))`| `Baz(bool_value, int_value)` | `FooActionP2` | | ... | ... | ... | Note that we have to pick different suffixes (`Action`, `ActionP`, `ActionP2`, and etc) for actions with different numbers of value parameters, or the action definitions cannot be overloaded on the number of them. ## Writing New Monomorphic Actions ## While the `ACTION*` macros are very convenient, sometimes they are inappropriate. For example, despite the tricks shown in the previous recipes, they don't let you directly specify the types of the mock function arguments and the action parameters, which in general leads to unoptimized compiler error messages that can baffle unfamiliar users. They also don't allow overloading actions based on parameter types without jumping through some hoops. An alternative to the `ACTION*` macros is to implement `::testing::ActionInterface`, where `F` is the type of the mock function in which the action will be used. For example: ``` template class ActionInterface { public: virtual ~ActionInterface(); // Performs the action. Result is the return type of function type // F, and ArgumentTuple is the tuple of arguments of F. // // For example, if F is int(bool, const string&), then Result would // be int, and ArgumentTuple would be ::testing::tuple. virtual Result Perform(const ArgumentTuple& args) = 0; }; using ::testing::_; using ::testing::Action; using ::testing::ActionInterface; using ::testing::MakeAction; typedef int IncrementMethod(int*); class IncrementArgumentAction : public ActionInterface { public: virtual int Perform(const ::testing::tuple& args) { int* p = ::testing::get<0>(args); // Grabs the first argument. return *p++; } }; Action IncrementArgument() { return MakeAction(new IncrementArgumentAction); } ... EXPECT_CALL(foo, Baz(_)) .WillOnce(IncrementArgument()); int n = 5; foo.Baz(&n); // Should return 5 and change n to 6. ``` ## Writing New Polymorphic Actions ## The previous recipe showed you how to define your own action. This is all good, except that you need to know the type of the function in which the action will be used. Sometimes that can be a problem. For example, if you want to use the action in functions with _different_ types (e.g. like `Return()` and `SetArgPointee()`). If an action can be used in several types of mock functions, we say it's _polymorphic_. The `MakePolymorphicAction()` function template makes it easy to define such an action: ``` namespace testing { template PolymorphicAction MakePolymorphicAction(const Impl& impl); } // namespace testing ``` As an example, let's define an action that returns the second argument in the mock function's argument list. The first step is to define an implementation class: ``` class ReturnSecondArgumentAction { public: template Result Perform(const ArgumentTuple& args) const { // To get the i-th (0-based) argument, use ::testing::get(args). return ::testing::get<1>(args); } }; ``` This implementation class does _not_ need to inherit from any particular class. What matters is that it must have a `Perform()` method template. This method template takes the mock function's arguments as a tuple in a **single** argument, and returns the result of the action. It can be either `const` or not, but must be invokable with exactly one template argument, which is the result type. In other words, you must be able to call `Perform(args)` where `R` is the mock function's return type and `args` is its arguments in a tuple. Next, we use `MakePolymorphicAction()` to turn an instance of the implementation class into the polymorphic action we need. It will be convenient to have a wrapper for this: ``` using ::testing::MakePolymorphicAction; using ::testing::PolymorphicAction; PolymorphicAction ReturnSecondArgument() { return MakePolymorphicAction(ReturnSecondArgumentAction()); } ``` Now, you can use this polymorphic action the same way you use the built-in ones: ``` using ::testing::_; class MockFoo : public Foo { public: MOCK_METHOD2(DoThis, int(bool flag, int n)); MOCK_METHOD3(DoThat, string(int x, const char* str1, const char* str2)); }; ... MockFoo foo; EXPECT_CALL(foo, DoThis(_, _)) .WillOnce(ReturnSecondArgument()); EXPECT_CALL(foo, DoThat(_, _, _)) .WillOnce(ReturnSecondArgument()); ... foo.DoThis(true, 5); // Will return 5. foo.DoThat(1, "Hi", "Bye"); // Will return "Hi". ``` ## Teaching Google Mock How to Print Your Values ## When an uninteresting or unexpected call occurs, Google Mock prints the argument values and the stack trace to help you debug. Assertion macros like `EXPECT_THAT` and `EXPECT_EQ` also print the values in question when the assertion fails. Google Mock and Google Test do this using Google Test's user-extensible value printer. This printer knows how to print built-in C++ types, native arrays, STL containers, and any type that supports the `<<` operator. For other types, it prints the raw bytes in the value and hopes that you the user can figure it out. [Google Test's advanced guide](../../googletest/docs/advanced.md#teaching-google-test-how-to-print-your-values) explains how to extend the printer to do a better job at printing your particular type than to dump the bytes. ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/docs/DesignDoc.md ================================================ This page discusses the design of new Google Mock features. # Macros for Defining Actions # ## Problem ## Due to the lack of closures in C++, it currently requires some non-trivial effort to define a custom action in Google Mock. For example, suppose you want to "increment the value pointed to by the second argument of the mock function and return it", you could write: ``` int IncrementArg1(Unused, int* p, Unused) { return ++(*p); } ... WillOnce(Invoke(IncrementArg1)); ``` There are several things unsatisfactory about this approach: * Even though the action only cares about the second argument of the mock function, its definition needs to list other arguments as dummies. This is tedious. * The defined action is usable only in mock functions that takes exactly 3 arguments - an unnecessary restriction. * To use the action, one has to say `Invoke(IncrementArg1)`, which isn't as nice as `IncrementArg1()`. The latter two problems can be overcome using `MakePolymorphicAction()`, but it requires much more boilerplate code: ``` class IncrementArg1Action { public: template Result Perform(const ArgumentTuple& args) const { return ++(*tr1::get<1>(args)); } }; PolymorphicAction IncrementArg1() { return MakePolymorphicAction(IncrementArg1Action()); } ... WillOnce(IncrementArg1()); ``` Our goal is to allow defining custom actions with the least amount of boiler-plate C++ requires. ## Solution ## We propose to introduce a new macro: ``` ACTION(name) { statements; } ``` Using this in a namespace scope will define an action with the given name that executes the statements. Inside the statements, you can refer to the K-th (0-based) argument of the mock function as `argK`. For example: ``` ACTION(IncrementArg1) { return ++(*arg1); } ``` allows you to write ``` ... WillOnce(IncrementArg1()); ``` Note that you don't need to specify the types of the mock function arguments, as brevity is a top design goal here. Rest assured that your code is still type-safe though: you'll get a compiler error if `*arg1` doesn't support the `++` operator, or if the type of `++(*arg1)` isn't compatible with the mock function's return type. Another example: ``` ACTION(Foo) { (*arg2)(5); Blah(); *arg1 = 0; return arg0; } ``` defines an action `Foo()` that invokes argument #2 (a function pointer) with 5, calls function `Blah()`, sets the value pointed to by argument #1 to 0, and returns argument #0. For more convenience and flexibility, you can also use the following pre-defined symbols in the body of `ACTION`: | `argK_type` | The type of the K-th (0-based) argument of the mock function | |:------------|:-------------------------------------------------------------| | `args` | All arguments of the mock function as a tuple | | `args_type` | The type of all arguments of the mock function as a tuple | | `return_type` | The return type of the mock function | | `function_type` | The type of the mock function | For example, when using an `ACTION` as a stub action for mock function: ``` int DoSomething(bool flag, int* ptr); ``` we have: | **Pre-defined Symbol** | **Is Bound To** | |:-----------------------|:----------------| | `arg0` | the value of `flag` | | `arg0_type` | the type `bool` | | `arg1` | the value of `ptr` | | `arg1_type` | the type `int*` | | `args` | the tuple `(flag, ptr)` | | `args_type` | the type `std::tr1::tuple` | | `return_type` | the type `int` | | `function_type` | the type `int(bool, int*)` | ## Parameterized actions ## Sometimes you'll want to parameterize the action. For that we propose another macro ``` ACTION_P(name, param) { statements; } ``` For example, ``` ACTION_P(Add, n) { return arg0 + n; } ``` will allow you to write ``` // Returns argument #0 + 5. ... WillOnce(Add(5)); ``` For convenience, we use the term _arguments_ for the values used to invoke the mock function, and the term _parameters_ for the values used to instantiate an action. Note that you don't need to provide the type of the parameter either. Suppose the parameter is named `param`, you can also use the Google-Mock-defined symbol `param_type` to refer to the type of the parameter as inferred by the compiler. We will also provide `ACTION_P2`, `ACTION_P3`, and etc to support multi-parameter actions. For example, ``` ACTION_P2(ReturnDistanceTo, x, y) { double dx = arg0 - x; double dy = arg1 - y; return sqrt(dx*dx + dy*dy); } ``` lets you write ``` ... WillOnce(ReturnDistanceTo(5.0, 26.5)); ``` You can view `ACTION` as a degenerated parameterized action where the number of parameters is 0. ## Advanced Usages ## ### Overloading Actions ### You can easily define actions overloaded on the number of parameters: ``` ACTION_P(Plus, a) { ... } ACTION_P2(Plus, a, b) { ... } ``` ### Restricting the Type of an Argument or Parameter ### For maximum brevity and reusability, the `ACTION*` macros don't let you specify the types of the mock function arguments and the action parameters. Instead, we let the compiler infer the types for us. Sometimes, however, we may want to be more explicit about the types. There are several tricks to do that. For example: ``` ACTION(Foo) { // Makes sure arg0 can be converted to int. int n = arg0; ... use n instead of arg0 here ... } ACTION_P(Bar, param) { // Makes sure the type of arg1 is const char*. ::testing::StaticAssertTypeEq(); // Makes sure param can be converted to bool. bool flag = param; } ``` where `StaticAssertTypeEq` is a compile-time assertion we plan to add to Google Test (the name is chosen to match `static_assert` in C++0x). ### Using the ACTION Object's Type ### If you are writing a function that returns an `ACTION` object, you'll need to know its type. The type depends on the macro used to define the action and the parameter types. The rule is relatively simple: | **Given Definition** | **Expression** | **Has Type** | |:---------------------|:---------------|:-------------| | `ACTION(Foo)` | `Foo()` | `FooAction` | | `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP` | | `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2` | | ... | ... | ... | Note that we have to pick different suffixes (`Action`, `ActionP`, `ActionP2`, and etc) for actions with different numbers of parameters, or the action definitions cannot be overloaded on the number of parameters. ## When to Use ## While the new macros are very convenient, please also consider other means of implementing actions (e.g. via `ActionInterface` or `MakePolymorphicAction()`), especially if you need to use the defined action a lot. While the other approaches require more work, they give you more control on the types of the mock function arguments and the action parameters, which in general leads to better compiler error messages that pay off in the long run. They also allow overloading actions based on parameter types, as opposed to just the number of parameters. ## Related Work ## As you may have realized, the `ACTION*` macros resemble closures (also known as lambda expressions or anonymous functions). Indeed, both of them seek to lower the syntactic overhead for defining a function. C++0x will support lambdas, but they are not part of C++ right now. Some non-standard libraries (most notably BLL or Boost Lambda Library) try to alleviate this problem. However, they are not a good choice for defining actions as: * They are non-standard and not widely installed. Google Mock only depends on standard libraries and `tr1::tuple`, which is part of the new C++ standard and comes with gcc 4+. We want to keep it that way. * They are not trivial to learn. * They will become obsolete when C++0x's lambda feature is widely supported. We don't want to make our users use a dying library. * Since they are based on operators, they are rather ad hoc: you cannot use statements, and you cannot pass the lambda arguments to a function, for example. * They have subtle semantics that easily confuses new users. For example, in expression `_1++ + foo++`, `foo` will be incremented only once where the expression is evaluated, while `_1` will be incremented every time the unnamed function is invoked. This is far from intuitive. `ACTION*` avoid all these problems. ## Future Improvements ## There may be a need for composing `ACTION*` definitions (i.e. invoking another `ACTION` inside the definition of one `ACTION*`). We are not sure we want it yet, as one can get a similar effect by putting `ACTION` definitions in function templates and composing the function templates. We'll revisit this based on user feedback. The reason we don't allow `ACTION*()` inside a function body is that the current C++ standard doesn't allow function-local types to be used to instantiate templates. The upcoming C++0x standard will lift this restriction. Once this feature is widely supported by compilers, we can revisit the implementation and add support for using `ACTION*()` inside a function. C++0x will also support lambda expressions. When they become available, we may want to support using lambdas as actions. # Macros for Defining Matchers # Once the macros for defining actions are implemented, we plan to do the same for matchers: ``` MATCHER(name) { statements; } ``` where you can refer to the value being matched as `arg`. For example, given: ``` MATCHER(IsPositive) { return arg > 0; } ``` you can use `IsPositive()` as a matcher that matches a value iff it is greater than 0. We will also add `MATCHER_P`, `MATCHER_P2`, and etc for parameterized matchers. ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/docs/Documentation.md ================================================ This page lists all documentation markdown files for Google Mock **(the current git version)** -- **if you use a former version of Google Mock, please read the documentation for that specific version instead (e.g. by checking out the respective git branch/tag).** * [ForDummies](ForDummies.md) -- start here if you are new to Google Mock. * [CheatSheet](CheatSheet.md) -- a quick reference. * [CookBook](CookBook.md) -- recipes for doing various tasks using Google Mock. * [FrequentlyAskedQuestions](FrequentlyAskedQuestions.md) -- check here before asking a question on the mailing list. To contribute code to Google Mock, read: * [CONTRIBUTING](../CONTRIBUTING.md) -- read this _before_ writing your first patch. * [Pump Manual](../../googletest/docs/PumpManual.md) -- how we generate some of Google Mock's source files. ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/docs/ForDummies.md ================================================ (**Note:** If you get compiler errors that you don't understand, be sure to consult [Google Mock Doctor](FrequentlyAskedQuestions.md#how-am-i-supposed-to-make-sense-of-these-horrible-template-errors).) # What Is Google C++ Mocking Framework? # When you write a prototype or test, often it's not feasible or wise to rely on real objects entirely. A **mock object** implements the same interface as a real object (so it can be used as one), but lets you specify at run time how it will be used and what it should do (which methods will be called? in which order? how many times? with what arguments? what will they return? etc). **Note:** It is easy to confuse the term _fake objects_ with mock objects. Fakes and mocks actually mean very different things in the Test-Driven Development (TDD) community: * **Fake** objects have working implementations, but usually take some shortcut (perhaps to make the operations less expensive), which makes them not suitable for production. An in-memory file system would be an example of a fake. * **Mocks** are objects pre-programmed with _expectations_, which form a specification of the calls they are expected to receive. If all this seems too abstract for you, don't worry - the most important thing to remember is that a mock allows you to check the _interaction_ between itself and code that uses it. The difference between fakes and mocks will become much clearer once you start to use mocks. **Google C++ Mocking Framework** (or **Google Mock** for short) is a library (sometimes we also call it a "framework" to make it sound cool) for creating mock classes and using them. It does to C++ what [jMock](http://www.jmock.org/) and [EasyMock](http://www.easymock.org/) do to Java. Using Google Mock involves three basic steps: 1. Use some simple macros to describe the interface you want to mock, and they will expand to the implementation of your mock class; 1. Create some mock objects and specify its expectations and behavior using an intuitive syntax; 1. Exercise code that uses the mock objects. Google Mock will catch any violation of the expectations as soon as it arises. # Why Google Mock? # While mock objects help you remove unnecessary dependencies in tests and make them fast and reliable, using mocks manually in C++ is _hard_: * Someone has to implement the mocks. The job is usually tedious and error-prone. No wonder people go great distances to avoid it. * The quality of those manually written mocks is a bit, uh, unpredictable. You may see some really polished ones, but you may also see some that were hacked up in a hurry and have all sorts of ad-hoc restrictions. * The knowledge you gained from using one mock doesn't transfer to the next. In contrast, Java and Python programmers have some fine mock frameworks, which automate the creation of mocks. As a result, mocking is a proven effective technique and widely adopted practice in those communities. Having the right tool absolutely makes the difference. Google Mock was built to help C++ programmers. It was inspired by [jMock](http://www.jmock.org/) and [EasyMock](http://www.easymock.org/), but designed with C++'s specifics in mind. It is your friend if any of the following problems is bothering you: * You are stuck with a sub-optimal design and wish you had done more prototyping before it was too late, but prototyping in C++ is by no means "rapid". * Your tests are slow as they depend on too many libraries or use expensive resources (e.g. a database). * Your tests are brittle as some resources they use are unreliable (e.g. the network). * You want to test how your code handles a failure (e.g. a file checksum error), but it's not easy to cause one. * You need to make sure that your module interacts with other modules in the right way, but it's hard to observe the interaction; therefore you resort to observing the side effects at the end of the action, which is awkward at best. * You want to "mock out" your dependencies, except that they don't have mock implementations yet; and, frankly, you aren't thrilled by some of those hand-written mocks. We encourage you to use Google Mock as: * a _design_ tool, for it lets you experiment with your interface design early and often. More iterations lead to better designs! * a _testing_ tool to cut your tests' outbound dependencies and probe the interaction between your module and its collaborators. # Getting Started # Using Google Mock is easy! Inside your C++ source file, just `#include` `"gtest/gtest.h"` and `"gmock/gmock.h"`, and you are ready to go. # A Case for Mock Turtles # Let's look at an example. Suppose you are developing a graphics program that relies on a LOGO-like API for drawing. How would you test that it does the right thing? Well, you can run it and compare the screen with a golden screen snapshot, but let's admit it: tests like this are expensive to run and fragile (What if you just upgraded to a shiny new graphics card that has better anti-aliasing? Suddenly you have to update all your golden images.). It would be too painful if all your tests are like this. Fortunately, you learned about Dependency Injection and know the right thing to do: instead of having your application talk to the drawing API directly, wrap the API in an interface (say, `Turtle`) and code to that interface: ``` class Turtle { ... virtual ~Turtle() {} virtual void PenUp() = 0; virtual void PenDown() = 0; virtual void Forward(int distance) = 0; virtual void Turn(int degrees) = 0; virtual void GoTo(int x, int y) = 0; virtual int GetX() const = 0; virtual int GetY() const = 0; }; ``` (Note that the destructor of `Turtle` **must** be virtual, as is the case for **all** classes you intend to inherit from - otherwise the destructor of the derived class will not be called when you delete an object through a base pointer, and you'll get corrupted program states like memory leaks.) You can control whether the turtle's movement will leave a trace using `PenUp()` and `PenDown()`, and control its movement using `Forward()`, `Turn()`, and `GoTo()`. Finally, `GetX()` and `GetY()` tell you the current position of the turtle. Your program will normally use a real implementation of this interface. In tests, you can use a mock implementation instead. This allows you to easily check what drawing primitives your program is calling, with what arguments, and in which order. Tests written this way are much more robust (they won't break because your new machine does anti-aliasing differently), easier to read and maintain (the intent of a test is expressed in the code, not in some binary images), and run _much, much faster_. # Writing the Mock Class # If you are lucky, the mocks you need to use have already been implemented by some nice people. If, however, you find yourself in the position to write a mock class, relax - Google Mock turns this task into a fun game! (Well, almost.) ## How to Define It ## Using the `Turtle` interface as example, here are the simple steps you need to follow: 1. Derive a class `MockTurtle` from `Turtle`. 1. Take a _virtual_ function of `Turtle` (while it's possible to [mock non-virtual methods using templates](CookBook.md#mocking-nonvirtual-methods), it's much more involved). Count how many arguments it has. 1. In the `public:` section of the child class, write `MOCK_METHODn();` (or `MOCK_CONST_METHODn();` if you are mocking a `const` method), where `n` is the number of the arguments; if you counted wrong, shame on you, and a compiler error will tell you so. 1. Now comes the fun part: you take the function signature, cut-and-paste the _function name_ as the _first_ argument to the macro, and leave what's left as the _second_ argument (in case you're curious, this is the _type of the function_). 1. Repeat until all virtual functions you want to mock are done. After the process, you should have something like: ``` #include "gmock/gmock.h" // Brings in Google Mock. class MockTurtle : public Turtle { public: ... MOCK_METHOD0(PenUp, void()); MOCK_METHOD0(PenDown, void()); MOCK_METHOD1(Forward, void(int distance)); MOCK_METHOD1(Turn, void(int degrees)); MOCK_METHOD2(GoTo, void(int x, int y)); MOCK_CONST_METHOD0(GetX, int()); MOCK_CONST_METHOD0(GetY, int()); }; ``` You don't need to define these mock methods somewhere else - the `MOCK_METHOD*` macros will generate the definitions for you. It's that simple! Once you get the hang of it, you can pump out mock classes faster than your source-control system can handle your check-ins. **Tip:** If even this is too much work for you, you'll find the `gmock_gen.py` tool in Google Mock's `scripts/generator/` directory (courtesy of the [cppclean](http://code.google.com/p/cppclean/) project) useful. This command-line tool requires that you have Python 2.4 installed. You give it a C++ file and the name of an abstract class defined in it, and it will print the definition of the mock class for you. Due to the complexity of the C++ language, this script may not always work, but it can be quite handy when it does. For more details, read the [user documentation](../scripts/generator/README). ## Where to Put It ## When you define a mock class, you need to decide where to put its definition. Some people put it in a `*_test.cc`. This is fine when the interface being mocked (say, `Foo`) is owned by the same person or team. Otherwise, when the owner of `Foo` changes it, your test could break. (You can't really expect `Foo`'s maintainer to fix every test that uses `Foo`, can you?) So, the rule of thumb is: if you need to mock `Foo` and it's owned by others, define the mock class in `Foo`'s package (better, in a `testing` sub-package such that you can clearly separate production code and testing utilities), and put it in a `mock_foo.h`. Then everyone can reference `mock_foo.h` from their tests. If `Foo` ever changes, there is only one copy of `MockFoo` to change, and only tests that depend on the changed methods need to be fixed. Another way to do it: you can introduce a thin layer `FooAdaptor` on top of `Foo` and code to this new interface. Since you own `FooAdaptor`, you can absorb changes in `Foo` much more easily. While this is more work initially, carefully choosing the adaptor interface can make your code easier to write and more readable (a net win in the long run), as you can choose `FooAdaptor` to fit your specific domain much better than `Foo` does. # Using Mocks in Tests # Once you have a mock class, using it is easy. The typical work flow is: 1. Import the Google Mock names from the `testing` namespace such that you can use them unqualified (You only have to do it once per file. Remember that namespaces are a good idea and good for your health.). 1. Create some mock objects. 1. Specify your expectations on them (How many times will a method be called? With what arguments? What should it do? etc.). 1. Exercise some code that uses the mocks; optionally, check the result using Google Test assertions. If a mock method is called more than expected or with wrong arguments, you'll get an error immediately. 1. When a mock is destructed, Google Mock will automatically check whether all expectations on it have been satisfied. Here's an example: ``` #include "path/to/mock-turtle.h" #include "gmock/gmock.h" #include "gtest/gtest.h" using ::testing::AtLeast; // #1 TEST(PainterTest, CanDrawSomething) { MockTurtle turtle; // #2 EXPECT_CALL(turtle, PenDown()) // #3 .Times(AtLeast(1)); Painter painter(&turtle); // #4 EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); } // #5 int main(int argc, char** argv) { // The following line must be executed to initialize Google Mock // (and Google Test) before running the tests. ::testing::InitGoogleMock(&argc, argv); return RUN_ALL_TESTS(); } ``` As you might have guessed, this test checks that `PenDown()` is called at least once. If the `painter` object didn't call this method, your test will fail with a message like this: ``` path/to/my_test.cc:119: Failure Actual function call count doesn't match this expectation: Actually: never called; Expected: called at least once. ``` **Tip 1:** If you run the test from an Emacs buffer, you can hit `` on the line number displayed in the error message to jump right to the failed expectation. **Tip 2:** If your mock objects are never deleted, the final verification won't happen. Therefore it's a good idea to use a heap leak checker in your tests when you allocate mocks on the heap. **Important note:** Google Mock requires expectations to be set **before** the mock functions are called, otherwise the behavior is **undefined**. In particular, you mustn't interleave `EXPECT_CALL()`s and calls to the mock functions. This means `EXPECT_CALL()` should be read as expecting that a call will occur _in the future_, not that a call has occurred. Why does Google Mock work like that? Well, specifying the expectation beforehand allows Google Mock to report a violation as soon as it arises, when the context (stack trace, etc) is still available. This makes debugging much easier. Admittedly, this test is contrived and doesn't do much. You can easily achieve the same effect without using Google Mock. However, as we shall reveal soon, Google Mock allows you to do _much more_ with the mocks. ## Using Google Mock with Any Testing Framework ## If you want to use something other than Google Test (e.g. [CppUnit](http://sourceforge.net/projects/cppunit/) or [CxxTest](https://cxxtest.com/)) as your testing framework, just change the `main()` function in the previous section to: ``` int main(int argc, char** argv) { // The following line causes Google Mock to throw an exception on failure, // which will be interpreted by your testing framework as a test failure. ::testing::GTEST_FLAG(throw_on_failure) = true; ::testing::InitGoogleMock(&argc, argv); ... whatever your testing framework requires ... } ``` This approach has a catch: it makes Google Mock throw an exception from a mock object's destructor sometimes. With some compilers, this sometimes causes the test program to crash. You'll still be able to notice that the test has failed, but it's not a graceful failure. A better solution is to use Google Test's [event listener API](../../googletest/docs/advanced.md#extending-google-test-by-handling-test-events) to report a test failure to your testing framework properly. You'll need to implement the `OnTestPartResult()` method of the event listener interface, but it should be straightforward. If this turns out to be too much work, we suggest that you stick with Google Test, which works with Google Mock seamlessly (in fact, it is technically part of Google Mock.). If there is a reason that you cannot use Google Test, please let us know. # Setting Expectations # The key to using a mock object successfully is to set the _right expectations_ on it. If you set the expectations too strict, your test will fail as the result of unrelated changes. If you set them too loose, bugs can slip through. You want to do it just right such that your test can catch exactly the kind of bugs you intend it to catch. Google Mock provides the necessary means for you to do it "just right." ## General Syntax ## In Google Mock we use the `EXPECT_CALL()` macro to set an expectation on a mock method. The general syntax is: ``` EXPECT_CALL(mock_object, method(matchers)) .Times(cardinality) .WillOnce(action) .WillRepeatedly(action); ``` The macro has two arguments: first the mock object, and then the method and its arguments. Note that the two are separated by a comma (`,`), not a period (`.`). (Why using a comma? The answer is that it was necessary for technical reasons.) The macro can be followed by some optional _clauses_ that provide more information about the expectation. We'll discuss how each clause works in the coming sections. This syntax is designed to make an expectation read like English. For example, you can probably guess that ``` using ::testing::Return; ... EXPECT_CALL(turtle, GetX()) .Times(5) .WillOnce(Return(100)) .WillOnce(Return(150)) .WillRepeatedly(Return(200)); ``` says that the `turtle` object's `GetX()` method will be called five times, it will return 100 the first time, 150 the second time, and then 200 every time. Some people like to call this style of syntax a Domain-Specific Language (DSL). **Note:** Why do we use a macro to do this? It serves two purposes: first it makes expectations easily identifiable (either by `grep` or by a human reader), and second it allows Google Mock to include the source file location of a failed expectation in messages, making debugging easier. ## Matchers: What Arguments Do We Expect? ## When a mock function takes arguments, we must specify what arguments we are expecting; for example: ``` // Expects the turtle to move forward by 100 units. EXPECT_CALL(turtle, Forward(100)); ``` Sometimes you may not want to be too specific (Remember that talk about tests being too rigid? Over specification leads to brittle tests and obscures the intent of tests. Therefore we encourage you to specify only what's necessary - no more, no less.). If you care to check that `Forward()` will be called but aren't interested in its actual argument, write `_` as the argument, which means "anything goes": ``` using ::testing::_; ... // Expects the turtle to move forward. EXPECT_CALL(turtle, Forward(_)); ``` `_` is an instance of what we call **matchers**. A matcher is like a predicate and can test whether an argument is what we'd expect. You can use a matcher inside `EXPECT_CALL()` wherever a function argument is expected. A list of built-in matchers can be found in the [CheatSheet](CheatSheet.md). For example, here's the `Ge` (greater than or equal) matcher: ``` using ::testing::Ge; ... EXPECT_CALL(turtle, Forward(Ge(100))); ``` This checks that the turtle will be told to go forward by at least 100 units. ## Cardinalities: How Many Times Will It Be Called? ## The first clause we can specify following an `EXPECT_CALL()` is `Times()`. We call its argument a **cardinality** as it tells _how many times_ the call should occur. It allows us to repeat an expectation many times without actually writing it as many times. More importantly, a cardinality can be "fuzzy", just like a matcher can be. This allows a user to express the intent of a test exactly. An interesting special case is when we say `Times(0)`. You may have guessed - it means that the function shouldn't be called with the given arguments at all, and Google Mock will report a Google Test failure whenever the function is (wrongfully) called. We've seen `AtLeast(n)` as an example of fuzzy cardinalities earlier. For the list of built-in cardinalities you can use, see the [CheatSheet](CheatSheet.md). The `Times()` clause can be omitted. **If you omit `Times()`, Google Mock will infer the cardinality for you.** The rules are easy to remember: * If **neither** `WillOnce()` **nor** `WillRepeatedly()` is in the `EXPECT_CALL()`, the inferred cardinality is `Times(1)`. * If there are `n WillOnce()`'s but **no** `WillRepeatedly()`, where `n` >= 1, the cardinality is `Times(n)`. * If there are `n WillOnce()`'s and **one** `WillRepeatedly()`, where `n` >= 0, the cardinality is `Times(AtLeast(n))`. **Quick quiz:** what do you think will happen if a function is expected to be called twice but actually called four times? ## Actions: What Should It Do? ## Remember that a mock object doesn't really have a working implementation? We as users have to tell it what to do when a method is invoked. This is easy in Google Mock. First, if the return type of a mock function is a built-in type or a pointer, the function has a **default action** (a `void` function will just return, a `bool` function will return `false`, and other functions will return 0). In addition, in C++ 11 and above, a mock function whose return type is default-constructible (i.e. has a default constructor) has a default action of returning a default-constructed value. If you don't say anything, this behavior will be used. Second, if a mock function doesn't have a default action, or the default action doesn't suit you, you can specify the action to be taken each time the expectation matches using a series of `WillOnce()` clauses followed by an optional `WillRepeatedly()`. For example, ``` using ::testing::Return; ... EXPECT_CALL(turtle, GetX()) .WillOnce(Return(100)) .WillOnce(Return(200)) .WillOnce(Return(300)); ``` This says that `turtle.GetX()` will be called _exactly three times_ (Google Mock inferred this from how many `WillOnce()` clauses we've written, since we didn't explicitly write `Times()`), and will return 100, 200, and 300 respectively. ``` using ::testing::Return; ... EXPECT_CALL(turtle, GetY()) .WillOnce(Return(100)) .WillOnce(Return(200)) .WillRepeatedly(Return(300)); ``` says that `turtle.GetY()` will be called _at least twice_ (Google Mock knows this as we've written two `WillOnce()` clauses and a `WillRepeatedly()` while having no explicit `Times()`), will return 100 the first time, 200 the second time, and 300 from the third time on. Of course, if you explicitly write a `Times()`, Google Mock will not try to infer the cardinality itself. What if the number you specified is larger than there are `WillOnce()` clauses? Well, after all `WillOnce()`s are used up, Google Mock will do the _default_ action for the function every time (unless, of course, you have a `WillRepeatedly()`.). What can we do inside `WillOnce()` besides `Return()`? You can return a reference using `ReturnRef(variable)`, or invoke a pre-defined function, among [others](CheatSheet.md#actions). **Important note:** The `EXPECT_CALL()` statement evaluates the action clause only once, even though the action may be performed many times. Therefore you must be careful about side effects. The following may not do what you want: ``` int n = 100; EXPECT_CALL(turtle, GetX()) .Times(4) .WillRepeatedly(Return(n++)); ``` Instead of returning 100, 101, 102, ..., consecutively, this mock function will always return 100 as `n++` is only evaluated once. Similarly, `Return(new Foo)` will create a new `Foo` object when the `EXPECT_CALL()` is executed, and will return the same pointer every time. If you want the side effect to happen every time, you need to define a custom action, which we'll teach in the [CookBook](CookBook.md). Time for another quiz! What do you think the following means? ``` using ::testing::Return; ... EXPECT_CALL(turtle, GetY()) .Times(4) .WillOnce(Return(100)); ``` Obviously `turtle.GetY()` is expected to be called four times. But if you think it will return 100 every time, think twice! Remember that one `WillOnce()` clause will be consumed each time the function is invoked and the default action will be taken afterwards. So the right answer is that `turtle.GetY()` will return 100 the first time, but **return 0 from the second time on**, as returning 0 is the default action for `int` functions. ## Using Multiple Expectations ## So far we've only shown examples where you have a single expectation. More realistically, you're going to specify expectations on multiple mock methods, which may be from multiple mock objects. By default, when a mock method is invoked, Google Mock will search the expectations in the **reverse order** they are defined, and stop when an active expectation that matches the arguments is found (you can think of it as "newer rules override older ones."). If the matching expectation cannot take any more calls, you will get an upper-bound-violated failure. Here's an example: ``` using ::testing::_; ... EXPECT_CALL(turtle, Forward(_)); // #1 EXPECT_CALL(turtle, Forward(10)) // #2 .Times(2); ``` If `Forward(10)` is called three times in a row, the third time it will be an error, as the last matching expectation (#2) has been saturated. If, however, the third `Forward(10)` call is replaced by `Forward(20)`, then it would be OK, as now #1 will be the matching expectation. **Side note:** Why does Google Mock search for a match in the _reverse_ order of the expectations? The reason is that this allows a user to set up the default expectations in a mock object's constructor or the test fixture's set-up phase and then customize the mock by writing more specific expectations in the test body. So, if you have two expectations on the same method, you want to put the one with more specific matchers **after** the other, or the more specific rule would be shadowed by the more general one that comes after it. ## Ordered vs Unordered Calls ## By default, an expectation can match a call even though an earlier expectation hasn't been satisfied. In other words, the calls don't have to occur in the order the expectations are specified. Sometimes, you may want all the expected calls to occur in a strict order. To say this in Google Mock is easy: ``` using ::testing::InSequence; ... TEST(FooTest, DrawsLineSegment) { ... { InSequence dummy; EXPECT_CALL(turtle, PenDown()); EXPECT_CALL(turtle, Forward(100)); EXPECT_CALL(turtle, PenUp()); } Foo(); } ``` By creating an object of type `InSequence`, all expectations in its scope are put into a _sequence_ and have to occur _sequentially_. Since we are just relying on the constructor and destructor of this object to do the actual work, its name is really irrelevant. In this example, we test that `Foo()` calls the three expected functions in the order as written. If a call is made out-of-order, it will be an error. (What if you care about the relative order of some of the calls, but not all of them? Can you specify an arbitrary partial order? The answer is ... yes! If you are impatient, the details can be found in the [CookBook](CookBook.md#expecting-partially-ordered-calls).) ## All Expectations Are Sticky (Unless Said Otherwise) ## Now let's do a quick quiz to see how well you can use this mock stuff already. How would you test that the turtle is asked to go to the origin _exactly twice_ (you want to ignore any other instructions it receives)? After you've come up with your answer, take a look at ours and compare notes (solve it yourself first - don't cheat!): ``` using ::testing::_; ... EXPECT_CALL(turtle, GoTo(_, _)) // #1 .Times(AnyNumber()); EXPECT_CALL(turtle, GoTo(0, 0)) // #2 .Times(2); ``` Suppose `turtle.GoTo(0, 0)` is called three times. In the third time, Google Mock will see that the arguments match expectation #2 (remember that we always pick the last matching expectation). Now, since we said that there should be only two such calls, Google Mock will report an error immediately. This is basically what we've told you in the "Using Multiple Expectations" section above. This example shows that **expectations in Google Mock are "sticky" by default**, in the sense that they remain active even after we have reached their invocation upper bounds. This is an important rule to remember, as it affects the meaning of the spec, and is **different** to how it's done in many other mocking frameworks (Why'd we do that? Because we think our rule makes the common cases easier to express and understand.). Simple? Let's see if you've really understood it: what does the following code say? ``` using ::testing::Return; ... for (int i = n; i > 0; i--) { EXPECT_CALL(turtle, GetX()) .WillOnce(Return(10*i)); } ``` If you think it says that `turtle.GetX()` will be called `n` times and will return 10, 20, 30, ..., consecutively, think twice! The problem is that, as we said, expectations are sticky. So, the second time `turtle.GetX()` is called, the last (latest) `EXPECT_CALL()` statement will match, and will immediately lead to an "upper bound exceeded" error - this piece of code is not very useful! One correct way of saying that `turtle.GetX()` will return 10, 20, 30, ..., is to explicitly say that the expectations are _not_ sticky. In other words, they should _retire_ as soon as they are saturated: ``` using ::testing::Return; ... for (int i = n; i > 0; i--) { EXPECT_CALL(turtle, GetX()) .WillOnce(Return(10*i)) .RetiresOnSaturation(); } ``` And, there's a better way to do it: in this case, we expect the calls to occur in a specific order, and we line up the actions to match the order. Since the order is important here, we should make it explicit using a sequence: ``` using ::testing::InSequence; using ::testing::Return; ... { InSequence s; for (int i = 1; i <= n; i++) { EXPECT_CALL(turtle, GetX()) .WillOnce(Return(10*i)) .RetiresOnSaturation(); } } ``` By the way, the other situation where an expectation may _not_ be sticky is when it's in a sequence - as soon as another expectation that comes after it in the sequence has been used, it automatically retires (and will never be used to match any call). ## Uninteresting Calls ## A mock object may have many methods, and not all of them are that interesting. For example, in some tests we may not care about how many times `GetX()` and `GetY()` get called. In Google Mock, if you are not interested in a method, just don't say anything about it. If a call to this method occurs, you'll see a warning in the test output, but it won't be a failure. # What Now? # Congratulations! You've learned enough about Google Mock to start using it. Now, you might want to join the [googlemock](http://groups.google.com/group/googlemock) discussion group and actually write some tests using Google Mock - it will be fun. Hey, it may even be addictive - you've been warned. Then, if you feel like increasing your mock quotient, you should move on to the [CookBook](CookBook.md). You can learn many advanced features of Google Mock there -- and advance your level of enjoyment and testing bliss. ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/docs/FrequentlyAskedQuestions.md ================================================ Please send your questions to the [googlemock](http://groups.google.com/group/googlemock) discussion group. If you need help with compiler errors, make sure you have tried [Google Mock Doctor](#How_am_I_supposed_to_make_sense_of_these_horrible_template_error.md) first. ## When I call a method on my mock object, the method for the real object is invoked instead. What's the problem? ## In order for a method to be mocked, it must be _virtual_, unless you use the [high-perf dependency injection technique](CookBook.md#mocking-nonvirtual-methods). ## I wrote some matchers. After I upgraded to a new version of Google Mock, they no longer compile. What's going on? ## After version 1.4.0 of Google Mock was released, we had an idea on how to make it easier to write matchers that can generate informative messages efficiently. We experimented with this idea and liked what we saw. Therefore we decided to implement it. Unfortunately, this means that if you have defined your own matchers by implementing `MatcherInterface` or using `MakePolymorphicMatcher()`, your definitions will no longer compile. Matchers defined using the `MATCHER*` family of macros are not affected. Sorry for the hassle if your matchers are affected. We believe it's in everyone's long-term interest to make this change sooner than later. Fortunately, it's usually not hard to migrate an existing matcher to the new API. Here's what you need to do: If you wrote your matcher like this: ``` // Old matcher definition that doesn't work with the latest // Google Mock. using ::testing::MatcherInterface; ... class MyWonderfulMatcher : public MatcherInterface { public: ... virtual bool Matches(MyType value) const { // Returns true if value matches. return value.GetFoo() > 5; } ... }; ``` you'll need to change it to: ``` // New matcher definition that works with the latest Google Mock. using ::testing::MatcherInterface; using ::testing::MatchResultListener; ... class MyWonderfulMatcher : public MatcherInterface { public: ... virtual bool MatchAndExplain(MyType value, MatchResultListener* listener) const { // Returns true if value matches. return value.GetFoo() > 5; } ... }; ``` (i.e. rename `Matches()` to `MatchAndExplain()` and give it a second argument of type `MatchResultListener*`.) If you were also using `ExplainMatchResultTo()` to improve the matcher message: ``` // Old matcher definition that doesn't work with the lastest // Google Mock. using ::testing::MatcherInterface; ... class MyWonderfulMatcher : public MatcherInterface { public: ... virtual bool Matches(MyType value) const { // Returns true if value matches. return value.GetFoo() > 5; } virtual void ExplainMatchResultTo(MyType value, ::std::ostream* os) const { // Prints some helpful information to os to help // a user understand why value matches (or doesn't match). *os << "the Foo property is " << value.GetFoo(); } ... }; ``` you should move the logic of `ExplainMatchResultTo()` into `MatchAndExplain()`, using the `MatchResultListener` argument where the `::std::ostream` was used: ``` // New matcher definition that works with the latest Google Mock. using ::testing::MatcherInterface; using ::testing::MatchResultListener; ... class MyWonderfulMatcher : public MatcherInterface { public: ... virtual bool MatchAndExplain(MyType value, MatchResultListener* listener) const { // Returns true if value matches. *listener << "the Foo property is " << value.GetFoo(); return value.GetFoo() > 5; } ... }; ``` If your matcher is defined using `MakePolymorphicMatcher()`: ``` // Old matcher definition that doesn't work with the latest // Google Mock. using ::testing::MakePolymorphicMatcher; ... class MyGreatMatcher { public: ... bool Matches(MyType value) const { // Returns true if value matches. return value.GetBar() < 42; } ... }; ... MakePolymorphicMatcher(MyGreatMatcher()) ... ``` you should rename the `Matches()` method to `MatchAndExplain()` and add a `MatchResultListener*` argument (the same as what you need to do for matchers defined by implementing `MatcherInterface`): ``` // New matcher definition that works with the latest Google Mock. using ::testing::MakePolymorphicMatcher; using ::testing::MatchResultListener; ... class MyGreatMatcher { public: ... bool MatchAndExplain(MyType value, MatchResultListener* listener) const { // Returns true if value matches. return value.GetBar() < 42; } ... }; ... MakePolymorphicMatcher(MyGreatMatcher()) ... ``` If your polymorphic matcher uses `ExplainMatchResultTo()` for better failure messages: ``` // Old matcher definition that doesn't work with the latest // Google Mock. using ::testing::MakePolymorphicMatcher; ... class MyGreatMatcher { public: ... bool Matches(MyType value) const { // Returns true if value matches. return value.GetBar() < 42; } ... }; void ExplainMatchResultTo(const MyGreatMatcher& matcher, MyType value, ::std::ostream* os) { // Prints some helpful information to os to help // a user understand why value matches (or doesn't match). *os << "the Bar property is " << value.GetBar(); } ... MakePolymorphicMatcher(MyGreatMatcher()) ... ``` you'll need to move the logic inside `ExplainMatchResultTo()` to `MatchAndExplain()`: ``` // New matcher definition that works with the latest Google Mock. using ::testing::MakePolymorphicMatcher; using ::testing::MatchResultListener; ... class MyGreatMatcher { public: ... bool MatchAndExplain(MyType value, MatchResultListener* listener) const { // Returns true if value matches. *listener << "the Bar property is " << value.GetBar(); return value.GetBar() < 42; } ... }; ... MakePolymorphicMatcher(MyGreatMatcher()) ... ``` For more information, you can read these [two](CookBook.md#writing-new-monomorphic-matchers) [recipes](CookBook.md#writing-new-polymorphic-matchers) from the cookbook. As always, you are welcome to post questions on `googlemock@googlegroups.com` if you need any help. ## When using Google Mock, do I have to use Google Test as the testing framework? I have my favorite testing framework and don't want to switch. ## Google Mock works out of the box with Google Test. However, it's easy to configure it to work with any testing framework of your choice. [Here](ForDummies.md#using-google-mock-with-any-testing-framework) is how. ## How am I supposed to make sense of these horrible template errors? ## If you are confused by the compiler errors gcc threw at you, try consulting the _Google Mock Doctor_ tool first. What it does is to scan stdin for gcc error messages, and spit out diagnoses on the problems (we call them diseases) your code has. To "install", run command: ``` alias gmd='/scripts/gmock_doctor.py' ``` To use it, do: ``` 2>&1 | gmd ``` For example: ``` make my_test 2>&1 | gmd ``` Or you can run `gmd` and copy-n-paste gcc's error messages to it. ## Can I mock a variadic function? ## You cannot mock a variadic function (i.e. a function taking ellipsis (`...`) arguments) directly in Google Mock. The problem is that in general, there is _no way_ for a mock object to know how many arguments are passed to the variadic method, and what the arguments' types are. Only the _author of the base class_ knows the protocol, and we cannot look into their head. Therefore, to mock such a function, the _user_ must teach the mock object how to figure out the number of arguments and their types. One way to do it is to provide overloaded versions of the function. Ellipsis arguments are inherited from C and not really a C++ feature. They are unsafe to use and don't work with arguments that have constructors or destructors. Therefore we recommend to avoid them in C++ as much as possible. ## MSVC gives me warning C4301 or C4373 when I define a mock method with a const parameter. Why? ## If you compile this using Microsoft Visual C++ 2005 SP1: ``` class Foo { ... virtual void Bar(const int i) = 0; }; class MockFoo : public Foo { ... MOCK_METHOD1(Bar, void(const int i)); }; ``` You may get the following warning: ``` warning C4301: 'MockFoo::Bar': overriding virtual function only differs from 'Foo::Bar' by const/volatile qualifier ``` This is a MSVC bug. The same code compiles fine with gcc ,for example. If you use Visual C++ 2008 SP1, you would get the warning: ``` warning C4373: 'MockFoo::Bar': virtual function overrides 'Foo::Bar', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers ``` In C++, if you _declare_ a function with a `const` parameter, the `const` modifier is _ignored_. Therefore, the `Foo` base class above is equivalent to: ``` class Foo { ... virtual void Bar(int i) = 0; // int or const int? Makes no difference. }; ``` In fact, you can _declare_ Bar() with an `int` parameter, and _define_ it with a `const int` parameter. The compiler will still match them up. Since making a parameter `const` is meaningless in the method _declaration_, we recommend to remove it in both `Foo` and `MockFoo`. That should workaround the VC bug. Note that we are talking about the _top-level_ `const` modifier here. If the function parameter is passed by pointer or reference, declaring the _pointee_ or _referee_ as `const` is still meaningful. For example, the following two declarations are _not_ equivalent: ``` void Bar(int* p); // Neither p nor *p is const. void Bar(const int* p); // p is not const, but *p is. ``` ## I have a huge mock class, and Microsoft Visual C++ runs out of memory when compiling it. What can I do? ## We've noticed that when the `/clr` compiler flag is used, Visual C++ uses 5~6 times as much memory when compiling a mock class. We suggest to avoid `/clr` when compiling native C++ mocks. ## I can't figure out why Google Mock thinks my expectations are not satisfied. What should I do? ## You might want to run your test with `--gmock_verbose=info`. This flag lets Google Mock print a trace of every mock function call it receives. By studying the trace, you'll gain insights on why the expectations you set are not met. ## How can I assert that a function is NEVER called? ## ``` EXPECT_CALL(foo, Bar(_)) .Times(0); ``` ## I have a failed test where Google Mock tells me TWICE that a particular expectation is not satisfied. Isn't this redundant? ## When Google Mock detects a failure, it prints relevant information (the mock function arguments, the state of relevant expectations, and etc) to help the user debug. If another failure is detected, Google Mock will do the same, including printing the state of relevant expectations. Sometimes an expectation's state didn't change between two failures, and you'll see the same description of the state twice. They are however _not_ redundant, as they refer to _different points in time_. The fact they are the same _is_ interesting information. ## I get a heap check failure when using a mock object, but using a real object is fine. What can be wrong? ## Does the class (hopefully a pure interface) you are mocking have a virtual destructor? Whenever you derive from a base class, make sure its destructor is virtual. Otherwise Bad Things will happen. Consider the following code: ``` class Base { public: // Not virtual, but should be. ~Base() { ... } ... }; class Derived : public Base { public: ... private: std::string value_; }; ... Base* p = new Derived; ... delete p; // Surprise! ~Base() will be called, but ~Derived() will not // - value_ is leaked. ``` By changing `~Base()` to virtual, `~Derived()` will be correctly called when `delete p` is executed, and the heap checker will be happy. ## The "newer expectations override older ones" rule makes writing expectations awkward. Why does Google Mock do that? ## When people complain about this, often they are referring to code like: ``` // foo.Bar() should be called twice, return 1 the first time, and return // 2 the second time. However, I have to write the expectations in the // reverse order. This sucks big time!!! EXPECT_CALL(foo, Bar()) .WillOnce(Return(2)) .RetiresOnSaturation(); EXPECT_CALL(foo, Bar()) .WillOnce(Return(1)) .RetiresOnSaturation(); ``` The problem is that they didn't pick the **best** way to express the test's intent. By default, expectations don't have to be matched in _any_ particular order. If you want them to match in a certain order, you need to be explicit. This is Google Mock's (and jMock's) fundamental philosophy: it's easy to accidentally over-specify your tests, and we want to make it harder to do so. There are two better ways to write the test spec. You could either put the expectations in sequence: ``` // foo.Bar() should be called twice, return 1 the first time, and return // 2 the second time. Using a sequence, we can write the expectations // in their natural order. { InSequence s; EXPECT_CALL(foo, Bar()) .WillOnce(Return(1)) .RetiresOnSaturation(); EXPECT_CALL(foo, Bar()) .WillOnce(Return(2)) .RetiresOnSaturation(); } ``` or you can put the sequence of actions in the same expectation: ``` // foo.Bar() should be called twice, return 1 the first time, and return // 2 the second time. EXPECT_CALL(foo, Bar()) .WillOnce(Return(1)) .WillOnce(Return(2)) .RetiresOnSaturation(); ``` Back to the original questions: why does Google Mock search the expectations (and `ON_CALL`s) from back to front? Because this allows a user to set up a mock's behavior for the common case early (e.g. in the mock's constructor or the test fixture's set-up phase) and customize it with more specific rules later. If Google Mock searches from front to back, this very useful pattern won't be possible. ## Google Mock prints a warning when a function without EXPECT\_CALL is called, even if I have set its behavior using ON\_CALL. Would it be reasonable not to show the warning in this case? ## When choosing between being neat and being safe, we lean toward the latter. So the answer is that we think it's better to show the warning. Often people write `ON_CALL`s in the mock object's constructor or `SetUp()`, as the default behavior rarely changes from test to test. Then in the test body they set the expectations, which are often different for each test. Having an `ON_CALL` in the set-up part of a test doesn't mean that the calls are expected. If there's no `EXPECT_CALL` and the method is called, it's possibly an error. If we quietly let the call go through without notifying the user, bugs may creep in unnoticed. If, however, you are sure that the calls are OK, you can write ``` EXPECT_CALL(foo, Bar(_)) .WillRepeatedly(...); ``` instead of ``` ON_CALL(foo, Bar(_)) .WillByDefault(...); ``` This tells Google Mock that you do expect the calls and no warning should be printed. Also, you can control the verbosity using the `--gmock_verbose` flag. If you find the output too noisy when debugging, just choose a less verbose level. ## How can I delete the mock function's argument in an action? ## If you find yourself needing to perform some action that's not supported by Google Mock directly, remember that you can define your own actions using [MakeAction()](CookBook.md#writing-new-actions) or [MakePolymorphicAction()](CookBook.md#writing_new_polymorphic_actions), or you can write a stub function and invoke it using [Invoke()](CookBook.md#using-functions_methods_functors). ## MOCK\_METHODn()'s second argument looks funny. Why don't you use the MOCK\_METHODn(Method, return\_type, arg\_1, ..., arg\_n) syntax? ## What?! I think it's beautiful. :-) While which syntax looks more natural is a subjective matter to some extent, Google Mock's syntax was chosen for several practical advantages it has. Try to mock a function that takes a map as an argument: ``` virtual int GetSize(const map& m); ``` Using the proposed syntax, it would be: ``` MOCK_METHOD1(GetSize, int, const map& m); ``` Guess what? You'll get a compiler error as the compiler thinks that `const map& m` are **two**, not one, arguments. To work around this you can use `typedef` to give the map type a name, but that gets in the way of your work. Google Mock's syntax avoids this problem as the function's argument types are protected inside a pair of parentheses: ``` // This compiles fine. MOCK_METHOD1(GetSize, int(const map& m)); ``` You still need a `typedef` if the return type contains an unprotected comma, but that's much rarer. Other advantages include: 1. `MOCK_METHOD1(Foo, int, bool)` can leave a reader wonder whether the method returns `int` or `bool`, while there won't be such confusion using Google Mock's syntax. 1. The way Google Mock describes a function type is nothing new, although many people may not be familiar with it. The same syntax was used in C, and the `function` library in `tr1` uses this syntax extensively. Since `tr1` will become a part of the new version of STL, we feel very comfortable to be consistent with it. 1. The function type syntax is also used in other parts of Google Mock's API (e.g. the action interface) in order to make the implementation tractable. A user needs to learn it anyway in order to utilize Google Mock's more advanced features. We'd as well stick to the same syntax in `MOCK_METHOD*`! ## My code calls a static/global function. Can I mock it? ## You can, but you need to make some changes. In general, if you find yourself needing to mock a static function, it's a sign that your modules are too tightly coupled (and less flexible, less reusable, less testable, etc). You are probably better off defining a small interface and call the function through that interface, which then can be easily mocked. It's a bit of work initially, but usually pays for itself quickly. This Google Testing Blog [post](https://testing.googleblog.com/2008/06/defeat-static-cling.html) says it excellently. Check it out. ## My mock object needs to do complex stuff. It's a lot of pain to specify the actions. Google Mock sucks! ## I know it's not a question, but you get an answer for free any way. :-) With Google Mock, you can create mocks in C++ easily. And people might be tempted to use them everywhere. Sometimes they work great, and sometimes you may find them, well, a pain to use. So, what's wrong in the latter case? When you write a test without using mocks, you exercise the code and assert that it returns the correct value or that the system is in an expected state. This is sometimes called "state-based testing". Mocks are great for what some call "interaction-based" testing: instead of checking the system state at the very end, mock objects verify that they are invoked the right way and report an error as soon as it arises, giving you a handle on the precise context in which the error was triggered. This is often more effective and economical to do than state-based testing. If you are doing state-based testing and using a test double just to simulate the real object, you are probably better off using a fake. Using a mock in this case causes pain, as it's not a strong point for mocks to perform complex actions. If you experience this and think that mocks suck, you are just not using the right tool for your problem. Or, you might be trying to solve the wrong problem. :-) ## I got a warning "Uninteresting function call encountered - default action taken.." Should I panic? ## By all means, NO! It's just an FYI. What it means is that you have a mock function, you haven't set any expectations on it (by Google Mock's rule this means that you are not interested in calls to this function and therefore it can be called any number of times), and it is called. That's OK - you didn't say it's not OK to call the function! What if you actually meant to disallow this function to be called, but forgot to write `EXPECT_CALL(foo, Bar()).Times(0)`? While one can argue that it's the user's fault, Google Mock tries to be nice and prints you a note. So, when you see the message and believe that there shouldn't be any uninteresting calls, you should investigate what's going on. To make your life easier, Google Mock prints the function name and arguments when an uninteresting call is encountered. ## I want to define a custom action. Should I use Invoke() or implement the action interface? ## Either way is fine - you want to choose the one that's more convenient for your circumstance. Usually, if your action is for a particular function type, defining it using `Invoke()` should be easier; if your action can be used in functions of different types (e.g. if you are defining `Return(value)`), `MakePolymorphicAction()` is easiest. Sometimes you want precise control on what types of functions the action can be used in, and implementing `ActionInterface` is the way to go here. See the implementation of `Return()` in `include/gmock/gmock-actions.h` for an example. ## I'm using the set-argument-pointee action, and the compiler complains about "conflicting return type specified". What does it mean? ## You got this error as Google Mock has no idea what value it should return when the mock method is called. `SetArgPointee()` says what the side effect is, but doesn't say what the return value should be. You need `DoAll()` to chain a `SetArgPointee()` with a `Return()`. See this [recipe](CookBook.md#mocking_side_effects) for more details and an example. ## My question is not in your FAQ! ## If you cannot find the answer to your question in this FAQ, there are some other resources you can use: 1. search the mailing list [archive](http://groups.google.com/group/googlemock/topics), 1. ask it on [googlemock@googlegroups.com](mailto:googlemock@googlegroups.com) and someone will answer it (to prevent spam, we require you to join the [discussion group](http://groups.google.com/group/googlemock) before you can post.). Please note that creating an issue in the [issue tracker](https://github.com/google/googletest/issues) is _not_ a good way to get your answer, as it is monitored infrequently by a very small number of people. When asking a question, it's helpful to provide as much of the following information as possible (people cannot help you if there's not enough information in your question): * the version (or the revision number if you check out from SVN directly) of Google Mock you use (Google Mock is under active development, so it's possible that your problem has been solved in a later version), * your operating system, * the name and version of your compiler, * the complete command line flags you give to your compiler, * the complete compiler error messages (if the question is about compilation), * the _actual_ code (ideally, a minimal but complete program) that has the problem you encounter. ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/docs/KnownIssues.md ================================================ As any non-trivial software system, Google Mock has some known limitations and problems. We are working on improving it, and welcome your help! The follow is a list of issues we know about. ## README contains outdated information on Google Mock's compatibility with other testing frameworks ## The `README` file in release 1.1.0 still says that Google Mock only works with Google Test. Actually, you can configure Google Mock to work with any testing framework you choose. ## Tests failing on machines using Power PC CPUs (e.g. some Macs) ## `gmock_output_test` and `gmock-printers_test` are known to fail with Power PC CPUs. This is due to portability issues with these tests, and is not an indication of problems in Google Mock itself. You can safely ignore them. ## Failed to resolve libgtest.so.0 in tests when built against installed Google Test ## This only applies if you manually built and installed Google Test, and then built a Google Mock against it (either explicitly, or because gtest-config was in your path post-install). In this situation, Libtool has a known issue with certain systems' ldconfig setup: http://article.gmane.org/gmane.comp.sysutils.automake.general/9025 This requires a manual run of "sudo ldconfig" after the "sudo make install" for Google Test before any binaries which link against it can be executed. This isn't a bug in our install, but we should at least have documented it or hacked a work-around into our install. We should have one of these solutions in our next release. ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-actions.h ================================================ // Copyright 2007, Google Inc. // 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 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. // Google Mock - a framework for writing C++ mock classes. // // This file implements some commonly used actions. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ #ifndef _WIN32_WCE # include #endif #include #include #include "gmock/internal/gmock-internal-utils.h" #include "gmock/internal/gmock-port.h" #if GTEST_LANG_CXX11 // Defined by gtest-port.h via gmock-port.h. #include #include #endif // GTEST_LANG_CXX11 namespace testing { // To implement an action Foo, define: // 1. a class FooAction that implements the ActionInterface interface, and // 2. a factory function that creates an Action object from a // const FooAction*. // // The two-level delegation design follows that of Matcher, providing // consistency for extension developers. It also eases ownership // management as Action objects can now be copied like plain values. namespace internal { template class ActionAdaptor; // BuiltInDefaultValueGetter::Get() returns a // default-constructed T value. BuiltInDefaultValueGetter::Get() crashes with an error. // // This primary template is used when kDefaultConstructible is true. template struct BuiltInDefaultValueGetter { static T Get() { return T(); } }; template struct BuiltInDefaultValueGetter { static T Get() { Assert(false, __FILE__, __LINE__, "Default action undefined for the function return type."); return internal::Invalid(); // The above statement will never be reached, but is required in // order for this function to compile. } }; // BuiltInDefaultValue::Get() returns the "built-in" default value // for type T, which is NULL when T is a raw pointer type, 0 when T is // a numeric type, false when T is bool, or "" when T is string or // std::string. In addition, in C++11 and above, it turns a // default-constructed T value if T is default constructible. For any // other type T, the built-in default T value is undefined, and the // function will abort the process. template class BuiltInDefaultValue { public: #if GTEST_LANG_CXX11 // This function returns true iff type T has a built-in default value. static bool Exists() { return ::std::is_default_constructible::value; } static T Get() { return BuiltInDefaultValueGetter< T, ::std::is_default_constructible::value>::Get(); } #else // GTEST_LANG_CXX11 // This function returns true iff type T has a built-in default value. static bool Exists() { return false; } static T Get() { return BuiltInDefaultValueGetter::Get(); } #endif // GTEST_LANG_CXX11 }; // This partial specialization says that we use the same built-in // default value for T and const T. template class BuiltInDefaultValue { public: static bool Exists() { return BuiltInDefaultValue::Exists(); } static T Get() { return BuiltInDefaultValue::Get(); } }; // This partial specialization defines the default values for pointer // types. template class BuiltInDefaultValue { public: static bool Exists() { return true; } static T* Get() { return NULL; } }; // The following specializations define the default values for // specific types we care about. #define GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(type, value) \ template <> \ class BuiltInDefaultValue { \ public: \ static bool Exists() { return true; } \ static type Get() { return value; } \ } GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, ); // NOLINT #if GTEST_HAS_GLOBAL_STRING GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::string, ""); #endif // GTEST_HAS_GLOBAL_STRING GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, ""); GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false); GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0'); GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed char, '\0'); GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(char, '\0'); // There's no need for a default action for signed wchar_t, as that // type is the same as wchar_t for gcc, and invalid for MSVC. // // There's also no need for a default action for unsigned wchar_t, as // that type is the same as unsigned int for gcc, and invalid for // MSVC. #if GMOCK_WCHAR_T_IS_NATIVE_ GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(wchar_t, 0U); // NOLINT #endif GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned short, 0U); // NOLINT GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed short, 0); // NOLINT GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned int, 0U); GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0); GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL); // NOLINT GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L); // NOLINT GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(UInt64, 0); GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(Int64, 0); GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0); GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0); #undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_ } // namespace internal // When an unexpected function call is encountered, Google Mock will // let it return a default value if the user has specified one for its // return type, or if the return type has a built-in default value; // otherwise Google Mock won't know what value to return and will have // to abort the process. // // The DefaultValue class allows a user to specify the // default value for a type T that is both copyable and publicly // destructible (i.e. anything that can be used as a function return // type). The usage is: // // // Sets the default value for type T to be foo. // DefaultValue::Set(foo); template class DefaultValue { public: // Sets the default value for type T; requires T to be // copy-constructable and have a public destructor. static void Set(T x) { delete producer_; producer_ = new FixedValueProducer(x); } // Provides a factory function to be called to generate the default value. // This method can be used even if T is only move-constructible, but it is not // limited to that case. typedef T (*FactoryFunction)(); static void SetFactory(FactoryFunction factory) { delete producer_; producer_ = new FactoryValueProducer(factory); } // Unsets the default value for type T. static void Clear() { delete producer_; producer_ = NULL; } // Returns true iff the user has set the default value for type T. static bool IsSet() { return producer_ != NULL; } // Returns true if T has a default return value set by the user or there // exists a built-in default value. static bool Exists() { return IsSet() || internal::BuiltInDefaultValue::Exists(); } // Returns the default value for type T if the user has set one; // otherwise returns the built-in default value. Requires that Exists() // is true, which ensures that the return value is well-defined. static T Get() { return producer_ == NULL ? internal::BuiltInDefaultValue::Get() : producer_->Produce(); } private: class ValueProducer { public: virtual ~ValueProducer() {} virtual T Produce() = 0; }; class FixedValueProducer : public ValueProducer { public: explicit FixedValueProducer(T value) : value_(value) {} virtual T Produce() { return value_; } private: const T value_; GTEST_DISALLOW_COPY_AND_ASSIGN_(FixedValueProducer); }; class FactoryValueProducer : public ValueProducer { public: explicit FactoryValueProducer(FactoryFunction factory) : factory_(factory) {} virtual T Produce() { return factory_(); } private: const FactoryFunction factory_; GTEST_DISALLOW_COPY_AND_ASSIGN_(FactoryValueProducer); }; static ValueProducer* producer_; }; // This partial specialization allows a user to set default values for // reference types. template class DefaultValue { public: // Sets the default value for type T&. static void Set(T& x) { // NOLINT address_ = &x; } // Unsets the default value for type T&. static void Clear() { address_ = NULL; } // Returns true iff the user has set the default value for type T&. static bool IsSet() { return address_ != NULL; } // Returns true if T has a default return value set by the user or there // exists a built-in default value. static bool Exists() { return IsSet() || internal::BuiltInDefaultValue::Exists(); } // Returns the default value for type T& if the user has set one; // otherwise returns the built-in default value if there is one; // otherwise aborts the process. static T& Get() { return address_ == NULL ? internal::BuiltInDefaultValue::Get() : *address_; } private: static T* address_; }; // This specialization allows DefaultValue::Get() to // compile. template <> class DefaultValue { public: static bool Exists() { return true; } static void Get() {} }; // Points to the user-set default value for type T. template typename DefaultValue::ValueProducer* DefaultValue::producer_ = NULL; // Points to the user-set default value for type T&. template T* DefaultValue::address_ = NULL; // Implement this interface to define an action for function type F. template class ActionInterface { public: typedef typename internal::Function::Result Result; typedef typename internal::Function::ArgumentTuple ArgumentTuple; ActionInterface() {} virtual ~ActionInterface() {} // Performs the action. This method is not const, as in general an // action can have side effects and be stateful. For example, a // get-the-next-element-from-the-collection action will need to // remember the current element. virtual Result Perform(const ArgumentTuple& args) = 0; private: GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionInterface); }; // An Action is a copyable and IMMUTABLE (except by assignment) // object that represents an action to be taken when a mock function // of type F is called. The implementation of Action is just a // linked_ptr to const ActionInterface, so copying is fairly cheap. // Don't inherit from Action! // // You can view an object implementing ActionInterface as a // concrete action (including its current state), and an Action // object as a handle to it. template class Action { public: typedef typename internal::Function::Result Result; typedef typename internal::Function::ArgumentTuple ArgumentTuple; // Constructs a null Action. Needed for storing Action objects in // STL containers. Action() {} #if GTEST_LANG_CXX11 // Construct an Action from a specified callable. // This cannot take std::function directly, because then Action would not be // directly constructible from lambda (it would require two conversions). template , G>::value>::type> Action(G&& fun) : fun_(::std::forward(fun)) {} // NOLINT #endif // Constructs an Action from its implementation. explicit Action(ActionInterface* impl) : impl_(impl) {} // This constructor allows us to turn an Action object into an // Action, as long as F's arguments can be implicitly converted // to Func's and Func's return type can be implicitly converted to // F's. template explicit Action(const Action& action); // Returns true iff this is the DoDefault() action. bool IsDoDefault() const { #if GTEST_LANG_CXX11 return impl_ == nullptr && fun_ == nullptr; #else return impl_ == NULL; #endif } // Performs the action. Note that this method is const even though // the corresponding method in ActionInterface is not. The reason // is that a const Action means that it cannot be re-bound to // another concrete action, not that the concrete action it binds to // cannot change state. (Think of the difference between a const // pointer and a pointer to const.) Result Perform(ArgumentTuple args) const { if (IsDoDefault()) { internal::IllegalDoDefault(__FILE__, __LINE__); } #if GTEST_LANG_CXX11 if (fun_ != nullptr) { return internal::Apply(fun_, ::std::move(args)); } #endif return impl_->Perform(args); } private: template friend class internal::ActionAdaptor; template friend class Action; // In C++11, Action can be implemented either as a generic functor (through // std::function), or legacy ActionInterface. In C++98, only ActionInterface // is available. The invariants are as follows: // * in C++98, impl_ is null iff this is the default action // * in C++11, at most one of fun_ & impl_ may be nonnull; both are null iff // this is the default action #if GTEST_LANG_CXX11 ::std::function fun_; #endif internal::linked_ptr > impl_; }; // The PolymorphicAction class template makes it easy to implement a // polymorphic action (i.e. an action that can be used in mock // functions of than one type, e.g. Return()). // // To define a polymorphic action, a user first provides a COPYABLE // implementation class that has a Perform() method template: // // class FooAction { // public: // template // Result Perform(const ArgumentTuple& args) const { // // Processes the arguments and returns a result, using // // tr1::get(args) to get the N-th (0-based) argument in the tuple. // } // ... // }; // // Then the user creates the polymorphic action using // MakePolymorphicAction(object) where object has type FooAction. See // the definition of Return(void) and SetArgumentPointee(value) for // complete examples. template class PolymorphicAction { public: explicit PolymorphicAction(const Impl& impl) : impl_(impl) {} template operator Action() const { return Action(new MonomorphicImpl(impl_)); } private: template class MonomorphicImpl : public ActionInterface { public: typedef typename internal::Function::Result Result; typedef typename internal::Function::ArgumentTuple ArgumentTuple; explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} virtual Result Perform(const ArgumentTuple& args) { return impl_.template Perform(args); } private: Impl impl_; GTEST_DISALLOW_ASSIGN_(MonomorphicImpl); }; Impl impl_; GTEST_DISALLOW_ASSIGN_(PolymorphicAction); }; // Creates an Action from its implementation and returns it. The // created Action object owns the implementation. template Action MakeAction(ActionInterface* impl) { return Action(impl); } // Creates a polymorphic action from its implementation. This is // easier to use than the PolymorphicAction constructor as it // doesn't require you to explicitly write the template argument, e.g. // // MakePolymorphicAction(foo); // vs // PolymorphicAction(foo); template inline PolymorphicAction MakePolymorphicAction(const Impl& impl) { return PolymorphicAction(impl); } namespace internal { // Allows an Action object to pose as an Action, as long as F2 // and F1 are compatible. template class ActionAdaptor : public ActionInterface { public: typedef typename internal::Function::Result Result; typedef typename internal::Function::ArgumentTuple ArgumentTuple; explicit ActionAdaptor(const Action& from) : impl_(from.impl_) {} virtual Result Perform(const ArgumentTuple& args) { return impl_->Perform(args); } private: const internal::linked_ptr > impl_; GTEST_DISALLOW_ASSIGN_(ActionAdaptor); }; // Helper struct to specialize ReturnAction to execute a move instead of a copy // on return. Useful for move-only types, but could be used on any type. template struct ByMoveWrapper { explicit ByMoveWrapper(T value) : payload(internal::move(value)) {} T payload; }; // Implements the polymorphic Return(x) action, which can be used in // any function that returns the type of x, regardless of the argument // types. // // Note: The value passed into Return must be converted into // Function::Result when this action is cast to Action rather than // when that action is performed. This is important in scenarios like // // MOCK_METHOD1(Method, T(U)); // ... // { // Foo foo; // X x(&foo); // EXPECT_CALL(mock, Method(_)).WillOnce(Return(x)); // } // // In the example above the variable x holds reference to foo which leaves // scope and gets destroyed. If copying X just copies a reference to foo, // that copy will be left with a hanging reference. If conversion to T // makes a copy of foo, the above code is safe. To support that scenario, we // need to make sure that the type conversion happens inside the EXPECT_CALL // statement, and conversion of the result of Return to Action is a // good place for that. // // The real life example of the above scenario happens when an invocation // of gtl::Container() is passed into Return. // template class ReturnAction { public: // Constructs a ReturnAction object from the value to be returned. // 'value' is passed by value instead of by const reference in order // to allow Return("string literal") to compile. explicit ReturnAction(R value) : value_(new R(internal::move(value))) {} // This template type conversion operator allows Return(x) to be // used in ANY function that returns x's type. template operator Action() const { // Assert statement belongs here because this is the best place to verify // conditions on F. It produces the clearest error messages // in most compilers. // Impl really belongs in this scope as a local class but can't // because MSVC produces duplicate symbols in different translation units // in this case. Until MS fixes that bug we put Impl into the class scope // and put the typedef both here (for use in assert statement) and // in the Impl class. But both definitions must be the same. typedef typename Function::Result Result; GTEST_COMPILE_ASSERT_( !is_reference::value, use_ReturnRef_instead_of_Return_to_return_a_reference); return Action(new Impl(value_)); } private: // Implements the Return(x) action for a particular function type F. template class Impl : public ActionInterface { public: typedef typename Function::Result Result; typedef typename Function::ArgumentTuple ArgumentTuple; // The implicit cast is necessary when Result has more than one // single-argument constructor (e.g. Result is std::vector) and R // has a type conversion operator template. In that case, value_(value) // won't compile as the compiler doesn't known which constructor of // Result to call. ImplicitCast_ forces the compiler to convert R to // Result without considering explicit constructors, thus resolving the // ambiguity. value_ is then initialized using its copy constructor. explicit Impl(const linked_ptr& value) : value_before_cast_(*value), value_(ImplicitCast_(value_before_cast_)) {} virtual Result Perform(const ArgumentTuple&) { return value_; } private: GTEST_COMPILE_ASSERT_(!is_reference::value, Result_cannot_be_a_reference_type); // We save the value before casting just in case it is being cast to a // wrapper type. R value_before_cast_; Result value_; GTEST_DISALLOW_COPY_AND_ASSIGN_(Impl); }; // Partially specialize for ByMoveWrapper. This version of ReturnAction will // move its contents instead. template class Impl, F> : public ActionInterface { public: typedef typename Function::Result Result; typedef typename Function::ArgumentTuple ArgumentTuple; explicit Impl(const linked_ptr& wrapper) : performed_(false), wrapper_(wrapper) {} virtual Result Perform(const ArgumentTuple&) { GTEST_CHECK_(!performed_) << "A ByMove() action should only be performed once."; performed_ = true; return internal::move(wrapper_->payload); } private: bool performed_; const linked_ptr wrapper_; GTEST_DISALLOW_ASSIGN_(Impl); }; const linked_ptr value_; GTEST_DISALLOW_ASSIGN_(ReturnAction); }; // Implements the ReturnNull() action. class ReturnNullAction { public: // Allows ReturnNull() to be used in any pointer-returning function. In C++11 // this is enforced by returning nullptr, and in non-C++11 by asserting a // pointer type on compile time. template static Result Perform(const ArgumentTuple&) { #if GTEST_LANG_CXX11 return nullptr; #else GTEST_COMPILE_ASSERT_(internal::is_pointer::value, ReturnNull_can_be_used_to_return_a_pointer_only); return NULL; #endif // GTEST_LANG_CXX11 } }; // Implements the Return() action. class ReturnVoidAction { public: // Allows Return() to be used in any void-returning function. template static void Perform(const ArgumentTuple&) { CompileAssertTypesEqual(); } }; // Implements the polymorphic ReturnRef(x) action, which can be used // in any function that returns a reference to the type of x, // regardless of the argument types. template class ReturnRefAction { public: // Constructs a ReturnRefAction object from the reference to be returned. explicit ReturnRefAction(T& ref) : ref_(ref) {} // NOLINT // This template type conversion operator allows ReturnRef(x) to be // used in ANY function that returns a reference to x's type. template operator Action() const { typedef typename Function::Result Result; // Asserts that the function return type is a reference. This // catches the user error of using ReturnRef(x) when Return(x) // should be used, and generates some helpful error message. GTEST_COMPILE_ASSERT_(internal::is_reference::value, use_Return_instead_of_ReturnRef_to_return_a_value); return Action(new Impl(ref_)); } private: // Implements the ReturnRef(x) action for a particular function type F. template class Impl : public ActionInterface { public: typedef typename Function::Result Result; typedef typename Function::ArgumentTuple ArgumentTuple; explicit Impl(T& ref) : ref_(ref) {} // NOLINT virtual Result Perform(const ArgumentTuple&) { return ref_; } private: T& ref_; GTEST_DISALLOW_ASSIGN_(Impl); }; T& ref_; GTEST_DISALLOW_ASSIGN_(ReturnRefAction); }; // Implements the polymorphic ReturnRefOfCopy(x) action, which can be // used in any function that returns a reference to the type of x, // regardless of the argument types. template class ReturnRefOfCopyAction { public: // Constructs a ReturnRefOfCopyAction object from the reference to // be returned. explicit ReturnRefOfCopyAction(const T& value) : value_(value) {} // NOLINT // This template type conversion operator allows ReturnRefOfCopy(x) to be // used in ANY function that returns a reference to x's type. template operator Action() const { typedef typename Function::Result Result; // Asserts that the function return type is a reference. This // catches the user error of using ReturnRefOfCopy(x) when Return(x) // should be used, and generates some helpful error message. GTEST_COMPILE_ASSERT_( internal::is_reference::value, use_Return_instead_of_ReturnRefOfCopy_to_return_a_value); return Action(new Impl(value_)); } private: // Implements the ReturnRefOfCopy(x) action for a particular function type F. template class Impl : public ActionInterface { public: typedef typename Function::Result Result; typedef typename Function::ArgumentTuple ArgumentTuple; explicit Impl(const T& value) : value_(value) {} // NOLINT virtual Result Perform(const ArgumentTuple&) { return value_; } private: T value_; GTEST_DISALLOW_ASSIGN_(Impl); }; const T value_; GTEST_DISALLOW_ASSIGN_(ReturnRefOfCopyAction); }; // Implements the polymorphic DoDefault() action. class DoDefaultAction { public: // This template type conversion operator allows DoDefault() to be // used in any function. template operator Action() const { return Action(); } // NOLINT }; // Implements the Assign action to set a given pointer referent to a // particular value. template class AssignAction { public: AssignAction(T1* ptr, T2 value) : ptr_(ptr), value_(value) {} template void Perform(const ArgumentTuple& /* args */) const { *ptr_ = value_; } private: T1* const ptr_; const T2 value_; GTEST_DISALLOW_ASSIGN_(AssignAction); }; #if !GTEST_OS_WINDOWS_MOBILE // Implements the SetErrnoAndReturn action to simulate return from // various system calls and libc functions. template class SetErrnoAndReturnAction { public: SetErrnoAndReturnAction(int errno_value, T result) : errno_(errno_value), result_(result) {} template Result Perform(const ArgumentTuple& /* args */) const { errno = errno_; return result_; } private: const int errno_; const T result_; GTEST_DISALLOW_ASSIGN_(SetErrnoAndReturnAction); }; #endif // !GTEST_OS_WINDOWS_MOBILE // Implements the SetArgumentPointee(x) action for any function // whose N-th argument (0-based) is a pointer to x's type. The // template parameter kIsProto is true iff type A is ProtocolMessage, // proto2::Message, or a sub-class of those. template class SetArgumentPointeeAction { public: // Constructs an action that sets the variable pointed to by the // N-th function argument to 'value'. explicit SetArgumentPointeeAction(const A& value) : value_(value) {} template void Perform(const ArgumentTuple& args) const { CompileAssertTypesEqual(); *::testing::get(args) = value_; } private: const A value_; GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction); }; template class SetArgumentPointeeAction { public: // Constructs an action that sets the variable pointed to by the // N-th function argument to 'proto'. Both ProtocolMessage and // proto2::Message have the CopyFrom() method, so the same // implementation works for both. explicit SetArgumentPointeeAction(const Proto& proto) : proto_(new Proto) { proto_->CopyFrom(proto); } template void Perform(const ArgumentTuple& args) const { CompileAssertTypesEqual(); ::testing::get(args)->CopyFrom(*proto_); } private: const internal::linked_ptr proto_; GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction); }; // Implements the InvokeWithoutArgs(f) action. The template argument // FunctionImpl is the implementation type of f, which can be either a // function pointer or a functor. InvokeWithoutArgs(f) can be used as an // Action as long as f's type is compatible with F (i.e. f can be // assigned to a tr1::function). template class InvokeWithoutArgsAction { public: // The c'tor makes a copy of function_impl (either a function // pointer or a functor). explicit InvokeWithoutArgsAction(FunctionImpl function_impl) : function_impl_(function_impl) {} // Allows InvokeWithoutArgs(f) to be used as any action whose type is // compatible with f. template Result Perform(const ArgumentTuple&) { return function_impl_(); } private: FunctionImpl function_impl_; GTEST_DISALLOW_ASSIGN_(InvokeWithoutArgsAction); }; // Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action. template class InvokeMethodWithoutArgsAction { public: InvokeMethodWithoutArgsAction(Class* obj_ptr, MethodPtr method_ptr) : obj_ptr_(obj_ptr), method_ptr_(method_ptr) {} template Result Perform(const ArgumentTuple&) const { return (obj_ptr_->*method_ptr_)(); } private: Class* const obj_ptr_; const MethodPtr method_ptr_; GTEST_DISALLOW_ASSIGN_(InvokeMethodWithoutArgsAction); }; // Implements the InvokeWithoutArgs(callback) action. template class InvokeCallbackWithoutArgsAction { public: // The c'tor takes ownership of the callback. explicit InvokeCallbackWithoutArgsAction(CallbackType* callback) : callback_(callback) { callback->CheckIsRepeatable(); // Makes sure the callback is permanent. } // This type conversion operator template allows Invoke(callback) to // be used wherever the callback's return type can be implicitly // converted to that of the mock function. template Result Perform(const ArgumentTuple&) const { return callback_->Run(); } private: const internal::linked_ptr callback_; GTEST_DISALLOW_ASSIGN_(InvokeCallbackWithoutArgsAction); }; // Implements the IgnoreResult(action) action. template class IgnoreResultAction { public: explicit IgnoreResultAction(const A& action) : action_(action) {} template operator Action() const { // Assert statement belongs here because this is the best place to verify // conditions on F. It produces the clearest error messages // in most compilers. // Impl really belongs in this scope as a local class but can't // because MSVC produces duplicate symbols in different translation units // in this case. Until MS fixes that bug we put Impl into the class scope // and put the typedef both here (for use in assert statement) and // in the Impl class. But both definitions must be the same. typedef typename internal::Function::Result Result; // Asserts at compile time that F returns void. CompileAssertTypesEqual(); return Action(new Impl(action_)); } private: template class Impl : public ActionInterface { public: typedef typename internal::Function::Result Result; typedef typename internal::Function::ArgumentTuple ArgumentTuple; explicit Impl(const A& action) : action_(action) {} virtual void Perform(const ArgumentTuple& args) { // Performs the action and ignores its result. action_.Perform(args); } private: // Type OriginalFunction is the same as F except that its return // type is IgnoredValue. typedef typename internal::Function::MakeResultIgnoredValue OriginalFunction; const Action action_; GTEST_DISALLOW_ASSIGN_(Impl); }; const A action_; GTEST_DISALLOW_ASSIGN_(IgnoreResultAction); }; // A ReferenceWrapper object represents a reference to type T, // which can be either const or not. It can be explicitly converted // from, and implicitly converted to, a T&. Unlike a reference, // ReferenceWrapper can be copied and can survive template type // inference. This is used to support by-reference arguments in the // InvokeArgument(...) action. The idea was from "reference // wrappers" in tr1, which we don't have in our source tree yet. template class ReferenceWrapper { public: // Constructs a ReferenceWrapper object from a T&. explicit ReferenceWrapper(T& l_value) : pointer_(&l_value) {} // NOLINT // Allows a ReferenceWrapper object to be implicitly converted to // a T&. operator T&() const { return *pointer_; } private: T* pointer_; }; // Allows the expression ByRef(x) to be printed as a reference to x. template void PrintTo(const ReferenceWrapper& ref, ::std::ostream* os) { T& value = ref; UniversalPrinter::Print(value, os); } // Does two actions sequentially. Used for implementing the DoAll(a1, // a2, ...) action. template class DoBothAction { public: DoBothAction(Action1 action1, Action2 action2) : action1_(action1), action2_(action2) {} // This template type conversion operator allows DoAll(a1, ..., a_n) // to be used in ANY function of compatible type. template operator Action() const { return Action(new Impl(action1_, action2_)); } private: // Implements the DoAll(...) action for a particular function type F. template class Impl : public ActionInterface { public: typedef typename Function::Result Result; typedef typename Function::ArgumentTuple ArgumentTuple; typedef typename Function::MakeResultVoid VoidResult; Impl(const Action& action1, const Action& action2) : action1_(action1), action2_(action2) {} virtual Result Perform(const ArgumentTuple& args) { action1_.Perform(args); return action2_.Perform(args); } private: const Action action1_; const Action action2_; GTEST_DISALLOW_ASSIGN_(Impl); }; Action1 action1_; Action2 action2_; GTEST_DISALLOW_ASSIGN_(DoBothAction); }; } // namespace internal // An Unused object can be implicitly constructed from ANY value. // This is handy when defining actions that ignore some or all of the // mock function arguments. For example, given // // MOCK_METHOD3(Foo, double(const string& label, double x, double y)); // MOCK_METHOD3(Bar, double(int index, double x, double y)); // // instead of // // double DistanceToOriginWithLabel(const string& label, double x, double y) { // return sqrt(x*x + y*y); // } // double DistanceToOriginWithIndex(int index, double x, double y) { // return sqrt(x*x + y*y); // } // ... // EXPECT_CALL(mock, Foo("abc", _, _)) // .WillOnce(Invoke(DistanceToOriginWithLabel)); // EXPECT_CALL(mock, Bar(5, _, _)) // .WillOnce(Invoke(DistanceToOriginWithIndex)); // // you could write // // // We can declare any uninteresting argument as Unused. // double DistanceToOrigin(Unused, double x, double y) { // return sqrt(x*x + y*y); // } // ... // EXPECT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin)); // EXPECT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin)); typedef internal::IgnoredValue Unused; // This constructor allows us to turn an Action object into an // Action, as long as To's arguments can be implicitly converted // to From's and From's return type cann be implicitly converted to // To's. template template Action::Action(const Action& from) : #if GTEST_LANG_CXX11 fun_(from.fun_), #endif impl_(from.impl_ == NULL ? NULL : new internal::ActionAdaptor(from)) { } // Creates an action that returns 'value'. 'value' is passed by value // instead of const reference - otherwise Return("string literal") // will trigger a compiler error about using array as initializer. template internal::ReturnAction Return(R value) { return internal::ReturnAction(internal::move(value)); } // Creates an action that returns NULL. inline PolymorphicAction ReturnNull() { return MakePolymorphicAction(internal::ReturnNullAction()); } // Creates an action that returns from a void function. inline PolymorphicAction Return() { return MakePolymorphicAction(internal::ReturnVoidAction()); } // Creates an action that returns the reference to a variable. template inline internal::ReturnRefAction ReturnRef(R& x) { // NOLINT return internal::ReturnRefAction(x); } // Creates an action that returns the reference to a copy of the // argument. The copy is created when the action is constructed and // lives as long as the action. template inline internal::ReturnRefOfCopyAction ReturnRefOfCopy(const R& x) { return internal::ReturnRefOfCopyAction(x); } // Modifies the parent action (a Return() action) to perform a move of the // argument instead of a copy. // Return(ByMove()) actions can only be executed once and will assert this // invariant. template internal::ByMoveWrapper ByMove(R x) { return internal::ByMoveWrapper(internal::move(x)); } // Creates an action that does the default action for the give mock function. inline internal::DoDefaultAction DoDefault() { return internal::DoDefaultAction(); } // Creates an action that sets the variable pointed by the N-th // (0-based) function argument to 'value'. template PolymorphicAction< internal::SetArgumentPointeeAction< N, T, internal::IsAProtocolMessage::value> > SetArgPointee(const T& x) { return MakePolymorphicAction(internal::SetArgumentPointeeAction< N, T, internal::IsAProtocolMessage::value>(x)); } #if !((GTEST_GCC_VER_ && GTEST_GCC_VER_ < 40000) || GTEST_OS_SYMBIAN) // This overload allows SetArgPointee() to accept a string literal. // GCC prior to the version 4.0 and Symbian C++ compiler cannot distinguish // this overload from the templated version and emit a compile error. template PolymorphicAction< internal::SetArgumentPointeeAction > SetArgPointee(const char* p) { return MakePolymorphicAction(internal::SetArgumentPointeeAction< N, const char*, false>(p)); } template PolymorphicAction< internal::SetArgumentPointeeAction > SetArgPointee(const wchar_t* p) { return MakePolymorphicAction(internal::SetArgumentPointeeAction< N, const wchar_t*, false>(p)); } #endif // The following version is DEPRECATED. template PolymorphicAction< internal::SetArgumentPointeeAction< N, T, internal::IsAProtocolMessage::value> > SetArgumentPointee(const T& x) { return MakePolymorphicAction(internal::SetArgumentPointeeAction< N, T, internal::IsAProtocolMessage::value>(x)); } // Creates an action that sets a pointer referent to a given value. template PolymorphicAction > Assign(T1* ptr, T2 val) { return MakePolymorphicAction(internal::AssignAction(ptr, val)); } #if !GTEST_OS_WINDOWS_MOBILE // Creates an action that sets errno and returns the appropriate error. template PolymorphicAction > SetErrnoAndReturn(int errval, T result) { return MakePolymorphicAction( internal::SetErrnoAndReturnAction(errval, result)); } #endif // !GTEST_OS_WINDOWS_MOBILE // Various overloads for InvokeWithoutArgs(). // Creates an action that invokes 'function_impl' with no argument. template PolymorphicAction > InvokeWithoutArgs(FunctionImpl function_impl) { return MakePolymorphicAction( internal::InvokeWithoutArgsAction(function_impl)); } // Creates an action that invokes the given method on the given object // with no argument. template PolymorphicAction > InvokeWithoutArgs(Class* obj_ptr, MethodPtr method_ptr) { return MakePolymorphicAction( internal::InvokeMethodWithoutArgsAction( obj_ptr, method_ptr)); } // Creates an action that performs an_action and throws away its // result. In other words, it changes the return type of an_action to // void. an_action MUST NOT return void, or the code won't compile. template inline internal::IgnoreResultAction IgnoreResult(const A& an_action) { return internal::IgnoreResultAction(an_action); } // Creates a reference wrapper for the given L-value. If necessary, // you can explicitly specify the type of the reference. For example, // suppose 'derived' is an object of type Derived, ByRef(derived) // would wrap a Derived&. If you want to wrap a const Base& instead, // where Base is a base class of Derived, just write: // // ByRef(derived) template inline internal::ReferenceWrapper ByRef(T& l_value) { // NOLINT return internal::ReferenceWrapper(l_value); } } // namespace testing #endif // GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-cardinalities.h ================================================ // Copyright 2007, Google Inc. // 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 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. // Google Mock - a framework for writing C++ mock classes. // // This file implements some commonly used cardinalities. More // cardinalities can be defined by the user implementing the // CardinalityInterface interface if necessary. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ #include #include // NOLINT #include "gmock/internal/gmock-port.h" #include "gtest/gtest.h" GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ /* class A needs to have dll-interface to be used by clients of class B */) namespace testing { // To implement a cardinality Foo, define: // 1. a class FooCardinality that implements the // CardinalityInterface interface, and // 2. a factory function that creates a Cardinality object from a // const FooCardinality*. // // The two-level delegation design follows that of Matcher, providing // consistency for extension developers. It also eases ownership // management as Cardinality objects can now be copied like plain values. // The implementation of a cardinality. class CardinalityInterface { public: virtual ~CardinalityInterface() {} // Conservative estimate on the lower/upper bound of the number of // calls allowed. virtual int ConservativeLowerBound() const { return 0; } virtual int ConservativeUpperBound() const { return INT_MAX; } // Returns true iff call_count calls will satisfy this cardinality. virtual bool IsSatisfiedByCallCount(int call_count) const = 0; // Returns true iff call_count calls will saturate this cardinality. virtual bool IsSaturatedByCallCount(int call_count) const = 0; // Describes self to an ostream. virtual void DescribeTo(::std::ostream* os) const = 0; }; // A Cardinality is a copyable and IMMUTABLE (except by assignment) // object that specifies how many times a mock function is expected to // be called. The implementation of Cardinality is just a linked_ptr // to const CardinalityInterface, so copying is fairly cheap. // Don't inherit from Cardinality! class GTEST_API_ Cardinality { public: // Constructs a null cardinality. Needed for storing Cardinality // objects in STL containers. Cardinality() {} // Constructs a Cardinality from its implementation. explicit Cardinality(const CardinalityInterface* impl) : impl_(impl) {} // Conservative estimate on the lower/upper bound of the number of // calls allowed. int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); } int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); } // Returns true iff call_count calls will satisfy this cardinality. bool IsSatisfiedByCallCount(int call_count) const { return impl_->IsSatisfiedByCallCount(call_count); } // Returns true iff call_count calls will saturate this cardinality. bool IsSaturatedByCallCount(int call_count) const { return impl_->IsSaturatedByCallCount(call_count); } // Returns true iff call_count calls will over-saturate this // cardinality, i.e. exceed the maximum number of allowed calls. bool IsOverSaturatedByCallCount(int call_count) const { return impl_->IsSaturatedByCallCount(call_count) && !impl_->IsSatisfiedByCallCount(call_count); } // Describes self to an ostream void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } // Describes the given actual call count to an ostream. static void DescribeActualCallCountTo(int actual_call_count, ::std::ostream* os); private: internal::linked_ptr impl_; }; // Creates a cardinality that allows at least n calls. GTEST_API_ Cardinality AtLeast(int n); // Creates a cardinality that allows at most n calls. GTEST_API_ Cardinality AtMost(int n); // Creates a cardinality that allows any number of calls. GTEST_API_ Cardinality AnyNumber(); // Creates a cardinality that allows between min and max calls. GTEST_API_ Cardinality Between(int min, int max); // Creates a cardinality that allows exactly n calls. GTEST_API_ Cardinality Exactly(int n); // Creates a cardinality from its implementation. inline Cardinality MakeCardinality(const CardinalityInterface* c) { return Cardinality(c); } } // namespace testing GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 #endif // GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-generated-actions.h ================================================ // This file was GENERATED by command: // pump.py gmock-generated-actions.h.pump // DO NOT EDIT BY HAND!!! // Copyright 2007, Google Inc. // 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 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. // Google Mock - a framework for writing C++ mock classes. // // This file implements some commonly used variadic actions. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ #include "gmock/gmock-actions.h" #include "gmock/internal/gmock-port.h" namespace testing { namespace internal { // InvokeHelper knows how to unpack an N-tuple and invoke an N-ary // function, method, or callback with the unpacked values, where F is // a function type that takes N arguments. template class InvokeHelper; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple<>&) { return function(); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple<>&) { return (obj_ptr->*method_ptr)(); } template static R InvokeCallback(CallbackType* callback, const ::testing::tuple<>&) { return callback->Run(); } }; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple& args) { return function(get<0>(args)); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple& args) { return (obj_ptr->*method_ptr)(get<0>(args)); } template static R InvokeCallback(CallbackType* callback, const ::testing::tuple& args) { return callback->Run(get<0>(args)); } }; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple& args) { return function(get<0>(args), get<1>(args)); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple& args) { return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args)); } template static R InvokeCallback(CallbackType* callback, const ::testing::tuple& args) { return callback->Run(get<0>(args), get<1>(args)); } }; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple& args) { return function(get<0>(args), get<1>(args), get<2>(args)); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple& args) { return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args)); } template static R InvokeCallback(CallbackType* callback, const ::testing::tuple& args) { return callback->Run(get<0>(args), get<1>(args), get<2>(args)); } }; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple& args) { return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args)); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple& args) { return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), get<3>(args)); } template static R InvokeCallback(CallbackType* callback, const ::testing::tuple& args) { return callback->Run(get<0>(args), get<1>(args), get<2>(args), get<3>(args)); } }; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple& args) { return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args)); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple& args) { return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args)); } template static R InvokeCallback(CallbackType* callback, const ::testing::tuple& args) { return callback->Run(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args)); } }; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple& args) { return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args)); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple& args) { return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args)); } // There is no InvokeCallback() for 6-tuples }; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple& args) { return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args)); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple& args) { return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args)); } // There is no InvokeCallback() for 7-tuples }; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple& args) { return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args)); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple& args) { return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args)); } // There is no InvokeCallback() for 8-tuples }; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple& args) { return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args)); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple& args) { return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args)); } // There is no InvokeCallback() for 9-tuples }; template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple& args) { return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), get<9>(args)); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple& args) { return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), get<9>(args)); } // There is no InvokeCallback() for 10-tuples }; // Implements the Invoke(callback) action. template class InvokeCallbackAction { public: // The c'tor takes ownership of the callback. explicit InvokeCallbackAction(CallbackType* callback) : callback_(callback) { callback->CheckIsRepeatable(); // Makes sure the callback is permanent. } // This type conversion operator template allows Invoke(callback) to // be used wherever the callback's type is compatible with that of // the mock function, i.e. if the mock function's arguments can be // implicitly converted to the callback's arguments and the // callback's result can be implicitly converted to the mock // function's result. template Result Perform(const ArgumentTuple& args) const { return InvokeHelper::InvokeCallback( callback_.get(), args); } private: const linked_ptr callback_; }; // An INTERNAL macro for extracting the type of a tuple field. It's // subject to change without notice - DO NOT USE IN USER CODE! #define GMOCK_FIELD_(Tuple, N) \ typename ::testing::tuple_element::type // SelectArgs::type is the // type of an n-ary function whose i-th (1-based) argument type is the // k{i}-th (0-based) field of ArgumentTuple, which must be a tuple // type, and whose return type is Result. For example, // SelectArgs, 0, 3>::type // is int(bool, long). // // SelectArgs::Select(args) // returns the selected fields (k1, k2, ..., k_n) of args as a tuple. // For example, // SelectArgs, 2, 0>::Select( // ::testing::make_tuple(true, 'a', 2.5)) // returns tuple (2.5, true). // // The numbers in list k1, k2, ..., k_n must be >= 0, where n can be // in the range [0, 10]. Duplicates are allowed and they don't have // to be in an ascending or descending order. template class SelectArgs { public: typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9), GMOCK_FIELD_(ArgumentTuple, k10)); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs(get(args), get(args), get(args), get(args), get(args), get(args), get(args), get(args), get(args), get(args)); } }; template class SelectArgs { public: typedef Result type(); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& /* args */) { return SelectedArgs(); } }; template class SelectArgs { public: typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1)); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs(get(args)); } }; template class SelectArgs { public: typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), GMOCK_FIELD_(ArgumentTuple, k2)); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs(get(args), get(args)); } }; template class SelectArgs { public: typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3)); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs(get(args), get(args), get(args)); } }; template class SelectArgs { public: typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), GMOCK_FIELD_(ArgumentTuple, k4)); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs(get(args), get(args), get(args), get(args)); } }; template class SelectArgs { public: typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5)); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs(get(args), get(args), get(args), get(args), get(args)); } }; template class SelectArgs { public: typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), GMOCK_FIELD_(ArgumentTuple, k6)); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs(get(args), get(args), get(args), get(args), get(args), get(args)); } }; template class SelectArgs { public: typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7)); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs(get(args), get(args), get(args), get(args), get(args), get(args), get(args)); } }; template class SelectArgs { public: typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), GMOCK_FIELD_(ArgumentTuple, k8)); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs(get(args), get(args), get(args), get(args), get(args), get(args), get(args), get(args)); } }; template class SelectArgs { public: typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9)); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs(get(args), get(args), get(args), get(args), get(args), get(args), get(args), get(args), get(args)); } }; #undef GMOCK_FIELD_ // Implements the WithArgs action. template class WithArgsAction { public: explicit WithArgsAction(const InnerAction& action) : action_(action) {} template operator Action() const { return MakeAction(new Impl(action_)); } private: template class Impl : public ActionInterface { public: typedef typename Function::Result Result; typedef typename Function::ArgumentTuple ArgumentTuple; explicit Impl(const InnerAction& action) : action_(action) {} virtual Result Perform(const ArgumentTuple& args) { return action_.Perform(SelectArgs::Select(args)); } private: typedef typename SelectArgs::type InnerFunctionType; Action action_; }; const InnerAction action_; GTEST_DISALLOW_ASSIGN_(WithArgsAction); }; // A macro from the ACTION* family (defined later in this file) // defines an action that can be used in a mock function. Typically, // these actions only care about a subset of the arguments of the mock // function. For example, if such an action only uses the second // argument, it can be used in any mock function that takes >= 2 // arguments where the type of the second argument is compatible. // // Therefore, the action implementation must be prepared to take more // arguments than it needs. The ExcessiveArg type is used to // represent those excessive arguments. In order to keep the compiler // error messages tractable, we define it in the testing namespace // instead of testing::internal. However, this is an INTERNAL TYPE // and subject to change without notice, so a user MUST NOT USE THIS // TYPE DIRECTLY. struct ExcessiveArg {}; // A helper class needed for implementing the ACTION* macros. template class ActionHelper { public: static Result Perform(Impl* impl, const ::testing::tuple<>& args) { return impl->template gmock_PerformImpl<>(args, ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); } template static Result Perform(Impl* impl, const ::testing::tuple& args) { return impl->template gmock_PerformImpl(args, get<0>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); } template static Result Perform(Impl* impl, const ::testing::tuple& args) { return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); } template static Result Perform(Impl* impl, const ::testing::tuple& args) { return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); } template static Result Perform(Impl* impl, const ::testing::tuple& args) { return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); } template static Result Perform(Impl* impl, const ::testing::tuple& args) { return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); } template static Result Perform(Impl* impl, const ::testing::tuple& args) { return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); } template static Result Perform(Impl* impl, const ::testing::tuple& args) { return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); } template static Result Perform(Impl* impl, const ::testing::tuple& args) { return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), ExcessiveArg(), ExcessiveArg()); } template static Result Perform(Impl* impl, const ::testing::tuple& args) { return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), ExcessiveArg()); } template static Result Perform(Impl* impl, const ::testing::tuple& args) { return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), get<9>(args)); } }; } // namespace internal // Various overloads for Invoke(). // WithArgs(an_action) creates an action that passes // the selected arguments of the mock function to an_action and // performs it. It serves as an adaptor between actions with // different argument lists. C++ doesn't support default arguments for // function templates, so we have to overload it. template inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } template inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } template inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } template inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } template inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } template inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } template inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } template inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } template inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } template inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } // Creates an action that does actions a1, a2, ..., sequentially in // each invocation. template inline internal::DoBothAction DoAll(Action1 a1, Action2 a2) { return internal::DoBothAction(a1, a2); } template inline internal::DoBothAction > DoAll(Action1 a1, Action2 a2, Action3 a3) { return DoAll(a1, DoAll(a2, a3)); } template inline internal::DoBothAction > > DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4) { return DoAll(a1, DoAll(a2, a3, a4)); } template inline internal::DoBothAction > > > DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5) { return DoAll(a1, DoAll(a2, a3, a4, a5)); } template inline internal::DoBothAction > > > > DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6) { return DoAll(a1, DoAll(a2, a3, a4, a5, a6)); } template inline internal::DoBothAction > > > > > DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, Action7 a7) { return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7)); } template inline internal::DoBothAction > > > > > > DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, Action7 a7, Action8 a8) { return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8)); } template inline internal::DoBothAction > > > > > > > DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, Action7 a7, Action8 a8, Action9 a9) { return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9)); } template inline internal::DoBothAction > > > > > > > > DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, Action7 a7, Action8 a8, Action9 a9, Action10 a10) { return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9, a10)); } } // namespace testing // The ACTION* family of macros can be used in a namespace scope to // define custom actions easily. The syntax: // // ACTION(name) { statements; } // // will define an action with the given name that executes the // statements. The value returned by the statements will be used as // the return value of the action. Inside the statements, you can // refer to the K-th (0-based) argument of the mock function by // 'argK', and refer to its type by 'argK_type'. For example: // // ACTION(IncrementArg1) { // arg1_type temp = arg1; // return ++(*temp); // } // // allows you to write // // ...WillOnce(IncrementArg1()); // // You can also refer to the entire argument tuple and its type by // 'args' and 'args_type', and refer to the mock function type and its // return type by 'function_type' and 'return_type'. // // Note that you don't need to specify the types of the mock function // arguments. However rest assured that your code is still type-safe: // you'll get a compiler error if *arg1 doesn't support the ++ // operator, or if the type of ++(*arg1) isn't compatible with the // mock function's return type, for example. // // Sometimes you'll want to parameterize the action. For that you can use // another macro: // // ACTION_P(name, param_name) { statements; } // // For example: // // ACTION_P(Add, n) { return arg0 + n; } // // will allow you to write: // // ...WillOnce(Add(5)); // // Note that you don't need to provide the type of the parameter // either. If you need to reference the type of a parameter named // 'foo', you can write 'foo_type'. For example, in the body of // ACTION_P(Add, n) above, you can write 'n_type' to refer to the type // of 'n'. // // We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support // multi-parameter actions. // // For the purpose of typing, you can view // // ACTION_Pk(Foo, p1, ..., pk) { ... } // // as shorthand for // // template // FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } // // In particular, you can provide the template type arguments // explicitly when invoking Foo(), as in Foo(5, false); // although usually you can rely on the compiler to infer the types // for you automatically. You can assign the result of expression // Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. // // You can also overload actions with different numbers of parameters: // // ACTION_P(Plus, a) { ... } // ACTION_P2(Plus, a, b) { ... } // // While it's tempting to always use the ACTION* macros when defining // a new action, you should also consider implementing ActionInterface // or using MakePolymorphicAction() instead, especially if you need to // use the action a lot. While these approaches require more work, // they give you more control on the types of the mock function // arguments and the action parameters, which in general leads to // better compiler error messages that pay off in the long run. They // also allow overloading actions based on parameter types (as opposed // to just based on the number of parameters). // // CAVEAT: // // ACTION*() can only be used in a namespace scope. The reason is // that C++ doesn't yet allow function-local types to be used to // instantiate templates. The up-coming C++0x standard will fix this. // Once that's done, we'll consider supporting using ACTION*() inside // a function. // // MORE INFORMATION: // // To learn more about using these macros, please search for 'ACTION' // on https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md // An internal macro needed for implementing ACTION*(). #define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\ const args_type& args GTEST_ATTRIBUTE_UNUSED_, \ arg0_type arg0 GTEST_ATTRIBUTE_UNUSED_, \ arg1_type arg1 GTEST_ATTRIBUTE_UNUSED_, \ arg2_type arg2 GTEST_ATTRIBUTE_UNUSED_, \ arg3_type arg3 GTEST_ATTRIBUTE_UNUSED_, \ arg4_type arg4 GTEST_ATTRIBUTE_UNUSED_, \ arg5_type arg5 GTEST_ATTRIBUTE_UNUSED_, \ arg6_type arg6 GTEST_ATTRIBUTE_UNUSED_, \ arg7_type arg7 GTEST_ATTRIBUTE_UNUSED_, \ arg8_type arg8 GTEST_ATTRIBUTE_UNUSED_, \ arg9_type arg9 GTEST_ATTRIBUTE_UNUSED_ // Sometimes you want to give an action explicit template parameters // that cannot be inferred from its value parameters. ACTION() and // ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that // and can be viewed as an extension to ACTION() and ACTION_P*(). // // The syntax: // // ACTION_TEMPLATE(ActionName, // HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), // AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } // // defines an action template that takes m explicit template // parameters and n value parameters. name_i is the name of the i-th // template parameter, and kind_i specifies whether it's a typename, // an integral constant, or a template. p_i is the name of the i-th // value parameter. // // Example: // // // DuplicateArg(output) converts the k-th argument of the mock // // function to type T and copies it to *output. // ACTION_TEMPLATE(DuplicateArg, // HAS_2_TEMPLATE_PARAMS(int, k, typename, T), // AND_1_VALUE_PARAMS(output)) { // *output = T(::testing::get(args)); // } // ... // int n; // EXPECT_CALL(mock, Foo(_, _)) // .WillOnce(DuplicateArg<1, unsigned char>(&n)); // // To create an instance of an action template, write: // // ActionName(v1, ..., v_n) // // where the ts are the template arguments and the vs are the value // arguments. The value argument types are inferred by the compiler. // If you want to explicitly specify the value argument types, you can // provide additional template arguments: // // ActionName(v1, ..., v_n) // // where u_i is the desired type of v_i. // // ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the // number of value parameters, but not on the number of template // parameters. Without the restriction, the meaning of the following // is unclear: // // OverloadedAction(x); // // Are we using a single-template-parameter action where 'bool' refers // to the type of x, or are we using a two-template-parameter action // where the compiler is asked to infer the type of x? // // Implementation notes: // // GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and // GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for // implementing ACTION_TEMPLATE. The main trick we use is to create // new macro invocations when expanding a macro. For example, we have // // #define ACTION_TEMPLATE(name, template_params, value_params) // ... GMOCK_INTERNAL_DECL_##template_params ... // // which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...) // to expand to // // ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ... // // Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the // preprocessor will continue to expand it to // // ... typename T ... // // This technique conforms to the C++ standard and is portable. It // allows us to implement action templates using O(N) code, where N is // the maximum number of template/value parameters supported. Without // using it, we'd have to devote O(N^2) amount of code to implement all // combinations of m and n. // Declares the template parameters. #define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0 #define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ name1) kind0 name0, kind1 name1 #define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2) kind0 name0, kind1 name1, kind2 name2 #define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \ kind3 name3 #define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \ kind2 name2, kind3 name3, kind4 name4 #define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \ kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5 #define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \ kind5 name5, kind6 name6 #define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \ kind4 name4, kind5 name5, kind6 name6, kind7 name7 #define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \ kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \ kind8 name8 #define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \ kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \ kind6 name6, kind7 name7, kind8 name8, kind9 name9 // Lists the template parameters. #define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0 #define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ name1) name0, name1 #define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2) name0, name1, name2 #define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3) name0, name1, name2, name3 #define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \ name4 #define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \ name2, name3, name4, name5 #define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ name6) name0, name1, name2, name3, name4, name5, name6 #define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7 #define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \ name6, name7, name8 #define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \ name3, name4, name5, name6, name7, name8, name9 // Declares the types of value parameters. #define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS() #define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type #define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \ typename p0##_type, typename p1##_type #define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \ typename p0##_type, typename p1##_type, typename p2##_type #define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ typename p0##_type, typename p1##_type, typename p2##_type, \ typename p3##_type #define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ typename p0##_type, typename p1##_type, typename p2##_type, \ typename p3##_type, typename p4##_type #define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ typename p0##_type, typename p1##_type, typename p2##_type, \ typename p3##_type, typename p4##_type, typename p5##_type #define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6) , typename p0##_type, typename p1##_type, typename p2##_type, \ typename p3##_type, typename p4##_type, typename p5##_type, \ typename p6##_type #define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \ typename p3##_type, typename p4##_type, typename p5##_type, \ typename p6##_type, typename p7##_type #define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \ typename p3##_type, typename p4##_type, typename p5##_type, \ typename p6##_type, typename p7##_type, typename p8##_type #define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \ typename p2##_type, typename p3##_type, typename p4##_type, \ typename p5##_type, typename p6##_type, typename p7##_type, \ typename p8##_type, typename p9##_type // Initializes the value parameters. #define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\ () #define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\ (p0##_type gmock_p0) : p0(::testing::internal::move(gmock_p0)) #define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\ (p0##_type gmock_p0, \ p1##_type gmock_p1) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)) #define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\ (p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)) #define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)) #define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, \ p4##_type gmock_p4) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)) #define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)) #define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)) #define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6, \ p7##_type gmock_p7) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)), \ p7(::testing::internal::move(gmock_p7)) #define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7, p8)\ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6, p7##_type gmock_p7, \ p8##_type gmock_p8) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)), \ p7(::testing::internal::move(gmock_p7)), \ p8(::testing::internal::move(gmock_p8)) #define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7, p8, p9)\ (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ p9##_type gmock_p9) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)), \ p7(::testing::internal::move(gmock_p7)), \ p8(::testing::internal::move(gmock_p8)), \ p9(::testing::internal::move(gmock_p9)) // Declares the fields for storing the value parameters. #define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS() #define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0; #define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \ p1##_type p1; #define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \ p1##_type p1; p2##_type p2; #define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \ p1##_type p1; p2##_type p2; p3##_type p3; #define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; #define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ p5##_type p5; #define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ p5##_type p5; p6##_type p6; #define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ p5##_type p5; p6##_type p6; p7##_type p7; #define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; #define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \ p9##_type p9; // Lists the value parameters. #define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS() #define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0 #define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1 #define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2 #define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3 #define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \ p2, p3, p4 #define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \ p1, p2, p3, p4, p5 #define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6) p0, p1, p2, p3, p4, p5, p6 #define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7) p0, p1, p2, p3, p4, p5, p6, p7 #define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8 #define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9 // Lists the value parameter types. #define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS() #define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type #define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \ p1##_type #define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \ p1##_type, p2##_type #define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ p0##_type, p1##_type, p2##_type, p3##_type #define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ p0##_type, p1##_type, p2##_type, p3##_type, p4##_type #define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type #define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \ p6##_type #define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ p5##_type, p6##_type, p7##_type #define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ p5##_type, p6##_type, p7##_type, p8##_type #define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ p5##_type, p6##_type, p7##_type, p8##_type, p9##_type // Declares the value parameters. #define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS() #define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0 #define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \ p1##_type p1 #define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \ p1##_type p1, p2##_type p2 #define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \ p1##_type p1, p2##_type p2, p3##_type p3 #define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4 #define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ p5##_type p5 #define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ p5##_type p5, p6##_type p6 #define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ p5##_type p5, p6##_type p6, p7##_type p7 #define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8 #define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ p9##_type p9 // The suffix of the class template implementing the action template. #define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS() #define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P #define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2 #define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3 #define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4 #define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5 #define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6 #define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7 #define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7) P8 #define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7, p8) P9 #define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ p7, p8, p9) P10 // The name of the class template implementing the action template. #define GMOCK_ACTION_CLASS_(name, value_params)\ GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params) #define ACTION_TEMPLATE(name, template_params, value_params)\ template \ class GMOCK_ACTION_CLASS_(name, value_params) {\ public:\ explicit GMOCK_ACTION_CLASS_(name, value_params)\ GMOCK_INTERNAL_INIT_##value_params {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ GMOCK_INTERNAL_DEFN_##value_params\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(\ new gmock_Impl(GMOCK_INTERNAL_LIST_##value_params));\ }\ GMOCK_INTERNAL_DEFN_##value_params\ private:\ GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\ };\ template \ inline GMOCK_ACTION_CLASS_(name, value_params)<\ GMOCK_INTERNAL_LIST_##template_params\ GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\ GMOCK_INTERNAL_DECL_##value_params) {\ return GMOCK_ACTION_CLASS_(name, value_params)<\ GMOCK_INTERNAL_LIST_##template_params\ GMOCK_INTERNAL_LIST_TYPE_##value_params>(\ GMOCK_INTERNAL_LIST_##value_params);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ GMOCK_ACTION_CLASS_(name, value_params)<\ GMOCK_INTERNAL_LIST_##template_params\ GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::\ gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION(name)\ class name##Action {\ public:\ name##Action() {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ gmock_Impl() {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl());\ }\ private:\ GTEST_DISALLOW_ASSIGN_(name##Action);\ };\ inline name##Action name() {\ return name##Action();\ }\ template \ template \ typename ::testing::internal::Function::Result\ name##Action::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION_P(name, p0)\ template \ class name##ActionP {\ public:\ explicit name##ActionP(p0##_type gmock_p0) : \ p0(::testing::internal::forward(gmock_p0)) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ explicit gmock_Impl(p0##_type gmock_p0) : \ p0(::testing::internal::forward(gmock_p0)) {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ p0##_type p0;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl(p0));\ }\ p0##_type p0;\ private:\ GTEST_DISALLOW_ASSIGN_(name##ActionP);\ };\ template \ inline name##ActionP name(p0##_type p0) {\ return name##ActionP(p0);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ name##ActionP::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION_P2(name, p0, p1)\ template \ class name##ActionP2 {\ public:\ name##ActionP2(p0##_type gmock_p0, \ p1##_type gmock_p1) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ gmock_Impl(p0##_type gmock_p0, \ p1##_type gmock_p1) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)) {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ p0##_type p0;\ p1##_type p1;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl(p0, p1));\ }\ p0##_type p0;\ p1##_type p1;\ private:\ GTEST_DISALLOW_ASSIGN_(name##ActionP2);\ };\ template \ inline name##ActionP2 name(p0##_type p0, \ p1##_type p1) {\ return name##ActionP2(p0, p1);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ name##ActionP2::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION_P3(name, p0, p1, p2)\ template \ class name##ActionP3 {\ public:\ name##ActionP3(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)) {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl(p0, p1, p2));\ }\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ private:\ GTEST_DISALLOW_ASSIGN_(name##ActionP3);\ };\ template \ inline name##ActionP3 name(p0##_type p0, \ p1##_type p1, p2##_type p2) {\ return name##ActionP3(p0, p1, p2);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ name##ActionP3::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION_P4(name, p0, p1, p2, p3)\ template \ class name##ActionP4 {\ public:\ name##ActionP4(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, \ p3##_type gmock_p3) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)) {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl(p0, p1, p2, p3));\ }\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ private:\ GTEST_DISALLOW_ASSIGN_(name##ActionP4);\ };\ template \ inline name##ActionP4 name(p0##_type p0, p1##_type p1, p2##_type p2, \ p3##_type p3) {\ return name##ActionP4(p0, p1, \ p2, p3);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ name##ActionP4::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION_P5(name, p0, p1, p2, p3, p4)\ template \ class name##ActionP5 {\ public:\ name##ActionP5(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, \ p4##_type gmock_p4) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, \ p4##_type gmock_p4) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)) {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4));\ }\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ private:\ GTEST_DISALLOW_ASSIGN_(name##ActionP5);\ };\ template \ inline name##ActionP5 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ p4##_type p4) {\ return name##ActionP5(p0, p1, p2, p3, p4);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ name##ActionP5::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION_P6(name, p0, p1, p2, p3, p4, p5)\ template \ class name##ActionP6 {\ public:\ name##ActionP6(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)), \ p5(::testing::internal::forward(gmock_p5)) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)), \ p5(::testing::internal::forward(gmock_p5)) {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ p5##_type p5;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5));\ }\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ p5##_type p5;\ private:\ GTEST_DISALLOW_ASSIGN_(name##ActionP6);\ };\ template \ inline name##ActionP6 name(p0##_type p0, p1##_type p1, p2##_type p2, \ p3##_type p3, p4##_type p4, p5##_type p5) {\ return name##ActionP6(p0, p1, p2, p3, p4, p5);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ name##ActionP6::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION_P7(name, p0, p1, p2, p3, p4, p5, p6)\ template \ class name##ActionP7 {\ public:\ name##ActionP7(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5, \ p6##_type gmock_p6) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)), \ p5(::testing::internal::forward(gmock_p5)), \ p6(::testing::internal::forward(gmock_p6)) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)), \ p5(::testing::internal::forward(gmock_p5)), \ p6(::testing::internal::forward(gmock_p6)) {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ p5##_type p5;\ p6##_type p6;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ p6));\ }\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ p5##_type p5;\ p6##_type p6;\ private:\ GTEST_DISALLOW_ASSIGN_(name##ActionP7);\ };\ template \ inline name##ActionP7 name(p0##_type p0, p1##_type p1, \ p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ p6##_type p6) {\ return name##ActionP7(p0, p1, p2, p3, p4, p5, p6);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ name##ActionP7::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION_P8(name, p0, p1, p2, p3, p4, p5, p6, p7)\ template \ class name##ActionP8 {\ public:\ name##ActionP8(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5, p6##_type gmock_p6, \ p7##_type gmock_p7) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)), \ p5(::testing::internal::forward(gmock_p5)), \ p6(::testing::internal::forward(gmock_p6)), \ p7(::testing::internal::forward(gmock_p7)) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6, \ p7##_type gmock_p7) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)), \ p5(::testing::internal::forward(gmock_p5)), \ p6(::testing::internal::forward(gmock_p6)), \ p7(::testing::internal::forward(gmock_p7)) {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ p5##_type p5;\ p6##_type p6;\ p7##_type p7;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ p6, p7));\ }\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ p5##_type p5;\ p6##_type p6;\ p7##_type p7;\ private:\ GTEST_DISALLOW_ASSIGN_(name##ActionP8);\ };\ template \ inline name##ActionP8 name(p0##_type p0, \ p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ p6##_type p6, p7##_type p7) {\ return name##ActionP8(p0, p1, p2, p3, p4, p5, \ p6, p7);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ name##ActionP8::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8)\ template \ class name##ActionP9 {\ public:\ name##ActionP9(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ p8##_type gmock_p8) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)), \ p5(::testing::internal::forward(gmock_p5)), \ p6(::testing::internal::forward(gmock_p6)), \ p7(::testing::internal::forward(gmock_p7)), \ p8(::testing::internal::forward(gmock_p8)) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6, p7##_type gmock_p7, \ p8##_type gmock_p8) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)), \ p5(::testing::internal::forward(gmock_p5)), \ p6(::testing::internal::forward(gmock_p6)), \ p7(::testing::internal::forward(gmock_p7)), \ p8(::testing::internal::forward(gmock_p8)) {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ p5##_type p5;\ p6##_type p6;\ p7##_type p7;\ p8##_type p8;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ p6, p7, p8));\ }\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ p5##_type p5;\ p6##_type p6;\ p7##_type p7;\ p8##_type p8;\ private:\ GTEST_DISALLOW_ASSIGN_(name##ActionP9);\ };\ template \ inline name##ActionP9 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \ p8##_type p8) {\ return name##ActionP9(p0, p1, p2, \ p3, p4, p5, p6, p7, p8);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ name##ActionP9::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const #define ACTION_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)\ template \ class name##ActionP10 {\ public:\ name##ActionP10(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ p8##_type gmock_p8, \ p9##_type gmock_p9) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)), \ p5(::testing::internal::forward(gmock_p5)), \ p6(::testing::internal::forward(gmock_p6)), \ p7(::testing::internal::forward(gmock_p7)), \ p8(::testing::internal::forward(gmock_p8)), \ p9(::testing::internal::forward(gmock_p9)) {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ p9##_type gmock_p9) : p0(::testing::internal::forward(gmock_p0)), \ p1(::testing::internal::forward(gmock_p1)), \ p2(::testing::internal::forward(gmock_p2)), \ p3(::testing::internal::forward(gmock_p3)), \ p4(::testing::internal::forward(gmock_p4)), \ p5(::testing::internal::forward(gmock_p5)), \ p6(::testing::internal::forward(gmock_p6)), \ p7(::testing::internal::forward(gmock_p7)), \ p8(::testing::internal::forward(gmock_p8)), \ p9(::testing::internal::forward(gmock_p9)) {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template \ return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ arg9_type arg9) const;\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ p5##_type p5;\ p6##_type p6;\ p7##_type p7;\ p8##_type p8;\ p9##_type p9;\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ p6, p7, p8, p9));\ }\ p0##_type p0;\ p1##_type p1;\ p2##_type p2;\ p3##_type p3;\ p4##_type p4;\ p5##_type p5;\ p6##_type p6;\ p7##_type p7;\ p8##_type p8;\ p9##_type p9;\ private:\ GTEST_DISALLOW_ASSIGN_(name##ActionP10);\ };\ template \ inline name##ActionP10 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ p9##_type p9) {\ return name##ActionP10(p0, \ p1, p2, p3, p4, p5, p6, p7, p8, p9);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ name##ActionP10::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const namespace testing { // The ACTION*() macros trigger warning C4100 (unreferenced formal // parameter) in MSVC with -W4. Unfortunately they cannot be fixed in // the macro definition, as the warnings are generated when the macro // is expanded and macro expansion cannot contain #pragma. Therefore // we suppress them here. #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4100) #endif // Various overloads for InvokeArgument(). // // The InvokeArgument(a1, a2, ..., a_k) action invokes the N-th // (0-based) argument, which must be a k-ary callable, of the mock // function, with arguments a1, a2, ..., a_k. // // Notes: // // 1. The arguments are passed by value by default. If you need to // pass an argument by reference, wrap it inside ByRef(). For // example, // // InvokeArgument<1>(5, string("Hello"), ByRef(foo)) // // passes 5 and string("Hello") by value, and passes foo by // reference. // // 2. If the callable takes an argument by reference but ByRef() is // not used, it will receive the reference to a copy of the value, // instead of the original value. For example, when the 0-th // argument of the mock function takes a const string&, the action // // InvokeArgument<0>(string("Hello")) // // makes a copy of the temporary string("Hello") object and passes a // reference of the copy, instead of the original temporary object, // to the callable. This makes it easy for a user to define an // InvokeArgument action from temporary values and have it performed // later. namespace internal { namespace invoke_argument { // Appears in InvokeArgumentAdl's argument list to help avoid // accidental calls to user functions of the same name. struct AdlTag {}; // InvokeArgumentAdl - a helper for InvokeArgument. // The basic overloads are provided here for generic functors. // Overloads for other custom-callables are provided in the // internal/custom/callback-actions.h header. template R InvokeArgumentAdl(AdlTag, F f) { return f(); } template R InvokeArgumentAdl(AdlTag, F f, A1 a1) { return f(a1); } template R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2) { return f(a1, a2); } template R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3) { return f(a1, a2, a3); } template R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4) { return f(a1, a2, a3, a4); } template R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { return f(a1, a2, a3, a4, a5); } template R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { return f(a1, a2, a3, a4, a5, a6); } template R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { return f(a1, a2, a3, a4, a5, a6, a7); } template R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { return f(a1, a2, a3, a4, a5, a6, a7, a8); } template R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { return f(a1, a2, a3, a4, a5, a6, a7, a8, a9); } template R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { return f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); } } // namespace invoke_argument } // namespace internal ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_0_VALUE_PARAMS()) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args)); } ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_1_VALUE_PARAMS(p0)) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args), p0); } ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_2_VALUE_PARAMS(p0, p1)) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args), p0, p1); } ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_3_VALUE_PARAMS(p0, p1, p2)) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args), p0, p1, p2); } ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args), p0, p1, p2, p3); } ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args), p0, p1, p2, p3, p4); } ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args), p0, p1, p2, p3, p4, p5); } ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args), p0, p1, p2, p3, p4, p5, p6); } ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args), p0, p1, p2, p3, p4, p5, p6, p7); } ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args), p0, p1, p2, p3, p4, p5, p6, p7, p8); } ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } // Various overloads for ReturnNew(). // // The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new // instance of type T, constructed on the heap with constructor arguments // a1, a2, ..., and a_k. The caller assumes ownership of the returned value. ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_0_VALUE_PARAMS()) { return new T(); } ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_1_VALUE_PARAMS(p0)) { return new T(p0); } ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_2_VALUE_PARAMS(p0, p1)) { return new T(p0, p1); } ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_3_VALUE_PARAMS(p0, p1, p2)) { return new T(p0, p1, p2); } ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { return new T(p0, p1, p2, p3); } ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { return new T(p0, p1, p2, p3, p4); } ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { return new T(p0, p1, p2, p3, p4, p5); } ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { return new T(p0, p1, p2, p3, p4, p5, p6); } ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) { return new T(p0, p1, p2, p3, p4, p5, p6, p7); } ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) { return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8); } ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) { return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } #ifdef _MSC_VER # pragma warning(pop) #endif } // namespace testing // Include any custom callback actions added by the local installation. // We must include this header at the end to make sure it can use the // declarations from this file. #include "gmock/internal/custom/gmock-generated-actions.h" #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-generated-actions.h.pump ================================================ $$ -*- mode: c++; -*- $$ This is a Pump source file. Please use Pump to convert it to $$ gmock-generated-actions.h. $$ $var n = 10 $$ The maximum arity we support. $$}} This meta comment fixes auto-indentation in editors. // Copyright 2007, Google Inc. // 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 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. // Google Mock - a framework for writing C++ mock classes. // // This file implements some commonly used variadic actions. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ #include "gmock/gmock-actions.h" #include "gmock/internal/gmock-port.h" namespace testing { namespace internal { // InvokeHelper knows how to unpack an N-tuple and invoke an N-ary // function, method, or callback with the unpacked values, where F is // a function type that takes N arguments. template class InvokeHelper; $var max_callback_arity = 5 $range i 0..n $for i [[ $range j 1..i $var types = [[$for j [[, typename A$j]]]] $var as = [[$for j, [[A$j]]]] $var args = [[$if i==0 [[]] $else [[ args]]]] $var gets = [[$for j, [[get<$(j - 1)>(args)]]]] template class InvokeHelper > { public: template static R Invoke(Function function, const ::testing::tuple<$as>&$args) { return function($gets); } template static R InvokeMethod(Class* obj_ptr, MethodPtr method_ptr, const ::testing::tuple<$as>&$args) { return (obj_ptr->*method_ptr)($gets); } $if i <= max_callback_arity [[ template static R InvokeCallback(CallbackType* callback, const ::testing::tuple<$as>&$args) { return callback->Run($gets); } ]] $else [[ // There is no InvokeCallback() for $i-tuples ]] }; ]] // Implements the Invoke(callback) action. template class InvokeCallbackAction { public: // The c'tor takes ownership of the callback. explicit InvokeCallbackAction(CallbackType* callback) : callback_(callback) { callback->CheckIsRepeatable(); // Makes sure the callback is permanent. } // This type conversion operator template allows Invoke(callback) to // be used wherever the callback's type is compatible with that of // the mock function, i.e. if the mock function's arguments can be // implicitly converted to the callback's arguments and the // callback's result can be implicitly converted to the mock // function's result. template Result Perform(const ArgumentTuple& args) const { return InvokeHelper::InvokeCallback( callback_.get(), args); } private: const linked_ptr callback_; }; // An INTERNAL macro for extracting the type of a tuple field. It's // subject to change without notice - DO NOT USE IN USER CODE! #define GMOCK_FIELD_(Tuple, N) \ typename ::testing::tuple_element::type $range i 1..n // SelectArgs::type is the // type of an n-ary function whose i-th (1-based) argument type is the // k{i}-th (0-based) field of ArgumentTuple, which must be a tuple // type, and whose return type is Result. For example, // SelectArgs, 0, 3>::type // is int(bool, long). // // SelectArgs::Select(args) // returns the selected fields (k1, k2, ..., k_n) of args as a tuple. // For example, // SelectArgs, 2, 0>::Select( // ::testing::make_tuple(true, 'a', 2.5)) // returns tuple (2.5, true). // // The numbers in list k1, k2, ..., k_n must be >= 0, where n can be // in the range [0, $n]. Duplicates are allowed and they don't have // to be in an ascending or descending order. template class SelectArgs { public: typedef Result type($for i, [[GMOCK_FIELD_(ArgumentTuple, k$i)]]); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& args) { return SelectedArgs($for i, [[get(args)]]); } }; $for i [[ $range j 1..n $range j1 1..i-1 template class SelectArgs { public: typedef Result type($for j1, [[GMOCK_FIELD_(ArgumentTuple, k$j1)]]); typedef typename Function::ArgumentTuple SelectedArgs; static SelectedArgs Select(const ArgumentTuple& [[]] $if i == 1 [[/* args */]] $else [[args]]) { return SelectedArgs($for j1, [[get(args)]]); } }; ]] #undef GMOCK_FIELD_ $var ks = [[$for i, [[k$i]]]] // Implements the WithArgs action. template class WithArgsAction { public: explicit WithArgsAction(const InnerAction& action) : action_(action) {} template operator Action() const { return MakeAction(new Impl(action_)); } private: template class Impl : public ActionInterface { public: typedef typename Function::Result Result; typedef typename Function::ArgumentTuple ArgumentTuple; explicit Impl(const InnerAction& action) : action_(action) {} virtual Result Perform(const ArgumentTuple& args) { return action_.Perform(SelectArgs::Select(args)); } private: typedef typename SelectArgs::type InnerFunctionType; Action action_; }; const InnerAction action_; GTEST_DISALLOW_ASSIGN_(WithArgsAction); }; // A macro from the ACTION* family (defined later in this file) // defines an action that can be used in a mock function. Typically, // these actions only care about a subset of the arguments of the mock // function. For example, if such an action only uses the second // argument, it can be used in any mock function that takes >= 2 // arguments where the type of the second argument is compatible. // // Therefore, the action implementation must be prepared to take more // arguments than it needs. The ExcessiveArg type is used to // represent those excessive arguments. In order to keep the compiler // error messages tractable, we define it in the testing namespace // instead of testing::internal. However, this is an INTERNAL TYPE // and subject to change without notice, so a user MUST NOT USE THIS // TYPE DIRECTLY. struct ExcessiveArg {}; // A helper class needed for implementing the ACTION* macros. template class ActionHelper { public: $range i 0..n $for i [[ $var template = [[$if i==0 [[]] $else [[ $range j 0..i-1 template <$for j, [[typename A$j]]> ]]]] $range j 0..i-1 $var As = [[$for j, [[A$j]]]] $var as = [[$for j, [[get<$j>(args)]]]] $range k 1..n-i $var eas = [[$for k, [[ExcessiveArg()]]]] $var arg_list = [[$if (i==0) | (i==n) [[$as$eas]] $else [[$as, $eas]]]] $template static Result Perform(Impl* impl, const ::testing::tuple<$As>& args) { return impl->template gmock_PerformImpl<$As>(args, $arg_list); } ]] }; } // namespace internal // Various overloads for Invoke(). // WithArgs(an_action) creates an action that passes // the selected arguments of the mock function to an_action and // performs it. It serves as an adaptor between actions with // different argument lists. C++ doesn't support default arguments for // function templates, so we have to overload it. $range i 1..n $for i [[ $range j 1..i template <$for j [[int k$j, ]]typename InnerAction> inline internal::WithArgsAction WithArgs(const InnerAction& action) { return internal::WithArgsAction(action); } ]] // Creates an action that does actions a1, a2, ..., sequentially in // each invocation. $range i 2..n $for i [[ $range j 2..i $var types = [[$for j, [[typename Action$j]]]] $var Aas = [[$for j [[, Action$j a$j]]]] template $range k 1..i-1 inline $for k [[internal::DoBothAction]] DoAll(Action1 a1$Aas) { $if i==2 [[ return internal::DoBothAction(a1, a2); ]] $else [[ $range j2 2..i return DoAll(a1, DoAll($for j2, [[a$j2]])); ]] } ]] } // namespace testing // The ACTION* family of macros can be used in a namespace scope to // define custom actions easily. The syntax: // // ACTION(name) { statements; } // // will define an action with the given name that executes the // statements. The value returned by the statements will be used as // the return value of the action. Inside the statements, you can // refer to the K-th (0-based) argument of the mock function by // 'argK', and refer to its type by 'argK_type'. For example: // // ACTION(IncrementArg1) { // arg1_type temp = arg1; // return ++(*temp); // } // // allows you to write // // ...WillOnce(IncrementArg1()); // // You can also refer to the entire argument tuple and its type by // 'args' and 'args_type', and refer to the mock function type and its // return type by 'function_type' and 'return_type'. // // Note that you don't need to specify the types of the mock function // arguments. However rest assured that your code is still type-safe: // you'll get a compiler error if *arg1 doesn't support the ++ // operator, or if the type of ++(*arg1) isn't compatible with the // mock function's return type, for example. // // Sometimes you'll want to parameterize the action. For that you can use // another macro: // // ACTION_P(name, param_name) { statements; } // // For example: // // ACTION_P(Add, n) { return arg0 + n; } // // will allow you to write: // // ...WillOnce(Add(5)); // // Note that you don't need to provide the type of the parameter // either. If you need to reference the type of a parameter named // 'foo', you can write 'foo_type'. For example, in the body of // ACTION_P(Add, n) above, you can write 'n_type' to refer to the type // of 'n'. // // We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P$n to support // multi-parameter actions. // // For the purpose of typing, you can view // // ACTION_Pk(Foo, p1, ..., pk) { ... } // // as shorthand for // // template // FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } // // In particular, you can provide the template type arguments // explicitly when invoking Foo(), as in Foo(5, false); // although usually you can rely on the compiler to infer the types // for you automatically. You can assign the result of expression // Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. // // You can also overload actions with different numbers of parameters: // // ACTION_P(Plus, a) { ... } // ACTION_P2(Plus, a, b) { ... } // // While it's tempting to always use the ACTION* macros when defining // a new action, you should also consider implementing ActionInterface // or using MakePolymorphicAction() instead, especially if you need to // use the action a lot. While these approaches require more work, // they give you more control on the types of the mock function // arguments and the action parameters, which in general leads to // better compiler error messages that pay off in the long run. They // also allow overloading actions based on parameter types (as opposed // to just based on the number of parameters). // // CAVEAT: // // ACTION*() can only be used in a namespace scope. The reason is // that C++ doesn't yet allow function-local types to be used to // instantiate templates. The up-coming C++0x standard will fix this. // Once that's done, we'll consider supporting using ACTION*() inside // a function. // // MORE INFORMATION: // // To learn more about using these macros, please search for 'ACTION' // on https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md $range i 0..n $range k 0..n-1 // An internal macro needed for implementing ACTION*(). #define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\ const args_type& args GTEST_ATTRIBUTE_UNUSED_ $for k [[, \ arg$k[[]]_type arg$k GTEST_ATTRIBUTE_UNUSED_]] // Sometimes you want to give an action explicit template parameters // that cannot be inferred from its value parameters. ACTION() and // ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that // and can be viewed as an extension to ACTION() and ACTION_P*(). // // The syntax: // // ACTION_TEMPLATE(ActionName, // HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), // AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } // // defines an action template that takes m explicit template // parameters and n value parameters. name_i is the name of the i-th // template parameter, and kind_i specifies whether it's a typename, // an integral constant, or a template. p_i is the name of the i-th // value parameter. // // Example: // // // DuplicateArg(output) converts the k-th argument of the mock // // function to type T and copies it to *output. // ACTION_TEMPLATE(DuplicateArg, // HAS_2_TEMPLATE_PARAMS(int, k, typename, T), // AND_1_VALUE_PARAMS(output)) { // *output = T(::testing::get(args)); // } // ... // int n; // EXPECT_CALL(mock, Foo(_, _)) // .WillOnce(DuplicateArg<1, unsigned char>(&n)); // // To create an instance of an action template, write: // // ActionName(v1, ..., v_n) // // where the ts are the template arguments and the vs are the value // arguments. The value argument types are inferred by the compiler. // If you want to explicitly specify the value argument types, you can // provide additional template arguments: // // ActionName(v1, ..., v_n) // // where u_i is the desired type of v_i. // // ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the // number of value parameters, but not on the number of template // parameters. Without the restriction, the meaning of the following // is unclear: // // OverloadedAction(x); // // Are we using a single-template-parameter action where 'bool' refers // to the type of x, or are we using a two-template-parameter action // where the compiler is asked to infer the type of x? // // Implementation notes: // // GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and // GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for // implementing ACTION_TEMPLATE. The main trick we use is to create // new macro invocations when expanding a macro. For example, we have // // #define ACTION_TEMPLATE(name, template_params, value_params) // ... GMOCK_INTERNAL_DECL_##template_params ... // // which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...) // to expand to // // ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ... // // Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the // preprocessor will continue to expand it to // // ... typename T ... // // This technique conforms to the C++ standard and is portable. It // allows us to implement action templates using O(N) code, where N is // the maximum number of template/value parameters supported. Without // using it, we'd have to devote O(N^2) amount of code to implement all // combinations of m and n. // Declares the template parameters. $range j 1..n $for j [[ $range m 0..j-1 #define GMOCK_INTERNAL_DECL_HAS_$j[[]] _TEMPLATE_PARAMS($for m, [[kind$m, name$m]]) $for m, [[kind$m name$m]] ]] // Lists the template parameters. $for j [[ $range m 0..j-1 #define GMOCK_INTERNAL_LIST_HAS_$j[[]] _TEMPLATE_PARAMS($for m, [[kind$m, name$m]]) $for m, [[name$m]] ]] // Declares the types of value parameters. $for i [[ $range j 0..i-1 #define GMOCK_INTERNAL_DECL_TYPE_AND_$i[[]] _VALUE_PARAMS($for j, [[p$j]]) $for j [[, typename p$j##_type]] ]] // Initializes the value parameters. $for i [[ $range j 0..i-1 #define GMOCK_INTERNAL_INIT_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]])\ ($for j, [[p$j##_type gmock_p$j]])$if i>0 [[ : ]]$for j, [[p$j(::testing::internal::move(gmock_p$j))]] ]] // Declares the fields for storing the value parameters. $for i [[ $range j 0..i-1 #define GMOCK_INTERNAL_DEFN_AND_$i[[]] _VALUE_PARAMS($for j, [[p$j]]) $for j [[p$j##_type p$j; ]] ]] // Lists the value parameters. $for i [[ $range j 0..i-1 #define GMOCK_INTERNAL_LIST_AND_$i[[]] _VALUE_PARAMS($for j, [[p$j]]) $for j, [[p$j]] ]] // Lists the value parameter types. $for i [[ $range j 0..i-1 #define GMOCK_INTERNAL_LIST_TYPE_AND_$i[[]] _VALUE_PARAMS($for j, [[p$j]]) $for j [[, p$j##_type]] ]] // Declares the value parameters. $for i [[ $range j 0..i-1 #define GMOCK_INTERNAL_DECL_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]]) [[]] $for j, [[p$j##_type p$j]] ]] // The suffix of the class template implementing the action template. $for i [[ $range j 0..i-1 #define GMOCK_INTERNAL_COUNT_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]]) [[]] $if i==1 [[P]] $elif i>=2 [[P$i]] ]] // The name of the class template implementing the action template. #define GMOCK_ACTION_CLASS_(name, value_params)\ GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params) $range k 0..n-1 #define ACTION_TEMPLATE(name, template_params, value_params)\ template \ class GMOCK_ACTION_CLASS_(name, value_params) {\ public:\ explicit GMOCK_ACTION_CLASS_(name, value_params)\ GMOCK_INTERNAL_INIT_##value_params {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template <$for k, [[typename arg$k[[]]_type]]>\ return_type gmock_PerformImpl(const args_type& args[[]] $for k [[, arg$k[[]]_type arg$k]]) const;\ GMOCK_INTERNAL_DEFN_##value_params\ private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(\ new gmock_Impl(GMOCK_INTERNAL_LIST_##value_params));\ }\ GMOCK_INTERNAL_DEFN_##value_params\ private:\ GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\ };\ template \ inline GMOCK_ACTION_CLASS_(name, value_params)<\ GMOCK_INTERNAL_LIST_##template_params\ GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\ GMOCK_INTERNAL_DECL_##value_params) {\ return GMOCK_ACTION_CLASS_(name, value_params)<\ GMOCK_INTERNAL_LIST_##template_params\ GMOCK_INTERNAL_LIST_TYPE_##value_params>(\ GMOCK_INTERNAL_LIST_##value_params);\ }\ template \ template \ template \ typename ::testing::internal::Function::Result\ GMOCK_ACTION_CLASS_(name, value_params)<\ GMOCK_INTERNAL_LIST_##template_params\ GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::\ gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const $for i [[ $var template = [[$if i==0 [[]] $else [[ $range j 0..i-1 template <$for j, [[typename p$j##_type]]>\ ]]]] $var class_name = [[name##Action[[$if i==0 [[]] $elif i==1 [[P]] $else [[P$i]]]]]] $range j 0..i-1 $var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] $var param_types_and_names = [[$for j, [[p$j##_type p$j]]]] $var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(::testing::internal::forward(gmock_p$j))]]]]]] $var param_field_decls = [[$for j [[ p$j##_type p$j;\ ]]]] $var param_field_decls2 = [[$for j [[ p$j##_type p$j;\ ]]]] $var params = [[$for j, [[p$j]]]] $var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]] $var typename_arg_types = [[$for k, [[typename arg$k[[]]_type]]]] $var arg_types_and_names = [[$for k, [[arg$k[[]]_type arg$k]]]] $var macro_name = [[$if i==0 [[ACTION]] $elif i==1 [[ACTION_P]] $else [[ACTION_P$i]]]] #define $macro_name(name$for j [[, p$j]])\$template class $class_name {\ public:\ [[$if i==1 [[explicit ]]]]$class_name($ctor_param_list)$inits {}\ template \ class gmock_Impl : public ::testing::ActionInterface {\ public:\ typedef F function_type;\ typedef typename ::testing::internal::Function::Result return_type;\ typedef typename ::testing::internal::Function::ArgumentTuple\ args_type;\ [[$if i==1 [[explicit ]]]]gmock_Impl($ctor_param_list)$inits {}\ virtual return_type Perform(const args_type& args) {\ return ::testing::internal::ActionHelper::\ Perform(this, args);\ }\ template <$typename_arg_types>\ return_type gmock_PerformImpl(const args_type& args, [[]] $arg_types_and_names) const;\$param_field_decls private:\ GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ };\ template operator ::testing::Action() const {\ return ::testing::Action(new gmock_Impl($params));\ }\$param_field_decls2 private:\ GTEST_DISALLOW_ASSIGN_($class_name);\ };\$template inline $class_name$param_types name($param_types_and_names) {\ return $class_name$param_types($params);\ }\$template template \ template <$typename_arg_types>\ typename ::testing::internal::Function::Result\ $class_name$param_types::gmock_Impl::gmock_PerformImpl(\ GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const ]] $$ } // This meta comment fixes auto-indentation in Emacs. It won't $$ // show up in the generated code. namespace testing { // The ACTION*() macros trigger warning C4100 (unreferenced formal // parameter) in MSVC with -W4. Unfortunately they cannot be fixed in // the macro definition, as the warnings are generated when the macro // is expanded and macro expansion cannot contain #pragma. Therefore // we suppress them here. #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4100) #endif // Various overloads for InvokeArgument(). // // The InvokeArgument(a1, a2, ..., a_k) action invokes the N-th // (0-based) argument, which must be a k-ary callable, of the mock // function, with arguments a1, a2, ..., a_k. // // Notes: // // 1. The arguments are passed by value by default. If you need to // pass an argument by reference, wrap it inside ByRef(). For // example, // // InvokeArgument<1>(5, string("Hello"), ByRef(foo)) // // passes 5 and string("Hello") by value, and passes foo by // reference. // // 2. If the callable takes an argument by reference but ByRef() is // not used, it will receive the reference to a copy of the value, // instead of the original value. For example, when the 0-th // argument of the mock function takes a const string&, the action // // InvokeArgument<0>(string("Hello")) // // makes a copy of the temporary string("Hello") object and passes a // reference of the copy, instead of the original temporary object, // to the callable. This makes it easy for a user to define an // InvokeArgument action from temporary values and have it performed // later. namespace internal { namespace invoke_argument { // Appears in InvokeArgumentAdl's argument list to help avoid // accidental calls to user functions of the same name. struct AdlTag {}; // InvokeArgumentAdl - a helper for InvokeArgument. // The basic overloads are provided here for generic functors. // Overloads for other custom-callables are provided in the // internal/custom/callback-actions.h header. $range i 0..n $for i [[ $range j 1..i template R InvokeArgumentAdl(AdlTag, F f[[$for j [[, A$j a$j]]]]) { return f([[$for j, [[a$j]]]]); } ]] } // namespace invoke_argument } // namespace internal $range i 0..n $for i [[ $range j 0..i-1 ACTION_TEMPLATE(InvokeArgument, HAS_1_TEMPLATE_PARAMS(int, k), AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]])) { using internal::invoke_argument::InvokeArgumentAdl; return InvokeArgumentAdl( internal::invoke_argument::AdlTag(), ::testing::get(args)$for j [[, p$j]]); } ]] // Various overloads for ReturnNew(). // // The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new // instance of type T, constructed on the heap with constructor arguments // a1, a2, ..., and a_k. The caller assumes ownership of the returned value. $range i 0..n $for i [[ $range j 0..i-1 $var ps = [[$for j, [[p$j]]]] ACTION_TEMPLATE(ReturnNew, HAS_1_TEMPLATE_PARAMS(typename, T), AND_$i[[]]_VALUE_PARAMS($ps)) { return new T($ps); } ]] #ifdef _MSC_VER # pragma warning(pop) #endif } // namespace testing // Include any custom callback actions added by the local installation. // We must include this header at the end to make sure it can use the // declarations from this file. #include "gmock/internal/custom/gmock-generated-actions.h" #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-generated-function-mockers.h ================================================ // This file was GENERATED by command: // pump.py gmock-generated-function-mockers.h.pump // DO NOT EDIT BY HAND!!! // Copyright 2007, Google Inc. // 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 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. // Google Mock - a framework for writing C++ mock classes. // // This file implements function mockers of various arities. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ #include "gmock/gmock-spec-builders.h" #include "gmock/internal/gmock-internal-utils.h" #if GTEST_HAS_STD_FUNCTION_ # include #endif namespace testing { namespace internal { template class FunctionMockerBase; // Note: class FunctionMocker really belongs to the ::testing // namespace. However if we define it in ::testing, MSVC will // complain when classes in ::testing::internal declare it as a // friend class template. To workaround this compiler bug, we define // FunctionMocker in ::testing::internal and import it into ::testing. template class FunctionMocker; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With() { return MockSpec(this, ::testing::make_tuple()); } R Invoke() { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple()); } }; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(A1); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With(const Matcher& m1) { return MockSpec(this, ::testing::make_tuple(m1)); } R Invoke(A1 a1) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple(internal::forward(a1))); } }; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(A1, A2); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With(const Matcher& m1, const Matcher& m2) { return MockSpec(this, ::testing::make_tuple(m1, m2)); } R Invoke(A1 a1, A2 a2) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple(internal::forward(a1), internal::forward(a2))); } }; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(A1, A2, A3); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With(const Matcher& m1, const Matcher& m2, const Matcher& m3) { return MockSpec(this, ::testing::make_tuple(m1, m2, m3)); } R Invoke(A1 a1, A2 a2, A3 a3) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple(internal::forward(a1), internal::forward(a2), internal::forward(a3))); } }; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(A1, A2, A3, A4); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With(const Matcher& m1, const Matcher& m2, const Matcher& m3, const Matcher& m4) { return MockSpec(this, ::testing::make_tuple(m1, m2, m3, m4)); } R Invoke(A1 a1, A2 a2, A3 a3, A4 a4) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple(internal::forward(a1), internal::forward(a2), internal::forward(a3), internal::forward(a4))); } }; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(A1, A2, A3, A4, A5); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With(const Matcher& m1, const Matcher& m2, const Matcher& m3, const Matcher& m4, const Matcher& m5) { return MockSpec(this, ::testing::make_tuple(m1, m2, m3, m4, m5)); } R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple(internal::forward(a1), internal::forward(a2), internal::forward(a3), internal::forward(a4), internal::forward(a5))); } }; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(A1, A2, A3, A4, A5, A6); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With(const Matcher& m1, const Matcher& m2, const Matcher& m3, const Matcher& m4, const Matcher& m5, const Matcher& m6) { return MockSpec(this, ::testing::make_tuple(m1, m2, m3, m4, m5, m6)); } R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple(internal::forward(a1), internal::forward(a2), internal::forward(a3), internal::forward(a4), internal::forward(a5), internal::forward(a6))); } }; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(A1, A2, A3, A4, A5, A6, A7); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With(const Matcher& m1, const Matcher& m2, const Matcher& m3, const Matcher& m4, const Matcher& m5, const Matcher& m6, const Matcher& m7) { return MockSpec(this, ::testing::make_tuple(m1, m2, m3, m4, m5, m6, m7)); } R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple(internal::forward(a1), internal::forward(a2), internal::forward(a3), internal::forward(a4), internal::forward(a5), internal::forward(a6), internal::forward(a7))); } }; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(A1, A2, A3, A4, A5, A6, A7, A8); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With(const Matcher& m1, const Matcher& m2, const Matcher& m3, const Matcher& m4, const Matcher& m5, const Matcher& m6, const Matcher& m7, const Matcher& m8) { return MockSpec(this, ::testing::make_tuple(m1, m2, m3, m4, m5, m6, m7, m8)); } R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple(internal::forward(a1), internal::forward(a2), internal::forward(a3), internal::forward(a4), internal::forward(a5), internal::forward(a6), internal::forward(a7), internal::forward(a8))); } }; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With(const Matcher& m1, const Matcher& m2, const Matcher& m3, const Matcher& m4, const Matcher& m5, const Matcher& m6, const Matcher& m7, const Matcher& m8, const Matcher& m9) { return MockSpec(this, ::testing::make_tuple(m1, m2, m3, m4, m5, m6, m7, m8, m9)); } R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple(internal::forward(a1), internal::forward(a2), internal::forward(a3), internal::forward(a4), internal::forward(a5), internal::forward(a6), internal::forward(a7), internal::forward(a8), internal::forward(a9))); } }; template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With(const Matcher& m1, const Matcher& m2, const Matcher& m3, const Matcher& m4, const Matcher& m5, const Matcher& m6, const Matcher& m7, const Matcher& m8, const Matcher& m9, const Matcher& m10) { return MockSpec(this, ::testing::make_tuple(m1, m2, m3, m4, m5, m6, m7, m8, m9, m10)); } R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple(internal::forward(a1), internal::forward(a2), internal::forward(a3), internal::forward(a4), internal::forward(a5), internal::forward(a6), internal::forward(a7), internal::forward(a8), internal::forward(a9), internal::forward(a10))); } }; // Removes the given pointer; this is a helper for the expectation setter method // for parameterless matchers. // // We want to make sure that the user cannot set a parameterless expectation on // overloaded methods, including methods which are overloaded on const. Example: // // class MockClass { // MOCK_METHOD0(GetName, string&()); // MOCK_CONST_METHOD0(GetName, const string&()); // }; // // TEST() { // // This should be an error, as it's not clear which overload is expected. // EXPECT_CALL(mock, GetName).WillOnce(ReturnRef(value)); // } // // Here are the generated expectation-setter methods: // // class MockClass { // // Overload 1 // MockSpec gmock_GetName() { ... } // // Overload 2. Declared const so that the compiler will generate an // // error when trying to resolve between this and overload 4 in // // 'gmock_GetName(WithoutMatchers(), nullptr)'. // MockSpec gmock_GetName( // const WithoutMatchers&, const Function*) const { // // Removes const from this, calls overload 1 // return AdjustConstness_(this)->gmock_GetName(); // } // // // Overload 3 // const string& gmock_GetName() const { ... } // // Overload 4 // MockSpec gmock_GetName( // const WithoutMatchers&, const Function*) const { // // Does not remove const, calls overload 3 // return AdjustConstness_const(this)->gmock_GetName(); // } // } // template const MockType* AdjustConstness_const(const MockType* mock) { return mock; } // Removes const from and returns the given pointer; this is a helper for the // expectation setter method for parameterless matchers. template MockType* AdjustConstness_(const MockType* mock) { return const_cast(mock); } } // namespace internal // The style guide prohibits "using" statements in a namespace scope // inside a header file. However, the FunctionMocker class template // is meant to be defined in the ::testing namespace. The following // line is just a trick for working around a bug in MSVC 8.0, which // cannot handle it if we define FunctionMocker in ::testing. using internal::FunctionMocker; // GMOCK_RESULT_(tn, F) expands to the result type of function type F. // We define this as a variadic macro in case F contains unprotected // commas (the same reason that we use variadic macros in other places // in this file). // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_RESULT_(tn, ...) \ tn ::testing::internal::Function<__VA_ARGS__>::Result // The type of argument N of the given function type. // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_ARG_(tn, N, ...) \ tn ::testing::internal::Function<__VA_ARGS__>::Argument##N // The matcher type for argument N of the given function type. // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_MATCHER_(tn, N, ...) \ const ::testing::Matcher& // The variable for mocking the given method. // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_MOCKER_(arity, constness, Method) \ GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD0_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method() constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 0), \ this_method_does_not_take_0_arguments); \ GMOCK_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(0, constness, Method).Invoke(); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method() constness { \ GMOCK_MOCKER_(0, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(0, constness, Method).With(); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(0, constness, \ Method) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD1_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) \ ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1) constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 1), \ this_method_does_not_take_1_argument); \ GMOCK_MOCKER_(1, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(1, constness, Method) \ .Invoke(::testing::internal::forward( \ gmock_a1)); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1) constness { \ GMOCK_MOCKER_(1, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(1, constness, Method).With(gmock_a1); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(::testing::A()); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(1, constness, \ Method) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD2_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) \ ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2) constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 2), \ this_method_does_not_take_2_arguments); \ GMOCK_MOCKER_(2, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(2, constness, Method) \ .Invoke(::testing::internal::forward( \ gmock_a1), \ ::testing::internal::forward( \ gmock_a2)); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2) constness { \ GMOCK_MOCKER_(2, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(2, constness, Method).With(gmock_a1, gmock_a2); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(::testing::A(), \ ::testing::A()); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(2, constness, \ Method) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD3_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) \ ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3) constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 3), \ this_method_does_not_take_3_arguments); \ GMOCK_MOCKER_(3, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(3, constness, Method) \ .Invoke(::testing::internal::forward( \ gmock_a1), \ ::testing::internal::forward( \ gmock_a2), \ ::testing::internal::forward( \ gmock_a3)); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3) constness { \ GMOCK_MOCKER_(3, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(3, constness, Method) \ .With(gmock_a1, gmock_a2, gmock_a3); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(::testing::A(), \ ::testing::A(), \ ::testing::A()); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(3, constness, \ Method) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD4_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) \ ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4) constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 4), \ this_method_does_not_take_4_arguments); \ GMOCK_MOCKER_(4, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(4, constness, Method) \ .Invoke(::testing::internal::forward( \ gmock_a1), \ ::testing::internal::forward( \ gmock_a2), \ ::testing::internal::forward( \ gmock_a3), \ ::testing::internal::forward( \ gmock_a4)); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4) constness { \ GMOCK_MOCKER_(4, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(4, constness, Method) \ .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A()); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(4, constness, \ Method) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD5_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) \ ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5) constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 5), \ this_method_does_not_take_5_arguments); \ GMOCK_MOCKER_(5, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(5, constness, Method) \ .Invoke(::testing::internal::forward( \ gmock_a1), \ ::testing::internal::forward( \ gmock_a2), \ ::testing::internal::forward( \ gmock_a3), \ ::testing::internal::forward( \ gmock_a4), \ ::testing::internal::forward( \ gmock_a5)); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5) constness { \ GMOCK_MOCKER_(5, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(5, constness, Method) \ .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A()); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(5, constness, \ Method) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD6_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) \ ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6) constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 6), \ this_method_does_not_take_6_arguments); \ GMOCK_MOCKER_(6, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(6, constness, Method) \ .Invoke(::testing::internal::forward( \ gmock_a1), \ ::testing::internal::forward( \ gmock_a2), \ ::testing::internal::forward( \ gmock_a3), \ ::testing::internal::forward( \ gmock_a4), \ ::testing::internal::forward( \ gmock_a5), \ ::testing::internal::forward( \ gmock_a6)); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6) constness { \ GMOCK_MOCKER_(6, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(6, constness, Method) \ .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A()); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(6, constness, \ Method) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD7_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) \ ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7) constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 7), \ this_method_does_not_take_7_arguments); \ GMOCK_MOCKER_(7, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(7, constness, Method) \ .Invoke(::testing::internal::forward( \ gmock_a1), \ ::testing::internal::forward( \ gmock_a2), \ ::testing::internal::forward( \ gmock_a3), \ ::testing::internal::forward( \ gmock_a4), \ ::testing::internal::forward( \ gmock_a5), \ ::testing::internal::forward( \ gmock_a6), \ ::testing::internal::forward( \ gmock_a7)); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7) constness { \ GMOCK_MOCKER_(7, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(7, constness, Method) \ .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, \ gmock_a7); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A()); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(7, constness, \ Method) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD8_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) \ ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \ GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8) constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 8), \ this_method_does_not_take_8_arguments); \ GMOCK_MOCKER_(8, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(8, constness, Method) \ .Invoke(::testing::internal::forward( \ gmock_a1), \ ::testing::internal::forward( \ gmock_a2), \ ::testing::internal::forward( \ gmock_a3), \ ::testing::internal::forward( \ gmock_a4), \ ::testing::internal::forward( \ gmock_a5), \ ::testing::internal::forward( \ gmock_a6), \ ::testing::internal::forward( \ gmock_a7), \ ::testing::internal::forward( \ gmock_a8)); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8) constness { \ GMOCK_MOCKER_(8, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(8, constness, Method) \ .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, \ gmock_a7, gmock_a8); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A()); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(8, constness, \ Method) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD9_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) \ ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \ GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8, \ GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9) constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 9), \ this_method_does_not_take_9_arguments); \ GMOCK_MOCKER_(9, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(9, constness, Method) \ .Invoke(::testing::internal::forward( \ gmock_a1), \ ::testing::internal::forward( \ gmock_a2), \ ::testing::internal::forward( \ gmock_a3), \ ::testing::internal::forward( \ gmock_a4), \ ::testing::internal::forward( \ gmock_a5), \ ::testing::internal::forward( \ gmock_a6), \ ::testing::internal::forward( \ gmock_a7), \ ::testing::internal::forward( \ gmock_a8), \ ::testing::internal::forward( \ gmock_a9)); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \ GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9) constness { \ GMOCK_MOCKER_(9, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(9, constness, Method) \ .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, \ gmock_a7, gmock_a8, gmock_a9); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A()); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(9, constness, \ Method) // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD10_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) \ ct Method(GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_ARG_(tn, 5, __VA_ARGS__) gmock_a5, \ GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \ GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, \ GMOCK_ARG_(tn, 8, __VA_ARGS__) gmock_a8, \ GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9, \ GMOCK_ARG_(tn, 10, __VA_ARGS__) gmock_a10) constness { \ GTEST_COMPILE_ASSERT_( \ (::testing::tuple_size::ArgumentTuple>::value == 10), \ this_method_does_not_take_10_arguments); \ GMOCK_MOCKER_(10, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_(10, constness, Method) \ .Invoke(::testing::internal::forward( \ gmock_a1), \ ::testing::internal::forward( \ gmock_a2), \ ::testing::internal::forward( \ gmock_a3), \ ::testing::internal::forward( \ gmock_a4), \ ::testing::internal::forward( \ gmock_a5), \ ::testing::internal::forward( \ gmock_a6), \ ::testing::internal::forward( \ gmock_a7), \ ::testing::internal::forward( \ gmock_a8), \ ::testing::internal::forward( \ gmock_a9), \ ::testing::internal::forward( \ gmock_a10)); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \ GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \ GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \ GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \ GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \ GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \ GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \ GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9, \ GMOCK_MATCHER_(tn, 10, __VA_ARGS__) gmock_a10) constness { \ GMOCK_MOCKER_(10, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_(10, constness, Method) \ .With(gmock_a1, gmock_a2, gmock_a3, gmock_a4, gmock_a5, gmock_a6, \ gmock_a7, gmock_a8, gmock_a9, gmock_a10); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>*) const { \ return ::testing::internal::AdjustConstness_##constness(this) \ ->gmock_##Method(::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A(), \ ::testing::A()); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(10, constness, \ Method) #define MOCK_METHOD0(m, ...) GMOCK_METHOD0_(, , , m, __VA_ARGS__) #define MOCK_METHOD1(m, ...) GMOCK_METHOD1_(, , , m, __VA_ARGS__) #define MOCK_METHOD2(m, ...) GMOCK_METHOD2_(, , , m, __VA_ARGS__) #define MOCK_METHOD3(m, ...) GMOCK_METHOD3_(, , , m, __VA_ARGS__) #define MOCK_METHOD4(m, ...) GMOCK_METHOD4_(, , , m, __VA_ARGS__) #define MOCK_METHOD5(m, ...) GMOCK_METHOD5_(, , , m, __VA_ARGS__) #define MOCK_METHOD6(m, ...) GMOCK_METHOD6_(, , , m, __VA_ARGS__) #define MOCK_METHOD7(m, ...) GMOCK_METHOD7_(, , , m, __VA_ARGS__) #define MOCK_METHOD8(m, ...) GMOCK_METHOD8_(, , , m, __VA_ARGS__) #define MOCK_METHOD9(m, ...) GMOCK_METHOD9_(, , , m, __VA_ARGS__) #define MOCK_METHOD10(m, ...) GMOCK_METHOD10_(, , , m, __VA_ARGS__) #define MOCK_CONST_METHOD0(m, ...) GMOCK_METHOD0_(, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD1(m, ...) GMOCK_METHOD1_(, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD2(m, ...) GMOCK_METHOD2_(, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD3(m, ...) GMOCK_METHOD3_(, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD4(m, ...) GMOCK_METHOD4_(, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD5(m, ...) GMOCK_METHOD5_(, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD6(m, ...) GMOCK_METHOD6_(, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD7(m, ...) GMOCK_METHOD7_(, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD8(m, ...) GMOCK_METHOD8_(, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD9(m, ...) GMOCK_METHOD9_(, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD10(m, ...) GMOCK_METHOD10_(, const, , m, __VA_ARGS__) #define MOCK_METHOD0_T(m, ...) GMOCK_METHOD0_(typename, , , m, __VA_ARGS__) #define MOCK_METHOD1_T(m, ...) GMOCK_METHOD1_(typename, , , m, __VA_ARGS__) #define MOCK_METHOD2_T(m, ...) GMOCK_METHOD2_(typename, , , m, __VA_ARGS__) #define MOCK_METHOD3_T(m, ...) GMOCK_METHOD3_(typename, , , m, __VA_ARGS__) #define MOCK_METHOD4_T(m, ...) GMOCK_METHOD4_(typename, , , m, __VA_ARGS__) #define MOCK_METHOD5_T(m, ...) GMOCK_METHOD5_(typename, , , m, __VA_ARGS__) #define MOCK_METHOD6_T(m, ...) GMOCK_METHOD6_(typename, , , m, __VA_ARGS__) #define MOCK_METHOD7_T(m, ...) GMOCK_METHOD7_(typename, , , m, __VA_ARGS__) #define MOCK_METHOD8_T(m, ...) GMOCK_METHOD8_(typename, , , m, __VA_ARGS__) #define MOCK_METHOD9_T(m, ...) GMOCK_METHOD9_(typename, , , m, __VA_ARGS__) #define MOCK_METHOD10_T(m, ...) GMOCK_METHOD10_(typename, , , m, __VA_ARGS__) #define MOCK_CONST_METHOD0_T(m, ...) \ GMOCK_METHOD0_(typename, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD1_T(m, ...) \ GMOCK_METHOD1_(typename, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD2_T(m, ...) \ GMOCK_METHOD2_(typename, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD3_T(m, ...) \ GMOCK_METHOD3_(typename, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD4_T(m, ...) \ GMOCK_METHOD4_(typename, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD5_T(m, ...) \ GMOCK_METHOD5_(typename, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD6_T(m, ...) \ GMOCK_METHOD6_(typename, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD7_T(m, ...) \ GMOCK_METHOD7_(typename, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD8_T(m, ...) \ GMOCK_METHOD8_(typename, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD9_T(m, ...) \ GMOCK_METHOD9_(typename, const, , m, __VA_ARGS__) #define MOCK_CONST_METHOD10_T(m, ...) \ GMOCK_METHOD10_(typename, const, , m, __VA_ARGS__) #define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD0_(, , ct, m, __VA_ARGS__) #define MOCK_METHOD1_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD1_(, , ct, m, __VA_ARGS__) #define MOCK_METHOD2_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD2_(, , ct, m, __VA_ARGS__) #define MOCK_METHOD3_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD3_(, , ct, m, __VA_ARGS__) #define MOCK_METHOD4_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD4_(, , ct, m, __VA_ARGS__) #define MOCK_METHOD5_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD5_(, , ct, m, __VA_ARGS__) #define MOCK_METHOD6_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD6_(, , ct, m, __VA_ARGS__) #define MOCK_METHOD7_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD7_(, , ct, m, __VA_ARGS__) #define MOCK_METHOD8_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD8_(, , ct, m, __VA_ARGS__) #define MOCK_METHOD9_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD9_(, , ct, m, __VA_ARGS__) #define MOCK_METHOD10_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD10_(, , ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD0_(, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD1_(, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD2_(, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD3_(, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD4_(, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD5_(, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD6_(, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD7_(, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD8_(, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD9_(, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD10_(, const, ct, m, __VA_ARGS__) #define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD0_(typename, , ct, m, __VA_ARGS__) #define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD1_(typename, , ct, m, __VA_ARGS__) #define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD2_(typename, , ct, m, __VA_ARGS__) #define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD3_(typename, , ct, m, __VA_ARGS__) #define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD4_(typename, , ct, m, __VA_ARGS__) #define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD5_(typename, , ct, m, __VA_ARGS__) #define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD6_(typename, , ct, m, __VA_ARGS__) #define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD7_(typename, , ct, m, __VA_ARGS__) #define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD8_(typename, , ct, m, __VA_ARGS__) #define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD9_(typename, , ct, m, __VA_ARGS__) #define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD10_(typename, , ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD0_(typename, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD1_(typename, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD2_(typename, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD3_(typename, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD4_(typename, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD5_(typename, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD6_(typename, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD7_(typename, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD8_(typename, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD9_(typename, const, ct, m, __VA_ARGS__) #define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD10_(typename, const, ct, m, __VA_ARGS__) // A MockFunction class has one mock method whose type is F. It is // useful when you just want your test code to emit some messages and // have Google Mock verify the right messages are sent (and perhaps at // the right times). For example, if you are exercising code: // // Foo(1); // Foo(2); // Foo(3); // // and want to verify that Foo(1) and Foo(3) both invoke // mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write: // // TEST(FooTest, InvokesBarCorrectly) { // MyMock mock; // MockFunction check; // { // InSequence s; // // EXPECT_CALL(mock, Bar("a")); // EXPECT_CALL(check, Call("1")); // EXPECT_CALL(check, Call("2")); // EXPECT_CALL(mock, Bar("a")); // } // Foo(1); // check.Call("1"); // Foo(2); // check.Call("2"); // Foo(3); // } // // The expectation spec says that the first Bar("a") must happen // before check point "1", the second Bar("a") must happen after check // point "2", and nothing should happen between the two check // points. The explicit check points make it easy to tell which // Bar("a") is called by which call to Foo(). // // MockFunction can also be used to exercise code that accepts // std::function callbacks. To do so, use AsStdFunction() method // to create std::function proxy forwarding to original object's Call. // Example: // // TEST(FooTest, RunsCallbackWithBarArgument) { // MockFunction callback; // EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1)); // Foo(callback.AsStdFunction()); // } template class MockFunction; template class MockFunction { public: MockFunction() {} MOCK_METHOD0_T(Call, R()); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this]() -> R { return this->Call(); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; template class MockFunction { public: MockFunction() {} MOCK_METHOD1_T(Call, R(A0)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this](A0 a0) -> R { return this->Call(::std::move(a0)); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; template class MockFunction { public: MockFunction() {} MOCK_METHOD2_T(Call, R(A0, A1)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this](A0 a0, A1 a1) -> R { return this->Call(::std::move(a0), ::std::move(a1)); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; template class MockFunction { public: MockFunction() {} MOCK_METHOD3_T(Call, R(A0, A1, A2)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this](A0 a0, A1 a1, A2 a2) -> R { return this->Call(::std::move(a0), ::std::move(a1), ::std::move(a2)); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; template class MockFunction { public: MockFunction() {} MOCK_METHOD4_T(Call, R(A0, A1, A2, A3)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this](A0 a0, A1 a1, A2 a2, A3 a3) -> R { return this->Call(::std::move(a0), ::std::move(a1), ::std::move(a2), ::std::move(a3)); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; template class MockFunction { public: MockFunction() {} MOCK_METHOD5_T(Call, R(A0, A1, A2, A3, A4)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) -> R { return this->Call(::std::move(a0), ::std::move(a1), ::std::move(a2), ::std::move(a3), ::std::move(a4)); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; template class MockFunction { public: MockFunction() {} MOCK_METHOD6_T(Call, R(A0, A1, A2, A3, A4, A5)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) -> R { return this->Call(::std::move(a0), ::std::move(a1), ::std::move(a2), ::std::move(a3), ::std::move(a4), ::std::move(a5)); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; template class MockFunction { public: MockFunction() {} MOCK_METHOD7_T(Call, R(A0, A1, A2, A3, A4, A5, A6)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) -> R { return this->Call(::std::move(a0), ::std::move(a1), ::std::move(a2), ::std::move(a3), ::std::move(a4), ::std::move(a5), ::std::move(a6)); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; template class MockFunction { public: MockFunction() {} MOCK_METHOD8_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) -> R { return this->Call(::std::move(a0), ::std::move(a1), ::std::move(a2), ::std::move(a3), ::std::move(a4), ::std::move(a5), ::std::move(a6), ::std::move(a7)); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; template class MockFunction { public: MockFunction() {} MOCK_METHOD9_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) -> R { return this->Call(::std::move(a0), ::std::move(a1), ::std::move(a2), ::std::move(a3), ::std::move(a4), ::std::move(a5), ::std::move(a6), ::std::move(a7), ::std::move(a8)); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; template class MockFunction { public: MockFunction() {} MOCK_METHOD10_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this](A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) -> R { return this->Call(::std::move(a0), ::std::move(a1), ::std::move(a2), ::std::move(a3), ::std::move(a4), ::std::move(a5), ::std::move(a6), ::std::move(a7), ::std::move(a8), ::std::move(a9)); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; } // namespace testing #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-generated-function-mockers.h.pump ================================================ $$ -*- mode: c++; -*- $$ This is a Pump source file. Please use Pump to convert $$ it to gmock-generated-function-mockers.h. $$ $var n = 10 $$ The maximum arity we support. // Copyright 2007, Google Inc. // 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 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. // Google Mock - a framework for writing C++ mock classes. // // This file implements function mockers of various arities. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ #include "gmock/gmock-spec-builders.h" #include "gmock/internal/gmock-internal-utils.h" #if GTEST_HAS_STD_FUNCTION_ # include #endif namespace testing { namespace internal { template class FunctionMockerBase; // Note: class FunctionMocker really belongs to the ::testing // namespace. However if we define it in ::testing, MSVC will // complain when classes in ::testing::internal declare it as a // friend class template. To workaround this compiler bug, we define // FunctionMocker in ::testing::internal and import it into ::testing. template class FunctionMocker; $range i 0..n $for i [[ $range j 1..i $var typename_As = [[$for j [[, typename A$j]]]] $var As = [[$for j, [[A$j]]]] $var as = [[$for j, [[internal::forward(a$j)]]]] $var Aas = [[$for j, [[A$j a$j]]]] $var ms = [[$for j, [[m$j]]]] $var matchers = [[$for j, [[const Matcher& m$j]]]] template class FunctionMocker : public internal::FunctionMockerBase { public: typedef R F($As); typedef typename internal::Function::ArgumentTuple ArgumentTuple; MockSpec With($matchers) { return MockSpec(this, ::testing::make_tuple($ms)); } R Invoke($Aas) { // Even though gcc and MSVC don't enforce it, 'this->' is required // by the C++ standard [14.6.4] here, as the base class type is // dependent on the template argument (and thus shouldn't be // looked into when resolving InvokeWith). return this->InvokeWith(ArgumentTuple($as)); } }; ]] // Removes the given pointer; this is a helper for the expectation setter method // for parameterless matchers. // // We want to make sure that the user cannot set a parameterless expectation on // overloaded methods, including methods which are overloaded on const. Example: // // class MockClass { // MOCK_METHOD0(GetName, string&()); // MOCK_CONST_METHOD0(GetName, const string&()); // }; // // TEST() { // // This should be an error, as it's not clear which overload is expected. // EXPECT_CALL(mock, GetName).WillOnce(ReturnRef(value)); // } // // Here are the generated expectation-setter methods: // // class MockClass { // // Overload 1 // MockSpec gmock_GetName() { ... } // // Overload 2. Declared const so that the compiler will generate an // // error when trying to resolve between this and overload 4 in // // 'gmock_GetName(WithoutMatchers(), nullptr)'. // MockSpec gmock_GetName( // const WithoutMatchers&, const Function*) const { // // Removes const from this, calls overload 1 // return AdjustConstness_(this)->gmock_GetName(); // } // // // Overload 3 // const string& gmock_GetName() const { ... } // // Overload 4 // MockSpec gmock_GetName( // const WithoutMatchers&, const Function*) const { // // Does not remove const, calls overload 3 // return AdjustConstness_const(this)->gmock_GetName(); // } // } // template const MockType* AdjustConstness_const(const MockType* mock) { return mock; } // Removes const from and returns the given pointer; this is a helper for the // expectation setter method for parameterless matchers. template MockType* AdjustConstness_(const MockType* mock) { return const_cast(mock); } } // namespace internal // The style guide prohibits "using" statements in a namespace scope // inside a header file. However, the FunctionMocker class template // is meant to be defined in the ::testing namespace. The following // line is just a trick for working around a bug in MSVC 8.0, which // cannot handle it if we define FunctionMocker in ::testing. using internal::FunctionMocker; // GMOCK_RESULT_(tn, F) expands to the result type of function type F. // We define this as a variadic macro in case F contains unprotected // commas (the same reason that we use variadic macros in other places // in this file). // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_RESULT_(tn, ...) \ tn ::testing::internal::Function<__VA_ARGS__>::Result // The type of argument N of the given function type. // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_ARG_(tn, N, ...) \ tn ::testing::internal::Function<__VA_ARGS__>::Argument##N // The matcher type for argument N of the given function type. // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_MATCHER_(tn, N, ...) \ const ::testing::Matcher& // The variable for mocking the given method. // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_MOCKER_(arity, constness, Method) \ GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) $for i [[ $range j 1..i $var arg_as = [[$for j, [[GMOCK_ARG_(tn, $j, __VA_ARGS__) gmock_a$j]]]] $var as = [[$for j, \ [[::testing::internal::forward(gmock_a$j)]]]] $var matcher_arg_as = [[$for j, \ [[GMOCK_MATCHER_(tn, $j, __VA_ARGS__) gmock_a$j]]]] $var matcher_as = [[$for j, [[gmock_a$j]]]] $var anything_matchers = [[$for j, \ [[::testing::A()]]]] // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! #define GMOCK_METHOD$i[[]]_(tn, constness, ct, Method, ...) \ GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \ $arg_as) constness { \ GTEST_COMPILE_ASSERT_((::testing::tuple_size< \ tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value == $i), \ this_method_does_not_take_$i[[]]_argument[[$if i != 1 [[s]]]]); \ GMOCK_MOCKER_($i, constness, Method).SetOwnerAndName(this, #Method); \ return GMOCK_MOCKER_($i, constness, Method).Invoke($as); \ } \ ::testing::MockSpec<__VA_ARGS__> \ gmock_##Method($matcher_arg_as) constness { \ GMOCK_MOCKER_($i, constness, Method).RegisterOwner(this); \ return GMOCK_MOCKER_($i, constness, Method).With($matcher_as); \ } \ ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \ const ::testing::internal::WithoutMatchers&, \ constness ::testing::internal::Function<__VA_ARGS__>* ) const { \ return ::testing::internal::AdjustConstness_##constness(this)-> \ gmock_##Method($anything_matchers); \ } \ mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_($i, constness, Method) ]] $for i [[ #define MOCK_METHOD$i(m, ...) GMOCK_METHOD$i[[]]_(, , , m, __VA_ARGS__) ]] $for i [[ #define MOCK_CONST_METHOD$i(m, ...) GMOCK_METHOD$i[[]]_(, const, , m, __VA_ARGS__) ]] $for i [[ #define MOCK_METHOD$i[[]]_T(m, ...) GMOCK_METHOD$i[[]]_(typename, , , m, __VA_ARGS__) ]] $for i [[ #define MOCK_CONST_METHOD$i[[]]_T(m, ...) \ GMOCK_METHOD$i[[]]_(typename, const, , m, __VA_ARGS__) ]] $for i [[ #define MOCK_METHOD$i[[]]_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD$i[[]]_(, , ct, m, __VA_ARGS__) ]] $for i [[ #define MOCK_CONST_METHOD$i[[]]_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD$i[[]]_(, const, ct, m, __VA_ARGS__) ]] $for i [[ #define MOCK_METHOD$i[[]]_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD$i[[]]_(typename, , ct, m, __VA_ARGS__) ]] $for i [[ #define MOCK_CONST_METHOD$i[[]]_T_WITH_CALLTYPE(ct, m, ...) \ GMOCK_METHOD$i[[]]_(typename, const, ct, m, __VA_ARGS__) ]] // A MockFunction class has one mock method whose type is F. It is // useful when you just want your test code to emit some messages and // have Google Mock verify the right messages are sent (and perhaps at // the right times). For example, if you are exercising code: // // Foo(1); // Foo(2); // Foo(3); // // and want to verify that Foo(1) and Foo(3) both invoke // mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write: // // TEST(FooTest, InvokesBarCorrectly) { // MyMock mock; // MockFunction check; // { // InSequence s; // // EXPECT_CALL(mock, Bar("a")); // EXPECT_CALL(check, Call("1")); // EXPECT_CALL(check, Call("2")); // EXPECT_CALL(mock, Bar("a")); // } // Foo(1); // check.Call("1"); // Foo(2); // check.Call("2"); // Foo(3); // } // // The expectation spec says that the first Bar("a") must happen // before check point "1", the second Bar("a") must happen after check // point "2", and nothing should happen between the two check // points. The explicit check points make it easy to tell which // Bar("a") is called by which call to Foo(). // // MockFunction can also be used to exercise code that accepts // std::function callbacks. To do so, use AsStdFunction() method // to create std::function proxy forwarding to original object's Call. // Example: // // TEST(FooTest, RunsCallbackWithBarArgument) { // MockFunction callback; // EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1)); // Foo(callback.AsStdFunction()); // } template class MockFunction; $for i [[ $range j 0..i-1 $var ArgTypes = [[$for j, [[A$j]]]] $var ArgValues = [[$for j, [[::std::move(a$j)]]]] $var ArgDecls = [[$for j, [[A$j a$j]]]] template class MockFunction { public: MockFunction() {} MOCK_METHOD$i[[]]_T(Call, R($ArgTypes)); #if GTEST_HAS_STD_FUNCTION_ ::std::function AsStdFunction() { return [this]($ArgDecls) -> R { return this->Call($ArgValues); }; } #endif // GTEST_HAS_STD_FUNCTION_ private: GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); }; ]] } // namespace testing #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-generated-matchers.h ================================================ // This file was GENERATED by command: // pump.py gmock-generated-matchers.h.pump // DO NOT EDIT BY HAND!!! // Copyright 2008, Google Inc. // 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 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. // Google Mock - a framework for writing C++ mock classes. // // This file implements some commonly used variadic matchers. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ #include #include #include #include #include "gmock/gmock-matchers.h" namespace testing { namespace internal { // The type of the i-th (0-based) field of Tuple. #define GMOCK_FIELD_TYPE_(Tuple, i) \ typename ::testing::tuple_element::type // TupleFields is for selecting fields from a // tuple of type Tuple. It has two members: // // type: a tuple type whose i-th field is the ki-th field of Tuple. // GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple. // // For example, in class TupleFields, 2, 0>, we have: // // type is tuple, and // GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true). template class TupleFields; // This generic version is used when there are 10 selectors. template class TupleFields { public: typedef ::testing::tuple type; static type GetSelectedFields(const Tuple& t) { return type(get(t), get(t), get(t), get(t), get(t), get(t), get(t), get(t), get(t), get(t)); } }; // The following specialization is used for 0 ~ 9 selectors. template class TupleFields { public: typedef ::testing::tuple<> type; static type GetSelectedFields(const Tuple& /* t */) { return type(); } }; template class TupleFields { public: typedef ::testing::tuple type; static type GetSelectedFields(const Tuple& t) { return type(get(t)); } }; template class TupleFields { public: typedef ::testing::tuple type; static type GetSelectedFields(const Tuple& t) { return type(get(t), get(t)); } }; template class TupleFields { public: typedef ::testing::tuple type; static type GetSelectedFields(const Tuple& t) { return type(get(t), get(t), get(t)); } }; template class TupleFields { public: typedef ::testing::tuple type; static type GetSelectedFields(const Tuple& t) { return type(get(t), get(t), get(t), get(t)); } }; template class TupleFields { public: typedef ::testing::tuple type; static type GetSelectedFields(const Tuple& t) { return type(get(t), get(t), get(t), get(t), get(t)); } }; template class TupleFields { public: typedef ::testing::tuple type; static type GetSelectedFields(const Tuple& t) { return type(get(t), get(t), get(t), get(t), get(t), get(t)); } }; template class TupleFields { public: typedef ::testing::tuple type; static type GetSelectedFields(const Tuple& t) { return type(get(t), get(t), get(t), get(t), get(t), get(t), get(t)); } }; template class TupleFields { public: typedef ::testing::tuple type; static type GetSelectedFields(const Tuple& t) { return type(get(t), get(t), get(t), get(t), get(t), get(t), get(t), get(t)); } }; template class TupleFields { public: typedef ::testing::tuple type; static type GetSelectedFields(const Tuple& t) { return type(get(t), get(t), get(t), get(t), get(t), get(t), get(t), get(t), get(t)); } }; #undef GMOCK_FIELD_TYPE_ // Implements the Args() matcher. template class ArgsMatcherImpl : public MatcherInterface { public: // ArgsTuple may have top-level const or reference modifiers. typedef GTEST_REMOVE_REFERENCE_AND_CONST_(ArgsTuple) RawArgsTuple; typedef typename internal::TupleFields::type SelectedArgs; typedef Matcher MonomorphicInnerMatcher; template explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher) : inner_matcher_(SafeMatcherCast(inner_matcher)) {} virtual bool MatchAndExplain(ArgsTuple args, MatchResultListener* listener) const { const SelectedArgs& selected_args = GetSelectedArgs(args); if (!listener->IsInterested()) return inner_matcher_.Matches(selected_args); PrintIndices(listener->stream()); *listener << "are " << PrintToString(selected_args); StringMatchResultListener inner_listener; const bool match = inner_matcher_.MatchAndExplain(selected_args, &inner_listener); PrintIfNotEmpty(inner_listener.str(), listener->stream()); return match; } virtual void DescribeTo(::std::ostream* os) const { *os << "are a tuple "; PrintIndices(os); inner_matcher_.DescribeTo(os); } virtual void DescribeNegationTo(::std::ostream* os) const { *os << "are a tuple "; PrintIndices(os); inner_matcher_.DescribeNegationTo(os); } private: static SelectedArgs GetSelectedArgs(ArgsTuple args) { return TupleFields::GetSelectedFields(args); } // Prints the indices of the selected fields. static void PrintIndices(::std::ostream* os) { *os << "whose fields ("; const int indices[10] = { k0, k1, k2, k3, k4, k5, k6, k7, k8, k9 }; for (int i = 0; i < 10; i++) { if (indices[i] < 0) break; if (i >= 1) *os << ", "; *os << "#" << indices[i]; } *os << ") "; } const MonomorphicInnerMatcher inner_matcher_; GTEST_DISALLOW_ASSIGN_(ArgsMatcherImpl); }; template class ArgsMatcher { public: explicit ArgsMatcher(const InnerMatcher& inner_matcher) : inner_matcher_(inner_matcher) {} template operator Matcher() const { return MakeMatcher(new ArgsMatcherImpl(inner_matcher_)); } private: const InnerMatcher inner_matcher_; GTEST_DISALLOW_ASSIGN_(ArgsMatcher); }; // A set of metafunctions for computing the result type of AllOf. // AllOf(m1, ..., mN) returns // AllOfResultN::type. // Although AllOf isn't defined for one argument, AllOfResult1 is defined // to simplify the implementation. template struct AllOfResult1 { typedef M1 type; }; template struct AllOfResult2 { typedef BothOfMatcher< typename AllOfResult1::type, typename AllOfResult1::type > type; }; template struct AllOfResult3 { typedef BothOfMatcher< typename AllOfResult1::type, typename AllOfResult2::type > type; }; template struct AllOfResult4 { typedef BothOfMatcher< typename AllOfResult2::type, typename AllOfResult2::type > type; }; template struct AllOfResult5 { typedef BothOfMatcher< typename AllOfResult2::type, typename AllOfResult3::type > type; }; template struct AllOfResult6 { typedef BothOfMatcher< typename AllOfResult3::type, typename AllOfResult3::type > type; }; template struct AllOfResult7 { typedef BothOfMatcher< typename AllOfResult3::type, typename AllOfResult4::type > type; }; template struct AllOfResult8 { typedef BothOfMatcher< typename AllOfResult4::type, typename AllOfResult4::type > type; }; template struct AllOfResult9 { typedef BothOfMatcher< typename AllOfResult4::type, typename AllOfResult5::type > type; }; template struct AllOfResult10 { typedef BothOfMatcher< typename AllOfResult5::type, typename AllOfResult5::type > type; }; // A set of metafunctions for computing the result type of AnyOf. // AnyOf(m1, ..., mN) returns // AnyOfResultN::type. // Although AnyOf isn't defined for one argument, AnyOfResult1 is defined // to simplify the implementation. template struct AnyOfResult1 { typedef M1 type; }; template struct AnyOfResult2 { typedef EitherOfMatcher< typename AnyOfResult1::type, typename AnyOfResult1::type > type; }; template struct AnyOfResult3 { typedef EitherOfMatcher< typename AnyOfResult1::type, typename AnyOfResult2::type > type; }; template struct AnyOfResult4 { typedef EitherOfMatcher< typename AnyOfResult2::type, typename AnyOfResult2::type > type; }; template struct AnyOfResult5 { typedef EitherOfMatcher< typename AnyOfResult2::type, typename AnyOfResult3::type > type; }; template struct AnyOfResult6 { typedef EitherOfMatcher< typename AnyOfResult3::type, typename AnyOfResult3::type > type; }; template struct AnyOfResult7 { typedef EitherOfMatcher< typename AnyOfResult3::type, typename AnyOfResult4::type > type; }; template struct AnyOfResult8 { typedef EitherOfMatcher< typename AnyOfResult4::type, typename AnyOfResult4::type > type; }; template struct AnyOfResult9 { typedef EitherOfMatcher< typename AnyOfResult4::type, typename AnyOfResult5::type > type; }; template struct AnyOfResult10 { typedef EitherOfMatcher< typename AnyOfResult5::type, typename AnyOfResult5::type > type; }; } // namespace internal // Args(a_matcher) matches a tuple if the selected // fields of it matches a_matcher. C++ doesn't support default // arguments for function templates, so we have to overload it. template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } template inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } // ElementsAre(e_1, e_2, ... e_n) matches an STL-style container with // n elements, where the i-th element in the container must // match the i-th argument in the list. Each argument of // ElementsAre() can be either a value or a matcher. We support up to // 10 arguments. // // The use of DecayArray in the implementation allows ElementsAre() // to accept string literals, whose type is const char[N], but we // want to treat them as const char*. // // NOTE: Since ElementsAre() cares about the order of the elements, it // must not be used with containers whose elements's order is // undefined (e.g. hash_map). inline internal::ElementsAreMatcher< ::testing::tuple<> > ElementsAre() { typedef ::testing::tuple<> Args; return internal::ElementsAreMatcher(Args()); } template inline internal::ElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type> > ElementsAre(const T1& e1) { typedef ::testing::tuple< typename internal::DecayArray::type> Args; return internal::ElementsAreMatcher(Args(e1)); } template inline internal::ElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type> > ElementsAre(const T1& e1, const T2& e2) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::ElementsAreMatcher(Args(e1, e2)); } template inline internal::ElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > ElementsAre(const T1& e1, const T2& e2, const T3& e3) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::ElementsAreMatcher(Args(e1, e2, e3)); } template inline internal::ElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::ElementsAreMatcher(Args(e1, e2, e3, e4)); } template inline internal::ElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5)); } template inline internal::ElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5, const T6& e6) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6)); } template inline internal::ElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5, const T6& e6, const T7& e7) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7)); } template inline internal::ElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5, const T6& e6, const T7& e7, const T8& e8) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7, e8)); } template inline internal::ElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7, e8, e9)); } template inline internal::ElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9, const T10& e10) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::ElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10)); } // UnorderedElementsAre(e_1, e_2, ..., e_n) is an ElementsAre extension // that matches n elements in any order. We support up to n=10 arguments. // // If you have >10 elements, consider UnorderedElementsAreArray() or // UnorderedPointwise() instead. inline internal::UnorderedElementsAreMatcher< ::testing::tuple<> > UnorderedElementsAre() { typedef ::testing::tuple<> Args; return internal::UnorderedElementsAreMatcher(Args()); } template inline internal::UnorderedElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type> > UnorderedElementsAre(const T1& e1) { typedef ::testing::tuple< typename internal::DecayArray::type> Args; return internal::UnorderedElementsAreMatcher(Args(e1)); } template inline internal::UnorderedElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type> > UnorderedElementsAre(const T1& e1, const T2& e2) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::UnorderedElementsAreMatcher(Args(e1, e2)); } template inline internal::UnorderedElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3)); } template inline internal::UnorderedElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4)); } template inline internal::UnorderedElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5)); } template inline internal::UnorderedElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5, const T6& e6) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6)); } template inline internal::UnorderedElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5, const T6& e6, const T7& e7) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7)); } template inline internal::UnorderedElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5, const T6& e6, const T7& e7, const T8& e8) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7, e8)); } template inline internal::UnorderedElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7, e8, e9)); } template inline internal::UnorderedElementsAreMatcher< ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> > UnorderedElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9, const T10& e10) { typedef ::testing::tuple< typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type, typename internal::DecayArray::type> Args; return internal::UnorderedElementsAreMatcher(Args(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10)); } // AllOf(m1, m2, ..., mk) matches any value that matches all of the given // sub-matchers. AllOf is called fully qualified to prevent ADL from firing. template inline typename internal::AllOfResult2::type AllOf(M1 m1, M2 m2) { return typename internal::AllOfResult2::type( m1, m2); } template inline typename internal::AllOfResult3::type AllOf(M1 m1, M2 m2, M3 m3) { return typename internal::AllOfResult3::type( m1, ::testing::AllOf(m2, m3)); } template inline typename internal::AllOfResult4::type AllOf(M1 m1, M2 m2, M3 m3, M4 m4) { return typename internal::AllOfResult4::type( ::testing::AllOf(m1, m2), ::testing::AllOf(m3, m4)); } template inline typename internal::AllOfResult5::type AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5) { return typename internal::AllOfResult5::type( ::testing::AllOf(m1, m2), ::testing::AllOf(m3, m4, m5)); } template inline typename internal::AllOfResult6::type AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6) { return typename internal::AllOfResult6::type( ::testing::AllOf(m1, m2, m3), ::testing::AllOf(m4, m5, m6)); } template inline typename internal::AllOfResult7::type AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7) { return typename internal::AllOfResult7::type( ::testing::AllOf(m1, m2, m3), ::testing::AllOf(m4, m5, m6, m7)); } template inline typename internal::AllOfResult8::type AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8) { return typename internal::AllOfResult8::type( ::testing::AllOf(m1, m2, m3, m4), ::testing::AllOf(m5, m6, m7, m8)); } template inline typename internal::AllOfResult9::type AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9) { return typename internal::AllOfResult9::type( ::testing::AllOf(m1, m2, m3, m4), ::testing::AllOf(m5, m6, m7, m8, m9)); } template inline typename internal::AllOfResult10::type AllOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) { return typename internal::AllOfResult10::type( ::testing::AllOf(m1, m2, m3, m4, m5), ::testing::AllOf(m6, m7, m8, m9, m10)); } // AnyOf(m1, m2, ..., mk) matches any value that matches any of the given // sub-matchers. AnyOf is called fully qualified to prevent ADL from firing. template inline typename internal::AnyOfResult2::type AnyOf(M1 m1, M2 m2) { return typename internal::AnyOfResult2::type( m1, m2); } template inline typename internal::AnyOfResult3::type AnyOf(M1 m1, M2 m2, M3 m3) { return typename internal::AnyOfResult3::type( m1, ::testing::AnyOf(m2, m3)); } template inline typename internal::AnyOfResult4::type AnyOf(M1 m1, M2 m2, M3 m3, M4 m4) { return typename internal::AnyOfResult4::type( ::testing::AnyOf(m1, m2), ::testing::AnyOf(m3, m4)); } template inline typename internal::AnyOfResult5::type AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5) { return typename internal::AnyOfResult5::type( ::testing::AnyOf(m1, m2), ::testing::AnyOf(m3, m4, m5)); } template inline typename internal::AnyOfResult6::type AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6) { return typename internal::AnyOfResult6::type( ::testing::AnyOf(m1, m2, m3), ::testing::AnyOf(m4, m5, m6)); } template inline typename internal::AnyOfResult7::type AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7) { return typename internal::AnyOfResult7::type( ::testing::AnyOf(m1, m2, m3), ::testing::AnyOf(m4, m5, m6, m7)); } template inline typename internal::AnyOfResult8::type AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8) { return typename internal::AnyOfResult8::type( ::testing::AnyOf(m1, m2, m3, m4), ::testing::AnyOf(m5, m6, m7, m8)); } template inline typename internal::AnyOfResult9::type AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9) { return typename internal::AnyOfResult9::type( ::testing::AnyOf(m1, m2, m3, m4), ::testing::AnyOf(m5, m6, m7, m8, m9)); } template inline typename internal::AnyOfResult10::type AnyOf(M1 m1, M2 m2, M3 m3, M4 m4, M5 m5, M6 m6, M7 m7, M8 m8, M9 m9, M10 m10) { return typename internal::AnyOfResult10::type( ::testing::AnyOf(m1, m2, m3, m4, m5), ::testing::AnyOf(m6, m7, m8, m9, m10)); } } // namespace testing // The MATCHER* family of macros can be used in a namespace scope to // define custom matchers easily. // // Basic Usage // =========== // // The syntax // // MATCHER(name, description_string) { statements; } // // defines a matcher with the given name that executes the statements, // which must return a bool to indicate if the match succeeds. Inside // the statements, you can refer to the value being matched by 'arg', // and refer to its type by 'arg_type'. // // The description string documents what the matcher does, and is used // to generate the failure message when the match fails. Since a // MATCHER() is usually defined in a header file shared by multiple // C++ source files, we require the description to be a C-string // literal to avoid possible side effects. It can be empty, in which // case we'll use the sequence of words in the matcher name as the // description. // // For example: // // MATCHER(IsEven, "") { return (arg % 2) == 0; } // // allows you to write // // // Expects mock_foo.Bar(n) to be called where n is even. // EXPECT_CALL(mock_foo, Bar(IsEven())); // // or, // // // Verifies that the value of some_expression is even. // EXPECT_THAT(some_expression, IsEven()); // // If the above assertion fails, it will print something like: // // Value of: some_expression // Expected: is even // Actual: 7 // // where the description "is even" is automatically calculated from the // matcher name IsEven. // // Argument Type // ============= // // Note that the type of the value being matched (arg_type) is // determined by the context in which you use the matcher and is // supplied to you by the compiler, so you don't need to worry about // declaring it (nor can you). This allows the matcher to be // polymorphic. For example, IsEven() can be used to match any type // where the value of "(arg % 2) == 0" can be implicitly converted to // a bool. In the "Bar(IsEven())" example above, if method Bar() // takes an int, 'arg_type' will be int; if it takes an unsigned long, // 'arg_type' will be unsigned long; and so on. // // Parameterizing Matchers // ======================= // // Sometimes you'll want to parameterize the matcher. For that you // can use another macro: // // MATCHER_P(name, param_name, description_string) { statements; } // // For example: // // MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } // // will allow you to write: // // EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); // // which may lead to this message (assuming n is 10): // // Value of: Blah("a") // Expected: has absolute value 10 // Actual: -9 // // Note that both the matcher description and its parameter are // printed, making the message human-friendly. // // In the matcher definition body, you can write 'foo_type' to // reference the type of a parameter named 'foo'. For example, in the // body of MATCHER_P(HasAbsoluteValue, value) above, you can write // 'value_type' to refer to the type of 'value'. // // We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P10 to // support multi-parameter matchers. // // Describing Parameterized Matchers // ================================= // // The last argument to MATCHER*() is a string-typed expression. The // expression can reference all of the matcher's parameters and a // special bool-typed variable named 'negation'. When 'negation' is // false, the expression should evaluate to the matcher's description; // otherwise it should evaluate to the description of the negation of // the matcher. For example, // // using testing::PrintToString; // // MATCHER_P2(InClosedRange, low, hi, // std::string(negation ? "is not" : "is") + " in range [" + // PrintToString(low) + ", " + PrintToString(hi) + "]") { // return low <= arg && arg <= hi; // } // ... // EXPECT_THAT(3, InClosedRange(4, 6)); // EXPECT_THAT(3, Not(InClosedRange(2, 4))); // // would generate two failures that contain the text: // // Expected: is in range [4, 6] // ... // Expected: is not in range [2, 4] // // If you specify "" as the description, the failure message will // contain the sequence of words in the matcher name followed by the // parameter values printed as a tuple. For example, // // MATCHER_P2(InClosedRange, low, hi, "") { ... } // ... // EXPECT_THAT(3, InClosedRange(4, 6)); // EXPECT_THAT(3, Not(InClosedRange(2, 4))); // // would generate two failures that contain the text: // // Expected: in closed range (4, 6) // ... // Expected: not (in closed range (2, 4)) // // Types of Matcher Parameters // =========================== // // For the purpose of typing, you can view // // MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } // // as shorthand for // // template // FooMatcherPk // Foo(p1_type p1, ..., pk_type pk) { ... } // // When you write Foo(v1, ..., vk), the compiler infers the types of // the parameters v1, ..., and vk for you. If you are not happy with // the result of the type inference, you can specify the types by // explicitly instantiating the template, as in Foo(5, // false). As said earlier, you don't get to (or need to) specify // 'arg_type' as that's determined by the context in which the matcher // is used. You can assign the result of expression Foo(p1, ..., pk) // to a variable of type FooMatcherPk. This // can be useful when composing matchers. // // While you can instantiate a matcher template with reference types, // passing the parameters by pointer usually makes your code more // readable. If, however, you still want to pass a parameter by // reference, be aware that in the failure message generated by the // matcher you will see the value of the referenced object but not its // address. // // Explaining Match Results // ======================== // // Sometimes the matcher description alone isn't enough to explain why // the match has failed or succeeded. For example, when expecting a // long string, it can be very helpful to also print the diff between // the expected string and the actual one. To achieve that, you can // optionally stream additional information to a special variable // named result_listener, whose type is a pointer to class // MatchResultListener: // // MATCHER_P(EqualsLongString, str, "") { // if (arg == str) return true; // // *result_listener << "the difference: " /// << DiffStrings(str, arg); // return false; // } // // Overloading Matchers // ==================== // // You can overload matchers with different numbers of parameters: // // MATCHER_P(Blah, a, description_string1) { ... } // MATCHER_P2(Blah, a, b, description_string2) { ... } // // Caveats // ======= // // When defining a new matcher, you should also consider implementing // MatcherInterface or using MakePolymorphicMatcher(). These // approaches require more work than the MATCHER* macros, but also // give you more control on the types of the value being matched and // the matcher parameters, which may leads to better compiler error // messages when the matcher is used wrong. They also allow // overloading matchers based on parameter types (as opposed to just // based on the number of parameters). // // MATCHER*() can only be used in a namespace scope. The reason is // that C++ doesn't yet allow function-local types to be used to // instantiate templates. The up-coming C++0x standard will fix this. // Once that's done, we'll consider supporting using MATCHER*() inside // a function. // // More Information // ================ // // To learn more about using these macros, please search for 'MATCHER' // on // https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md #define MATCHER(name, description)\ class name##Matcher {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ gmock_Impl()\ {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple<>()));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl());\ }\ name##Matcher() {\ }\ private:\ };\ inline name##Matcher name() {\ return name##Matcher();\ }\ template \ bool name##Matcher::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #define MATCHER_P(name, p0, description)\ template \ class name##MatcherP {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ explicit gmock_Impl(p0##_type gmock_p0)\ : p0(::testing::internal::move(gmock_p0)) {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ p0##_type const p0;\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple(p0)));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl(p0));\ }\ explicit name##MatcherP(p0##_type gmock_p0) : \ p0(::testing::internal::move(gmock_p0)) {\ }\ p0##_type const p0;\ private:\ };\ template \ inline name##MatcherP name(p0##_type p0) {\ return name##MatcherP(p0);\ }\ template \ template \ bool name##MatcherP::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #define MATCHER_P2(name, p0, p1, description)\ template \ class name##MatcherP2 {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1)\ : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)) {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ p0##_type const p0;\ p1##_type const p1;\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple(p0, p1)));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl(p0, p1));\ }\ name##MatcherP2(p0##_type gmock_p0, \ p1##_type gmock_p1) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)) {\ }\ p0##_type const p0;\ p1##_type const p1;\ private:\ };\ template \ inline name##MatcherP2 name(p0##_type p0, \ p1##_type p1) {\ return name##MatcherP2(p0, p1);\ }\ template \ template \ bool name##MatcherP2::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #define MATCHER_P3(name, p0, p1, p2, description)\ template \ class name##MatcherP3 {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2)\ : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)) {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple(p0, p1, \ p2)));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl(p0, p1, p2));\ }\ name##MatcherP3(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)) {\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ private:\ };\ template \ inline name##MatcherP3 name(p0##_type p0, \ p1##_type p1, p2##_type p2) {\ return name##MatcherP3(p0, p1, p2);\ }\ template \ template \ bool name##MatcherP3::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #define MATCHER_P4(name, p0, p1, p2, p3, description)\ template \ class name##MatcherP4 {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3)\ : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)) {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple(p0, p1, p2, p3)));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl(p0, p1, p2, p3));\ }\ name##MatcherP4(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, \ p3##_type gmock_p3) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)) {\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ private:\ };\ template \ inline name##MatcherP4 name(p0##_type p0, p1##_type p1, p2##_type p2, \ p3##_type p3) {\ return name##MatcherP4(p0, \ p1, p2, p3);\ }\ template \ template \ bool name##MatcherP4::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #define MATCHER_P5(name, p0, p1, p2, p3, p4, description)\ template \ class name##MatcherP5 {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4)\ : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)) {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple(p0, p1, p2, p3, p4)));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl(p0, p1, p2, p3, p4));\ }\ name##MatcherP5(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, \ p4##_type gmock_p4) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)) {\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ private:\ };\ template \ inline name##MatcherP5 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ p4##_type p4) {\ return name##MatcherP5(p0, p1, p2, p3, p4);\ }\ template \ template \ bool name##MatcherP5::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #define MATCHER_P6(name, p0, p1, p2, p3, p4, p5, description)\ template \ class name##MatcherP6 {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5)\ : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)) {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ p5##_type const p5;\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple(p0, p1, p2, p3, p4, p5)));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl(p0, p1, p2, p3, p4, p5));\ }\ name##MatcherP6(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)) {\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ p5##_type const p5;\ private:\ };\ template \ inline name##MatcherP6 name(p0##_type p0, p1##_type p1, p2##_type p2, \ p3##_type p3, p4##_type p4, p5##_type p5) {\ return name##MatcherP6(p0, p1, p2, p3, p4, p5);\ }\ template \ template \ bool name##MatcherP6::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #define MATCHER_P7(name, p0, p1, p2, p3, p4, p5, p6, description)\ template \ class name##MatcherP7 {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6)\ : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)) {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ p5##_type const p5;\ p6##_type const p6;\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple(p0, p1, p2, p3, p4, p5, \ p6)));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl(p0, p1, p2, p3, p4, p5, p6));\ }\ name##MatcherP7(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5, \ p6##_type gmock_p6) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)) {\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ p5##_type const p5;\ p6##_type const p6;\ private:\ };\ template \ inline name##MatcherP7 name(p0##_type p0, p1##_type p1, \ p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ p6##_type p6) {\ return name##MatcherP7(p0, p1, p2, p3, p4, p5, p6);\ }\ template \ template \ bool name##MatcherP7::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #define MATCHER_P8(name, p0, p1, p2, p3, p4, p5, p6, p7, description)\ template \ class name##MatcherP8 {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6, p7##_type gmock_p7)\ : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)), \ p7(::testing::internal::move(gmock_p7)) {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ p5##_type const p5;\ p6##_type const p6;\ p7##_type const p7;\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple(p0, p1, p2, \ p3, p4, p5, p6, p7)));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7));\ }\ name##MatcherP8(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5, p6##_type gmock_p6, \ p7##_type gmock_p7) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)), \ p7(::testing::internal::move(gmock_p7)) {\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ p5##_type const p5;\ p6##_type const p6;\ p7##_type const p7;\ private:\ };\ template \ inline name##MatcherP8 name(p0##_type p0, \ p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ p6##_type p6, p7##_type p7) {\ return name##MatcherP8(p0, p1, p2, p3, p4, p5, \ p6, p7);\ }\ template \ template \ bool name##MatcherP8::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #define MATCHER_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, description)\ template \ class name##MatcherP9 {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8)\ : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)), \ p7(::testing::internal::move(gmock_p7)), \ p8(::testing::internal::move(gmock_p8)) {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ p5##_type const p5;\ p6##_type const p6;\ p7##_type const p7;\ p8##_type const p8;\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple(p0, p1, p2, p3, p4, p5, p6, p7, p8)));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7, p8));\ }\ name##MatcherP9(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ p8##_type gmock_p8) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)), \ p7(::testing::internal::move(gmock_p7)), \ p8(::testing::internal::move(gmock_p8)) {\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ p5##_type const p5;\ p6##_type const p6;\ p7##_type const p7;\ p8##_type const p8;\ private:\ };\ template \ inline name##MatcherP9 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \ p8##_type p8) {\ return name##MatcherP9(p0, p1, p2, \ p3, p4, p5, p6, p7, p8);\ }\ template \ template \ bool name##MatcherP9::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #define MATCHER_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, description)\ template \ class name##MatcherP10 {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ p9##_type gmock_p9)\ : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)), \ p7(::testing::internal::move(gmock_p7)), \ p8(::testing::internal::move(gmock_p8)), \ p9(::testing::internal::move(gmock_p9)) {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ p5##_type const p5;\ p6##_type const p6;\ p7##_type const p7;\ p8##_type const p8;\ p9##_type const p9;\ private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9));\ }\ name##MatcherP10(p0##_type gmock_p0, p1##_type gmock_p1, \ p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ p8##_type gmock_p8, \ p9##_type gmock_p9) : p0(::testing::internal::move(gmock_p0)), \ p1(::testing::internal::move(gmock_p1)), \ p2(::testing::internal::move(gmock_p2)), \ p3(::testing::internal::move(gmock_p3)), \ p4(::testing::internal::move(gmock_p4)), \ p5(::testing::internal::move(gmock_p5)), \ p6(::testing::internal::move(gmock_p6)), \ p7(::testing::internal::move(gmock_p7)), \ p8(::testing::internal::move(gmock_p8)), \ p9(::testing::internal::move(gmock_p9)) {\ }\ p0##_type const p0;\ p1##_type const p1;\ p2##_type const p2;\ p3##_type const p3;\ p4##_type const p4;\ p5##_type const p5;\ p6##_type const p6;\ p7##_type const p7;\ p8##_type const p8;\ p9##_type const p9;\ private:\ };\ template \ inline name##MatcherP10 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ p9##_type p9) {\ return name##MatcherP10(p0, \ p1, p2, p3, p4, p5, p6, p7, p8, p9);\ }\ template \ template \ bool name##MatcherP10::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-generated-matchers.h.pump ================================================ $$ -*- mode: c++; -*- $$ This is a Pump source file. Please use Pump to convert $$ it to gmock-generated-matchers.h. $$ $var n = 10 $$ The maximum arity we support. $$ }} This line fixes auto-indentation of the following code in Emacs. // Copyright 2008, Google Inc. // 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 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. // Google Mock - a framework for writing C++ mock classes. // // This file implements some commonly used variadic matchers. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ #include #include #include #include #include "gmock/gmock-matchers.h" namespace testing { namespace internal { $range i 0..n-1 // The type of the i-th (0-based) field of Tuple. #define GMOCK_FIELD_TYPE_(Tuple, i) \ typename ::testing::tuple_element::type // TupleFields is for selecting fields from a // tuple of type Tuple. It has two members: // // type: a tuple type whose i-th field is the ki-th field of Tuple. // GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple. // // For example, in class TupleFields, 2, 0>, we have: // // type is tuple, and // GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true). template class TupleFields; // This generic version is used when there are $n selectors. template class TupleFields { public: typedef ::testing::tuple<$for i, [[GMOCK_FIELD_TYPE_(Tuple, k$i)]]> type; static type GetSelectedFields(const Tuple& t) { return type($for i, [[get(t)]]); } }; // The following specialization is used for 0 ~ $(n-1) selectors. $for i [[ $$ }}} $range j 0..i-1 $range k 0..n-1 template class TupleFields { public: typedef ::testing::tuple<$for j, [[GMOCK_FIELD_TYPE_(Tuple, k$j)]]> type; static type GetSelectedFields(const Tuple& $if i==0 [[/* t */]] $else [[t]]) { return type($for j, [[get(t)]]); } }; ]] #undef GMOCK_FIELD_TYPE_ // Implements the Args() matcher. $var ks = [[$for i, [[k$i]]]] template class ArgsMatcherImpl : public MatcherInterface { public: // ArgsTuple may have top-level const or reference modifiers. typedef GTEST_REMOVE_REFERENCE_AND_CONST_(ArgsTuple) RawArgsTuple; typedef typename internal::TupleFields::type SelectedArgs; typedef Matcher MonomorphicInnerMatcher; template explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher) : inner_matcher_(SafeMatcherCast(inner_matcher)) {} virtual bool MatchAndExplain(ArgsTuple args, MatchResultListener* listener) const { const SelectedArgs& selected_args = GetSelectedArgs(args); if (!listener->IsInterested()) return inner_matcher_.Matches(selected_args); PrintIndices(listener->stream()); *listener << "are " << PrintToString(selected_args); StringMatchResultListener inner_listener; const bool match = inner_matcher_.MatchAndExplain(selected_args, &inner_listener); PrintIfNotEmpty(inner_listener.str(), listener->stream()); return match; } virtual void DescribeTo(::std::ostream* os) const { *os << "are a tuple "; PrintIndices(os); inner_matcher_.DescribeTo(os); } virtual void DescribeNegationTo(::std::ostream* os) const { *os << "are a tuple "; PrintIndices(os); inner_matcher_.DescribeNegationTo(os); } private: static SelectedArgs GetSelectedArgs(ArgsTuple args) { return TupleFields::GetSelectedFields(args); } // Prints the indices of the selected fields. static void PrintIndices(::std::ostream* os) { *os << "whose fields ("; const int indices[$n] = { $ks }; for (int i = 0; i < $n; i++) { if (indices[i] < 0) break; if (i >= 1) *os << ", "; *os << "#" << indices[i]; } *os << ") "; } const MonomorphicInnerMatcher inner_matcher_; GTEST_DISALLOW_ASSIGN_(ArgsMatcherImpl); }; template class ArgsMatcher { public: explicit ArgsMatcher(const InnerMatcher& inner_matcher) : inner_matcher_(inner_matcher) {} template operator Matcher() const { return MakeMatcher(new ArgsMatcherImpl(inner_matcher_)); } private: const InnerMatcher inner_matcher_; GTEST_DISALLOW_ASSIGN_(ArgsMatcher); }; // A set of metafunctions for computing the result type of AllOf. // AllOf(m1, ..., mN) returns // AllOfResultN::type. // Although AllOf isn't defined for one argument, AllOfResult1 is defined // to simplify the implementation. template struct AllOfResult1 { typedef M1 type; }; $range i 1..n $range i 2..n $for i [[ $range j 2..i $var m = i/2 $range k 1..m $range t m+1..i template struct AllOfResult$i { typedef BothOfMatcher< typename AllOfResult$m<$for k, [[M$k]]>::type, typename AllOfResult$(i-m)<$for t, [[M$t]]>::type > type; }; ]] // A set of metafunctions for computing the result type of AnyOf. // AnyOf(m1, ..., mN) returns // AnyOfResultN::type. // Although AnyOf isn't defined for one argument, AnyOfResult1 is defined // to simplify the implementation. template struct AnyOfResult1 { typedef M1 type; }; $range i 1..n $range i 2..n $for i [[ $range j 2..i $var m = i/2 $range k 1..m $range t m+1..i template struct AnyOfResult$i { typedef EitherOfMatcher< typename AnyOfResult$m<$for k, [[M$k]]>::type, typename AnyOfResult$(i-m)<$for t, [[M$t]]>::type > type; }; ]] } // namespace internal // Args(a_matcher) matches a tuple if the selected // fields of it matches a_matcher. C++ doesn't support default // arguments for function templates, so we have to overload it. $range i 0..n $for i [[ $range j 1..i template <$for j [[int k$j, ]]typename InnerMatcher> inline internal::ArgsMatcher Args(const InnerMatcher& matcher) { return internal::ArgsMatcher(matcher); } ]] // ElementsAre(e_1, e_2, ... e_n) matches an STL-style container with // n elements, where the i-th element in the container must // match the i-th argument in the list. Each argument of // ElementsAre() can be either a value or a matcher. We support up to // $n arguments. // // The use of DecayArray in the implementation allows ElementsAre() // to accept string literals, whose type is const char[N], but we // want to treat them as const char*. // // NOTE: Since ElementsAre() cares about the order of the elements, it // must not be used with containers whose elements's order is // undefined (e.g. hash_map). $range i 0..n $for i [[ $range j 1..i $if i>0 [[ template <$for j, [[typename T$j]]> ]] inline internal::ElementsAreMatcher< ::testing::tuple< $for j, [[ typename internal::DecayArray::type]]> > ElementsAre($for j, [[const T$j& e$j]]) { typedef ::testing::tuple< $for j, [[ typename internal::DecayArray::type]]> Args; return internal::ElementsAreMatcher(Args($for j, [[e$j]])); } ]] // UnorderedElementsAre(e_1, e_2, ..., e_n) is an ElementsAre extension // that matches n elements in any order. We support up to n=$n arguments. // // If you have >$n elements, consider UnorderedElementsAreArray() or // UnorderedPointwise() instead. $range i 0..n $for i [[ $range j 1..i $if i>0 [[ template <$for j, [[typename T$j]]> ]] inline internal::UnorderedElementsAreMatcher< ::testing::tuple< $for j, [[ typename internal::DecayArray::type]]> > UnorderedElementsAre($for j, [[const T$j& e$j]]) { typedef ::testing::tuple< $for j, [[ typename internal::DecayArray::type]]> Args; return internal::UnorderedElementsAreMatcher(Args($for j, [[e$j]])); } ]] // AllOf(m1, m2, ..., mk) matches any value that matches all of the given // sub-matchers. AllOf is called fully qualified to prevent ADL from firing. $range i 2..n $for i [[ $range j 1..i $var m = i/2 $range k 1..m $range t m+1..i template <$for j, [[typename M$j]]> inline typename internal::AllOfResult$i<$for j, [[M$j]]>::type AllOf($for j, [[M$j m$j]]) { return typename internal::AllOfResult$i<$for j, [[M$j]]>::type( $if m == 1 [[m1]] $else [[::testing::AllOf($for k, [[m$k]])]], $if m+1 == i [[m$i]] $else [[::testing::AllOf($for t, [[m$t]])]]); } ]] // AnyOf(m1, m2, ..., mk) matches any value that matches any of the given // sub-matchers. AnyOf is called fully qualified to prevent ADL from firing. $range i 2..n $for i [[ $range j 1..i $var m = i/2 $range k 1..m $range t m+1..i template <$for j, [[typename M$j]]> inline typename internal::AnyOfResult$i<$for j, [[M$j]]>::type AnyOf($for j, [[M$j m$j]]) { return typename internal::AnyOfResult$i<$for j, [[M$j]]>::type( $if m == 1 [[m1]] $else [[::testing::AnyOf($for k, [[m$k]])]], $if m+1 == i [[m$i]] $else [[::testing::AnyOf($for t, [[m$t]])]]); } ]] } // namespace testing $$ } // This Pump meta comment fixes auto-indentation in Emacs. It will not $$ // show up in the generated code. // The MATCHER* family of macros can be used in a namespace scope to // define custom matchers easily. // // Basic Usage // =========== // // The syntax // // MATCHER(name, description_string) { statements; } // // defines a matcher with the given name that executes the statements, // which must return a bool to indicate if the match succeeds. Inside // the statements, you can refer to the value being matched by 'arg', // and refer to its type by 'arg_type'. // // The description string documents what the matcher does, and is used // to generate the failure message when the match fails. Since a // MATCHER() is usually defined in a header file shared by multiple // C++ source files, we require the description to be a C-string // literal to avoid possible side effects. It can be empty, in which // case we'll use the sequence of words in the matcher name as the // description. // // For example: // // MATCHER(IsEven, "") { return (arg % 2) == 0; } // // allows you to write // // // Expects mock_foo.Bar(n) to be called where n is even. // EXPECT_CALL(mock_foo, Bar(IsEven())); // // or, // // // Verifies that the value of some_expression is even. // EXPECT_THAT(some_expression, IsEven()); // // If the above assertion fails, it will print something like: // // Value of: some_expression // Expected: is even // Actual: 7 // // where the description "is even" is automatically calculated from the // matcher name IsEven. // // Argument Type // ============= // // Note that the type of the value being matched (arg_type) is // determined by the context in which you use the matcher and is // supplied to you by the compiler, so you don't need to worry about // declaring it (nor can you). This allows the matcher to be // polymorphic. For example, IsEven() can be used to match any type // where the value of "(arg % 2) == 0" can be implicitly converted to // a bool. In the "Bar(IsEven())" example above, if method Bar() // takes an int, 'arg_type' will be int; if it takes an unsigned long, // 'arg_type' will be unsigned long; and so on. // // Parameterizing Matchers // ======================= // // Sometimes you'll want to parameterize the matcher. For that you // can use another macro: // // MATCHER_P(name, param_name, description_string) { statements; } // // For example: // // MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } // // will allow you to write: // // EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); // // which may lead to this message (assuming n is 10): // // Value of: Blah("a") // Expected: has absolute value 10 // Actual: -9 // // Note that both the matcher description and its parameter are // printed, making the message human-friendly. // // In the matcher definition body, you can write 'foo_type' to // reference the type of a parameter named 'foo'. For example, in the // body of MATCHER_P(HasAbsoluteValue, value) above, you can write // 'value_type' to refer to the type of 'value'. // // We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to // support multi-parameter matchers. // // Describing Parameterized Matchers // ================================= // // The last argument to MATCHER*() is a string-typed expression. The // expression can reference all of the matcher's parameters and a // special bool-typed variable named 'negation'. When 'negation' is // false, the expression should evaluate to the matcher's description; // otherwise it should evaluate to the description of the negation of // the matcher. For example, // // using testing::PrintToString; // // MATCHER_P2(InClosedRange, low, hi, // std::string(negation ? "is not" : "is") + " in range [" + // PrintToString(low) + ", " + PrintToString(hi) + "]") { // return low <= arg && arg <= hi; // } // ... // EXPECT_THAT(3, InClosedRange(4, 6)); // EXPECT_THAT(3, Not(InClosedRange(2, 4))); // // would generate two failures that contain the text: // // Expected: is in range [4, 6] // ... // Expected: is not in range [2, 4] // // If you specify "" as the description, the failure message will // contain the sequence of words in the matcher name followed by the // parameter values printed as a tuple. For example, // // MATCHER_P2(InClosedRange, low, hi, "") { ... } // ... // EXPECT_THAT(3, InClosedRange(4, 6)); // EXPECT_THAT(3, Not(InClosedRange(2, 4))); // // would generate two failures that contain the text: // // Expected: in closed range (4, 6) // ... // Expected: not (in closed range (2, 4)) // // Types of Matcher Parameters // =========================== // // For the purpose of typing, you can view // // MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } // // as shorthand for // // template // FooMatcherPk // Foo(p1_type p1, ..., pk_type pk) { ... } // // When you write Foo(v1, ..., vk), the compiler infers the types of // the parameters v1, ..., and vk for you. If you are not happy with // the result of the type inference, you can specify the types by // explicitly instantiating the template, as in Foo(5, // false). As said earlier, you don't get to (or need to) specify // 'arg_type' as that's determined by the context in which the matcher // is used. You can assign the result of expression Foo(p1, ..., pk) // to a variable of type FooMatcherPk. This // can be useful when composing matchers. // // While you can instantiate a matcher template with reference types, // passing the parameters by pointer usually makes your code more // readable. If, however, you still want to pass a parameter by // reference, be aware that in the failure message generated by the // matcher you will see the value of the referenced object but not its // address. // // Explaining Match Results // ======================== // // Sometimes the matcher description alone isn't enough to explain why // the match has failed or succeeded. For example, when expecting a // long string, it can be very helpful to also print the diff between // the expected string and the actual one. To achieve that, you can // optionally stream additional information to a special variable // named result_listener, whose type is a pointer to class // MatchResultListener: // // MATCHER_P(EqualsLongString, str, "") { // if (arg == str) return true; // // *result_listener << "the difference: " /// << DiffStrings(str, arg); // return false; // } // // Overloading Matchers // ==================== // // You can overload matchers with different numbers of parameters: // // MATCHER_P(Blah, a, description_string1) { ... } // MATCHER_P2(Blah, a, b, description_string2) { ... } // // Caveats // ======= // // When defining a new matcher, you should also consider implementing // MatcherInterface or using MakePolymorphicMatcher(). These // approaches require more work than the MATCHER* macros, but also // give you more control on the types of the value being matched and // the matcher parameters, which may leads to better compiler error // messages when the matcher is used wrong. They also allow // overloading matchers based on parameter types (as opposed to just // based on the number of parameters). // // MATCHER*() can only be used in a namespace scope. The reason is // that C++ doesn't yet allow function-local types to be used to // instantiate templates. The up-coming C++0x standard will fix this. // Once that's done, we'll consider supporting using MATCHER*() inside // a function. // // More Information // ================ // // To learn more about using these macros, please search for 'MATCHER' // on // https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md $range i 0..n $for i [[ $var macro_name = [[$if i==0 [[MATCHER]] $elif i==1 [[MATCHER_P]] $else [[MATCHER_P$i]]]] $var class_name = [[name##Matcher[[$if i==0 [[]] $elif i==1 [[P]] $else [[P$i]]]]]] $range j 0..i-1 $var template = [[$if i==0 [[]] $else [[ template <$for j, [[typename p$j##_type]]>\ ]]]] $var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] $var impl_ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] $var impl_inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(::testing::internal::move(gmock_p$j))]]]]]] $var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(::testing::internal::move(gmock_p$j))]]]]]] $var params = [[$for j, [[p$j]]]] $var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]] $var param_types_and_names = [[$for j, [[p$j##_type p$j]]]] $var param_field_decls = [[$for j [[ p$j##_type const p$j;\ ]]]] $var param_field_decls2 = [[$for j [[ p$j##_type const p$j;\ ]]]] #define $macro_name(name$for j [[, p$j]], description)\$template class $class_name {\ public:\ template \ class gmock_Impl : public ::testing::MatcherInterface<\ GTEST_REFERENCE_TO_CONST_(arg_type)> {\ public:\ [[$if i==1 [[explicit ]]]]gmock_Impl($impl_ctor_param_list)\ $impl_inits {}\ virtual bool MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener) const;\ virtual void DescribeTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(false);\ }\ virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ *gmock_os << FormatDescription(true);\ }\$param_field_decls private:\ ::std::string FormatDescription(bool negation) const {\ ::std::string gmock_description = (description);\ if (!gmock_description.empty())\ return gmock_description;\ return ::testing::internal::FormatMatcherDescription(\ negation, #name, \ ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ ::testing::tuple<$for j, [[p$j##_type]]>($for j, [[p$j]])));\ }\ };\ template \ operator ::testing::Matcher() const {\ return ::testing::Matcher(\ new gmock_Impl($params));\ }\ [[$if i==1 [[explicit ]]]]$class_name($ctor_param_list)$inits {\ }\$param_field_decls2 private:\ };\$template inline $class_name$param_types name($param_types_and_names) {\ return $class_name$param_types($params);\ }\$template template \ bool $class_name$param_types::gmock_Impl::MatchAndExplain(\ GTEST_REFERENCE_TO_CONST_(arg_type) arg,\ ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ const ]] #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-generated-nice-strict.h ================================================ // This file was GENERATED by command: // pump.py gmock-generated-nice-strict.h.pump // DO NOT EDIT BY HAND!!! // Copyright 2008, Google Inc. // 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 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. // Implements class templates NiceMock, NaggyMock, and StrictMock. // // Given a mock class MockFoo that is created using Google Mock, // NiceMock is a subclass of MockFoo that allows // uninteresting calls (i.e. calls to mock methods that have no // EXPECT_CALL specs), NaggyMock is a subclass of MockFoo // that prints a warning when an uninteresting call occurs, and // StrictMock is a subclass of MockFoo that treats all // uninteresting calls as errors. // // Currently a mock is naggy by default, so MockFoo and // NaggyMock behave like the same. However, we will soon // switch the default behavior of mocks to be nice, as that in general // leads to more maintainable tests. When that happens, MockFoo will // stop behaving like NaggyMock and start behaving like // NiceMock. // // NiceMock, NaggyMock, and StrictMock "inherit" the constructors of // their respective base class. Therefore you can write // NiceMock(5, "a") to construct a nice mock where MockFoo // has a constructor that accepts (int, const char*), for example. // // A known limitation is that NiceMock, NaggyMock, // and StrictMock only works for mock methods defined using // the MOCK_METHOD* family of macros DIRECTLY in the MockFoo class. // If a mock method is defined in a base class of MockFoo, the "nice" // or "strict" modifier may not affect it, depending on the compiler. // In particular, nesting NiceMock, NaggyMock, and StrictMock is NOT // supported. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ #include "gmock/gmock-spec-builders.h" #include "gmock/internal/gmock-port.h" namespace testing { template class NiceMock : public MockClass { public: NiceMock() : MockClass() { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } #if GTEST_LANG_CXX11 // Ideally, we would inherit base class's constructors through a using // declaration, which would preserve their visibility. However, many existing // tests rely on the fact that current implementation reexports protected // constructors as public. These tests would need to be cleaned up first. // Single argument constructor is special-cased so that it can be // made explicit. template explicit NiceMock(A&& arg) : MockClass(std::forward(arg)) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } template NiceMock(A1&& arg1, A2&& arg2, An&&... args) : MockClass(std::forward(arg1), std::forward(arg2), std::forward(args)...) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } #else // C++98 doesn't have variadic templates, so we have to define one // for each arity. template explicit NiceMock(const A1& a1) : MockClass(a1) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } template NiceMock(const A1& a1, const A2& a2) : MockClass(a1, a2) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } template NiceMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } template NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4) : MockClass(a1, a2, a3, a4) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } template NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) : MockClass(a1, a2, a3, a4, a5) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } template NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } template NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5, a6, a7) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } template NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } template NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } template NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { ::testing::Mock::AllowUninterestingCalls( internal::ImplicitCast_(this)); } #endif // GTEST_LANG_CXX11 ~NiceMock() { ::testing::Mock::UnregisterCallReaction( internal::ImplicitCast_(this)); } private: GTEST_DISALLOW_COPY_AND_ASSIGN_(NiceMock); }; template class NaggyMock : public MockClass { public: NaggyMock() : MockClass() { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } #if GTEST_LANG_CXX11 // Ideally, we would inherit base class's constructors through a using // declaration, which would preserve their visibility. However, many existing // tests rely on the fact that current implementation reexports protected // constructors as public. These tests would need to be cleaned up first. // Single argument constructor is special-cased so that it can be // made explicit. template explicit NaggyMock(A&& arg) : MockClass(std::forward(arg)) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } template NaggyMock(A1&& arg1, A2&& arg2, An&&... args) : MockClass(std::forward(arg1), std::forward(arg2), std::forward(args)...) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } #else // C++98 doesn't have variadic templates, so we have to define one // for each arity. template explicit NaggyMock(const A1& a1) : MockClass(a1) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } template NaggyMock(const A1& a1, const A2& a2) : MockClass(a1, a2) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } template NaggyMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } template NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4) : MockClass(a1, a2, a3, a4) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } template NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) : MockClass(a1, a2, a3, a4, a5) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } template NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } template NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5, a6, a7) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } template NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } template NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } template NaggyMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { ::testing::Mock::WarnUninterestingCalls( internal::ImplicitCast_(this)); } #endif // GTEST_LANG_CXX11 ~NaggyMock() { ::testing::Mock::UnregisterCallReaction( internal::ImplicitCast_(this)); } private: GTEST_DISALLOW_COPY_AND_ASSIGN_(NaggyMock); }; template class StrictMock : public MockClass { public: StrictMock() : MockClass() { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } #if GTEST_LANG_CXX11 // Ideally, we would inherit base class's constructors through a using // declaration, which would preserve their visibility. However, many existing // tests rely on the fact that current implementation reexports protected // constructors as public. These tests would need to be cleaned up first. // Single argument constructor is special-cased so that it can be // made explicit. template explicit StrictMock(A&& arg) : MockClass(std::forward(arg)) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } template StrictMock(A1&& arg1, A2&& arg2, An&&... args) : MockClass(std::forward(arg1), std::forward(arg2), std::forward(args)...) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } #else // C++98 doesn't have variadic templates, so we have to define one // for each arity. template explicit StrictMock(const A1& a1) : MockClass(a1) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } template StrictMock(const A1& a1, const A2& a2) : MockClass(a1, a2) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } template StrictMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } template StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4) : MockClass(a1, a2, a3, a4) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } template StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5) : MockClass(a1, a2, a3, a4, a5) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } template StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } template StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5, a6, a7) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } template StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } template StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } template StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { ::testing::Mock::FailUninterestingCalls( internal::ImplicitCast_(this)); } #endif // GTEST_LANG_CXX11 ~StrictMock() { ::testing::Mock::UnregisterCallReaction( internal::ImplicitCast_(this)); } private: GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock); }; // The following specializations catch some (relatively more common) // user errors of nesting nice and strict mocks. They do NOT catch // all possible errors. // These specializations are declared but not defined, as NiceMock, // NaggyMock, and StrictMock cannot be nested. template class NiceMock >; template class NiceMock >; template class NiceMock >; template class NaggyMock >; template class NaggyMock >; template class NaggyMock >; template class StrictMock >; template class StrictMock >; template class StrictMock >; } // namespace testing #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-generated-nice-strict.h.pump ================================================ $$ -*- mode: c++; -*- $$ This is a Pump source file. Please use Pump to convert $$ it to gmock-generated-nice-strict.h. $$ $var n = 10 $$ The maximum arity we support. // Copyright 2008, Google Inc. // 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 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. // Implements class templates NiceMock, NaggyMock, and StrictMock. // // Given a mock class MockFoo that is created using Google Mock, // NiceMock is a subclass of MockFoo that allows // uninteresting calls (i.e. calls to mock methods that have no // EXPECT_CALL specs), NaggyMock is a subclass of MockFoo // that prints a warning when an uninteresting call occurs, and // StrictMock is a subclass of MockFoo that treats all // uninteresting calls as errors. // // Currently a mock is naggy by default, so MockFoo and // NaggyMock behave like the same. However, we will soon // switch the default behavior of mocks to be nice, as that in general // leads to more maintainable tests. When that happens, MockFoo will // stop behaving like NaggyMock and start behaving like // NiceMock. // // NiceMock, NaggyMock, and StrictMock "inherit" the constructors of // their respective base class. Therefore you can write // NiceMock(5, "a") to construct a nice mock where MockFoo // has a constructor that accepts (int, const char*), for example. // // A known limitation is that NiceMock, NaggyMock, // and StrictMock only works for mock methods defined using // the MOCK_METHOD* family of macros DIRECTLY in the MockFoo class. // If a mock method is defined in a base class of MockFoo, the "nice" // or "strict" modifier may not affect it, depending on the compiler. // In particular, nesting NiceMock, NaggyMock, and StrictMock is NOT // supported. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ #include "gmock/gmock-spec-builders.h" #include "gmock/internal/gmock-port.h" namespace testing { $range kind 0..2 $for kind [[ $var clazz=[[$if kind==0 [[NiceMock]] $elif kind==1 [[NaggyMock]] $else [[StrictMock]]]] $var method=[[$if kind==0 [[AllowUninterestingCalls]] $elif kind==1 [[WarnUninterestingCalls]] $else [[FailUninterestingCalls]]]] template class $clazz : public MockClass { public: $clazz() : MockClass() { ::testing::Mock::$method( internal::ImplicitCast_(this)); } #if GTEST_LANG_CXX11 // Ideally, we would inherit base class's constructors through a using // declaration, which would preserve their visibility. However, many existing // tests rely on the fact that current implementation reexports protected // constructors as public. These tests would need to be cleaned up first. // Single argument constructor is special-cased so that it can be // made explicit. template explicit $clazz(A&& arg) : MockClass(std::forward(arg)) { ::testing::Mock::$method( internal::ImplicitCast_(this)); } template $clazz(A1&& arg1, A2&& arg2, An&&... args) : MockClass(std::forward(arg1), std::forward(arg2), std::forward(args)...) { ::testing::Mock::$method( internal::ImplicitCast_(this)); } #else // C++98 doesn't have variadic templates, so we have to define one // for each arity. template explicit $clazz(const A1& a1) : MockClass(a1) { ::testing::Mock::$method( internal::ImplicitCast_(this)); } $range i 2..n $for i [[ $range j 1..i template <$for j, [[typename A$j]]> $clazz($for j, [[const A$j& a$j]]) : MockClass($for j, [[a$j]]) { ::testing::Mock::$method( internal::ImplicitCast_(this)); } ]] #endif // GTEST_LANG_CXX11 ~$clazz() { ::testing::Mock::UnregisterCallReaction( internal::ImplicitCast_(this)); } private: GTEST_DISALLOW_COPY_AND_ASSIGN_($clazz); }; ]] // The following specializations catch some (relatively more common) // user errors of nesting nice and strict mocks. They do NOT catch // all possible errors. // These specializations are declared but not defined, as NiceMock, // NaggyMock, and StrictMock cannot be nested. template class NiceMock >; template class NiceMock >; template class NiceMock >; template class NaggyMock >; template class NaggyMock >; template class NaggyMock >; template class StrictMock >; template class StrictMock >; template class StrictMock >; } // namespace testing #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ ================================================ FILE: src/third-party/googletest-1.8.1/googlemock/include/gmock/gmock-matchers.h ================================================ // Copyright 2007, Google Inc. // 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 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. // Google Mock - a framework for writing C++ mock classes. // // This file implements some commonly used argument matchers. More // matchers can be defined by the user implementing the // MatcherInterface interface if necessary. // GOOGLETEST_CM0002 DO NOT DELETE #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ #include #include #include #include #include // NOLINT #include #include #include #include #include "gtest/gtest.h" #include "gmock/internal/gmock-internal-utils.h" #include "gmock/internal/gmock-port.h" #if GTEST_HAS_STD_INITIALIZER_LIST_ # include // NOLINT -- must be after gtest.h #endif GTEST_DISABLE_MSC_WARNINGS_PUSH_( 4251 5046 /* class A needs to have dll-interface to be used by clients of class B */ /* Symbol involving type with internal linkage not defined */) namespace testing { // To implement a matcher Foo for type T, define: // 1. a class FooMatcherImpl that implements the // MatcherInterface interface, and // 2. a factory function that creates a Matcher object from a // FooMatcherImpl*. // // The two-level delegation design makes it possible to allow a user // to write "v" instead of "Eq(v)" where a Matcher is expected, which // is impossible if we pass matchers by pointers. It also eases // ownership management as Matcher objects can now be copied like // plain values. // MatchResultListener is an abstract class. Its << operator can be // used by a matcher to explain why a value matches or doesn't match. // // FIXME: add method // bool InterestedInWhy(bool result) const; // to indicate whether the listener is interested in why the match // result is 'result'. class MatchResultListener { public: // Creates a listener object with the given underlying ostream. The // listener does not own the ostream, and does not dereference it // in the constructor or destructor. explicit MatchResultListener(::std::ostream* os) : stream_(os) {} virtual ~MatchResultListener() = 0; // Makes this class abstract. // Streams x to the underlying ostream; does nothing if the ostream // is NULL. template MatchResultListener& operator<<(const T& x) { if (stream_ != NULL) *stream_ << x; return *this; } // Returns the underlying ostream. ::std::ostream* stream() { return stream_; } // Returns true iff the listener is interested in an explanation of // the match result. A matcher's MatchAndExplain() method can use // this information to avoid generating the explanation when no one // intends to hear it. bool IsInterested() const { return stream_ != NULL; } private: ::std::ostream* const stream_; GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener); }; inline MatchResultListener::~MatchResultListener() { } // An instance of a subclass of this knows how to describe itself as a // matcher. class MatcherDescriberInterface { public: virtual ~MatcherDescriberInterface() {} // Describes this matcher to an ostream. The function should print // a verb phrase that describes the property a value matching this // matcher should have. The subject of the verb phrase is the value // being matched. For example, the DescribeTo() method of the Gt(7) // matcher prints "is greater than 7". virtual void DescribeTo(::std::ostream* os) const = 0; // Describes the negation of this matcher to an ostream. For // example, if the description of this matcher is "is greater than // 7", the negated description could be "is not greater than 7". // You are not required to override this when implementing // MatcherInterface, but it is highly advised so that your matcher // can produce good error messages. virtual void DescribeNegationTo(::std::ostream* os) const { *os << "not ("; DescribeTo(os); *os << ")"; } }; // The implementation of a matcher. template class MatcherInterface : public MatcherDescriberInterface { public: // Returns true iff the matcher matches x; also explains the match // result to 'listener' if necessary (see the next paragraph), in // the form of a non-restrictive relative clause ("which ...", // "whose ...", etc) that describes x. For example, the // MatchAndExplain() method of the Pointee(...) matcher should // generate an explanation like "which points to ...". // // Implementations of MatchAndExplain() should add an explanation of // the match result *if and only if* they can provide additional // information that's not already present (or not obvious) in the // print-out of x and the matcher's description. Whether the match // succeeds is not a factor in deciding whether an explanation is // needed, as sometimes the caller needs to print a failure message // when the match succeeds (e.g. when the matcher is used inside // Not()). // // For example, a "has at least 10 elements" matcher should explain // what the actual element count is, regardless of the match result, // as it is useful information to the reader; on the other hand, an // "is empty" matcher probably only needs to explain what the actual // size is when the match fails, as it's redundant to say that the // size is 0 when the value is already known to be empty. // // You should override this method when defining a new matcher. // // It's the responsibility of the caller (Google Mock) to guarantee // that 'listener' is not NULL. This helps to simplify a matcher's // implementation when it doesn't care about the performance, as it // can talk to 'listener' without checking its validity first. // However, in order to implement dummy listeners efficiently, // listener->stream() may be NULL. virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; // Inherits these methods from MatcherDescriberInterface: // virtual void DescribeTo(::std::ostream* os) const = 0; // virtual void DescribeNegationTo(::std::ostream* os) const; }; namespace internal { // Converts a MatcherInterface to a MatcherInterface. template class MatcherInterfaceAdapter : public MatcherInterface { public: explicit MatcherInterfaceAdapter(const MatcherInterface* impl) : impl_(impl) {} virtual ~MatcherInterfaceAdapter() { delete impl_; } virtual void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } virtual void DescribeNegationTo(::std::ostream* os) const { impl_->DescribeNegationTo(os); } virtual bool MatchAndExplain(const T& x, MatchResultListener* listener) const { return impl_->MatchAndExplain(x, listener); } private: const MatcherInterface* const impl_; GTEST_DISALLOW_COPY_AND_ASSIGN_(MatcherInterfaceAdapter); }; } // namespace internal // A match result listener that stores the explanation in a string. class StringMatchResultListener : public MatchResultListener { public: StringMatchResultListener() : MatchResultListener(&ss_) {} // Returns the explanation accumulated so far. std::string str() const { return ss_.str(); } // Clears the explanation accumulated so far. void Clear() { ss_.str(""); } private: ::std::stringstream ss_; GTEST_DISALLOW_COPY_AND_ASSIGN_(StringMatchResultListener); }; namespace internal { struct AnyEq { template bool operator()(const A& a, const B& b) const { return a == b; } }; struct AnyNe { template bool operator()(const A& a, const B& b) const { return a != b; } }; struct AnyLt { template bool operator()(const A& a, const B& b) const { return a < b; } }; struct AnyGt { template bool operator()(const A& a, const B& b) const { return a > b; } }; struct AnyLe { template bool operator()(const A& a, const B& b) const { return a <= b; } }; struct AnyGe { template bool operator()(const A& a, const B& b) const { return a >= b; } }; // A match result listener that ignores the explanation. class DummyMatchResultListener : public MatchResultListener { public: DummyMatchResultListener() : MatchResultListener(NULL) {} private: GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener); }; // A match result listener that forwards the explanation to a given // ostream. The difference between this and MatchResultListener is // that the former is concrete. class StreamMatchResultListener : public MatchResultListener { public: explicit StreamMatchResultListener(::std::ostream* os) : MatchResultListener(os) {} private: GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener); }; // An internal class for implementing Matcher, which will derive // from it. We put functionalities common to all Matcher // specializations here to avoid code duplication. template class MatcherBase { public: // Returns true iff the matcher matches x; also explains the match // result to 'listener'. bool MatchAndExplain(GTEST_REFERENCE_TO_CONST_(T) x, MatchResultListener* listener) const { return impl_->MatchAndExplain(x, listener); } // Returns true iff this matcher matches x. bool Matches(GTEST_REFERENCE_TO_CONST_(T) x) const { DummyMatchResultListener dummy; return MatchAndExplain(x, &dummy); } // Describes this matcher to an ostream. void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } // Describes the negation of this matcher to an ostream. void DescribeNegationTo(::std::ostream* os) const { impl_->DescribeNegationTo(os); } // Explains why x matches, or doesn't match, the matcher. void ExplainMatchResultTo(GTEST_REFERENCE_TO_CONST_(T) x, ::std::ostream* os) const { StreamMatchResultListener listener(os); MatchAndExplain(x, &listener); } // Returns the describer for this matcher object; retains ownership // of the describer, which is only guaranteed to be alive when // this matcher object is alive. const MatcherDescriberInterface* GetDescriber() const { return impl_.get(); } protected: MatcherBase() {} // Constructs a matcher from its implementation. explicit MatcherBase( const MatcherInterface* impl) : impl_(impl) {} template explicit MatcherBase( const MatcherInterface* impl, typename internal::EnableIf< !internal::IsSame::value>::type* = NULL) : impl_(new internal::MatcherInterfaceAdapter(impl)) {} virtual ~MatcherBase() {} private: // shared_ptr (util/gtl/shared_ptr.h) and linked_ptr have similar // interfaces. The former dynamically allocates a chunk of memory // to hold the reference count, while the latter tracks all // references using a circular linked list without allocating // memory. It has been observed that linked_ptr performs better in // typical scenarios. However, shared_ptr can out-perform // linked_ptr when there are many more uses of the copy constructor // than the default constructor. // // If performance becomes a problem, we should see if using // shared_ptr helps. ::testing::internal::linked_ptr< const MatcherInterface > impl_; }; } // namespace internal // A Matcher is a copyable and IMMUTABLE (except by assignment) // object that can check whether a value of type T matches. The // implementation of Matcher is just a linked_ptr to const // MatcherInterface, so copying is fairly cheap. Don't inherit // from Matcher! template class Matcher : public internal::MatcherBase { public: // Constructs a null matcher. Needed for storing Matcher objects in STL // containers. A default-constructed matcher is not yet initialized. You // cannot use it until a valid value has been assigned to it. explicit Matcher() {} // NOLINT // Constructs a matcher from its implementation. explicit Matcher(const MatcherInterface* impl) : internal::MatcherBase(impl) {} template explicit Matcher(const MatcherInterface* impl, typename internal::EnableIf::value>::type* = NULL) : internal::MatcherBase(impl) {} // Implicit constructor here allows people to write // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes Matcher(T value); // NOLINT }; // The following two specializations allow the user to write str // instead of Eq(str) and "foo" instead of Eq("foo") when a std::string // matcher is expected. template <> class GTEST_API_ Matcher : public internal::MatcherBase { public: Matcher() {} explicit Matcher(const MatcherInterface* impl) : internal::MatcherBase(impl) {} // Allows the user to write str instead of Eq(str) sometimes, where // str is a std::string object. Matcher(const std::string& s); // NOLINT #if GTEST_HAS_GLOBAL_STRING // Allows the user to write str instead of Eq(str) sometimes, where // str is a ::string object. Matcher(const ::string& s); // NOLINT #endif // GTEST_HAS_GLOBAL_STRING // Allows the user to write "foo" instead of Eq("foo") sometimes. Matcher(const char* s); // NOLINT }; template <> class GTEST_API_ Matcher : public internal::MatcherBase { public: Matcher() {} explicit Matcher(const MatcherInterface* impl) : internal::MatcherBase(impl) {} explicit Matcher(const MatcherInterface* impl) : internal::MatcherBase(impl) {} // Allows the user to write str instead of Eq(str) sometimes, where // str is a string object. Matcher(const std::string& s); // NOLINT #if GTEST_HAS_GLOBAL_STRING // Allows the user to write str instead of Eq(str) sometimes, where // str is a ::string object. Matcher(const ::string& s); // NOLINT #endif // GTEST_HAS_GLOBAL_STRING // Allows the user to write "foo" instead of Eq("foo") sometimes. Matcher(const char* s); // NOLINT }; #if GTEST_HAS_GLOBAL_STRING // The following two specializations allow the user to write str // instead of Eq(str) and "foo" instead of Eq("foo") when a ::string // matcher is expected. template <> class GTEST_API_ Matcher : public internal::MatcherBase { public: Matcher() {} explicit Matcher(const MatcherInterface* impl) : internal::MatcherBase(impl) {} // Allows the user to write str instead of Eq(str) sometimes, where // str is a std::string object. Matcher(const std::string& s); // NOLINT // Allows the user to write str instead of Eq(str) sometimes, where // str is a ::string object. Matcher(const ::string& s); // NOLINT // Allows the user to write "foo" instead of Eq("foo") sometimes. Matcher(const char* s); // NOLINT }; template <> class GTEST_API_ Matcher< ::string> : public internal::MatcherBase< ::string> { public: Matcher() {} explicit Matcher(const MatcherInterface* impl) : internal::MatcherBase< ::string>(impl) {} explicit Matcher(const MatcherInterface< ::string>* impl) : internal::MatcherBase< ::string>(impl) {} // Allows the user to write str instead of Eq(str) sometimes, where // str is a std::string object. Matcher(const std::string& s); // NOLINT // Allows the user to write str instead of Eq(str) sometimes, where // str is a ::string object. Matcher(const ::string& s); // NOLINT // Allows the user to write "foo" instead of Eq("foo") sometimes. Matcher(const char* s); // NOLINT }; #endif // GTEST_HAS_GLOBAL_STRING #if GTEST_HAS_ABSL // The following two specializations allow the user to write str // instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view // matcher is expected. template <> class GTEST_API_ Matcher : public internal::MatcherBase { public: Matcher() {} explicit Matcher(const MatcherInterface* impl) : internal::MatcherBase(impl) {} // Allows the user to write str instead of Eq(str) sometimes, where // str is a std::string object. Matcher(const std::string& s); // NOLINT #if GTEST_HAS_GLOBAL_STRING // Allows the user to write str instead of Eq(str) sometimes, where // str is a ::string object. Matcher(const ::string& s); // NOLINT #endif // GTEST_HAS_GLOBAL_STRING // Allows the user to write "foo" instead of Eq("foo") sometimes. Matcher(const char* s); // NOLINT // Allows the user to pass absl::string_views directly. Matcher(absl::string_view s); // NOLINT }; template <> class GTEST_API_ Matcher : public internal::MatcherBase { public: Matcher() {} explicit Matcher(const MatcherInterface* impl) : internal::MatcherBase(impl) {} explicit Matcher(const MatcherInterface* impl) : internal::MatcherBase(impl) {} // Allows the user to write str instead of Eq(str) sometimes, where // str is a std::string object. Matcher(const std::string& s); // NOLINT #if GTEST_HAS_GLOBAL_STRING // Allows the user to write str instead of Eq(str) sometimes, where // str is a ::string object. Matcher(const ::string& s); // NOLINT #endif // GTEST_HAS_GLOBAL_STRING // Allows the user to write "foo" instead of Eq("foo") sometimes. Matcher(const char* s); // NOLINT // Allows the user to pass absl::string_views directly. Matcher(absl::string_view s); // NOLINT }; #endif // GTEST_HAS_ABSL // Prints a matcher in a human-readable format. template std::ostream& operator<<(std::ostream& os, const Matcher& matcher) { matcher.DescribeTo(&os); return os; } // The PolymorphicMatcher class template makes it easy to implement a // polymorphic matcher (i.e. a matcher that can match values of more // than one type, e.g. Eq(n) and NotNull()). // // To define a polymorphic matcher, a user should provide an Impl // class that has a DescribeTo() method and a DescribeNegationTo() // method, and define a member function (or member function template) // // bool MatchAndExplain(const Value& value, // MatchResultListener* listener) const; // // See the definition of NotNull() for a complete example. template class PolymorphicMatcher { public: explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {} // Returns a mutable reference to the underlying matcher // implementation object. Impl& mutable_impl() { return impl_; } // Returns an immutable reference to the underlying matcher // implementation object. const Impl& impl() const { return impl_; } template operator Matcher() const { return Matcher(new MonomorphicImpl(impl_)); } private: template class MonomorphicImpl : public MatcherInterface { public: explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} virtual void DescribeTo(::std::ostream* os) const { impl_.DescribeTo(os); } virtual void DescribeNegationTo(::std::ostream* os) const { impl_.DescribeNegationTo(os); } virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { return impl_.MatchAndExplain(x, listener); } private: const Impl impl_; GTEST_DISALLOW_ASSIGN_(MonomorphicImpl); }; Impl impl_; GTEST_DISALLOW_ASSIGN_(PolymorphicMatcher); }; // Creates a matcher from its implementation. This is easier to use // than the Matcher constructor as it doesn't require you to // explicitly write the template argument, e.g. // // MakeMatcher(foo); // vs // Matcher(foo); template inline Matcher MakeMatcher(const MatcherInterface* impl) { return Matcher(impl); } // Creates a polymorphic matcher from its implementation. This is // easier to use than the PolymorphicMatcher constructor as it // doesn't require you to explicitly write the template argument, e.g. // // MakePolymorphicMatcher(foo); // vs // PolymorphicMatcher(foo); template inline PolymorphicMatcher MakePolymorphicMatcher(const Impl& impl) { return PolymorphicMatcher(impl); } // Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION // and MUST NOT BE USED IN USER CODE!!! namespace internal { // The MatcherCastImpl class template is a helper for implementing // MatcherCast(). We need this helper in order to partially // specialize the implementation of MatcherCast() (C++ allows // class/struct templates to be partially specialized, but not // function templates.). // This general version is used when MatcherCast()'s argument is a // polymorphic matcher (i.e. something that can be converted to a // Matcher but is not one yet; for example, Eq(value)) or a value (for // example, "hello"). template class MatcherCastImpl { public: static Matcher Cast(const M& polymorphic_matcher_or_value) { // M can be a polymorphic matcher, in which case we want to use // its conversion operator to create Matcher. Or it can be a value // that should be passed to the Matcher's constructor. // // We can't call Matcher(polymorphic_matcher_or_value) when M is a // polymorphic matcher because it'll be ambiguous if T has an implicit // constructor from M (this usually happens when T has an implicit // constructor from any type). // // It won't work to unconditionally implict_cast // polymorphic_matcher_or_value to Matcher because it won't trigger // a user-defined conversion from M to T if one exists (assuming M is // a value). return CastImpl( polymorphic_matcher_or_value, BooleanConstant< internal::ImplicitlyConvertible >::value>(), BooleanConstant< internal::ImplicitlyConvertible::value>()); } private: template static Matcher CastImpl(const M& polymorphic_matcher_or_value, BooleanConstant /* convertible_to_matcher */, BooleanConstant) { // M is implicitly convertible to Matcher, which means that either // M is a polymorphic matcher or Matcher has an implicit constructor // from M. In both cases using the implicit conversion will produce a // matcher. // // Even if T has an implicit constructor from M, it won't be called because // creating Matcher would require a chain of two user-defined conversions // (first to create T from M and then to create Matcher from T). return polymorphic_matcher_or_value; } // M can't be implicitly converted to Matcher, so M isn't a polymorphic // matcher. It's a value of a type implicitly convertible to T. Use direct // initialization to create a matcher. static Matcher CastImpl( const M& value, BooleanConstant /* convertible_to_matcher */, BooleanConstant /* convertible_to_T */) { return Matcher(ImplicitCast_(value)); } // M can't be implicitly converted to either Matcher or T. Attempt to use // polymorphic matcher Eq(value) in this case. // // Note that we first attempt to perform an implicit cast on the value and // only fall back to the polymorphic Eq() matcher afterwards because the // latter calls bool operator==(const Lhs& lhs, const Rhs& rhs) in the end // which might be undefined even when Rhs is implicitly convertible to Lhs // (e.g. std::pair vs. std::pair). // // We don't define this method inline as we need the declaration of Eq(). static Matcher CastImpl( const M& value, BooleanConstant /* convertible_to_matcher */, BooleanConstant /* convertible_to_T */); }; // This more specialized version is used when MatcherCast()'s argument // is already a Matcher. This only compiles when type T can be // statically converted to type U. template class MatcherCastImpl > { public: static Matcher Cast(const Matcher& source_matcher) { return Matcher(new Impl(source_matcher)); } private: class Impl : public MatcherInterface { public: explicit Impl(const Matcher& source_matcher) : source_matcher_(source_matcher) {} // We delegate the matching logic to the source matcher. virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { #if GTEST_LANG_CXX11 using FromType = typename std::remove_cv::type>::type>::type; using ToType = typename std::remove_cv::type>::type>::type; // Do not allow implicitly converting base*/& to derived*/&. static_assert( // Do not trigger if only one of them is a pointer. That implies a // regular conversion and not a down_cast. (std::is_pointer::type>::value != std::is_pointer::type>::value) || std::is_same::value || !std::is_base_of::value, "Can't implicitly convert from to "); #endif // GTEST_LANG_CXX11 return source_matcher_.MatchAndExplain(static_cast(x), listener); } virtual void DescribeTo(::std::ostream* os) const { source_matcher_.DescribeTo(os); } virtual void DescribeNegationTo(::std::ostream* os) const { source_matcher_.DescribeNegationTo(os); } private: const Matcher source_matcher_; GTEST_DISALLOW_ASSIGN_(Impl); }; }; // This even more specialized version is used for efficiently casting // a matcher to its own type. template class MatcherCastImpl > { public: static Matcher Cast(const Matcher& matcher) { return matcher; } }; } // namespace internal // In order to be safe and clear, casting between different matcher // types is done explicitly via MatcherCast(m), which takes a // matcher m and returns a Matcher. It compiles only when T can be // statically converted to the argument type of m. template inline Matcher MatcherCast(const M& matcher) { return internal::MatcherCastImpl::Cast(matcher); } // Implements SafeMatcherCast(). // // We use an intermediate class to do the actual safe casting as Nokia's // Symbian compiler cannot decide between // template ... (M) and // template ... (const Matcher&) // for function templates but can for member function templates. template class SafeMatcherCastImpl { public: // This overload handles polymorphic matchers and values only since // monomorphic matchers are handled by the next one. template static inline Matcher Cast(const M& polymorphic_matcher_or_value) { return internal::MatcherCastImpl::Cast(polymorphic_matcher_or_value); } // This overload handles monomorphic matchers. // // In general, if type T can be implicitly converted to type U, we can // safely convert a Matcher to a Matcher (i.e. Matcher is // contravariant): just keep a copy of the original Matcher, convert the // argument from type T to U, and then pass it to the underlying Matcher. // The only exception is when U is a reference and T is not, as the // underlying Matcher may be interested in the argument's address, which // is not preserved in the conversion from T to U. template static inline Matcher Cast(const Matcher& matcher) { // Enforce that T can be implicitly converted to U. GTEST_COMPILE_ASSERT_((internal::ImplicitlyConvertible::value), T_must_be_implicitly_convertible_to_U); // Enforce that we are not converting a non-reference type T to a reference // type U. GTEST_COMPILE_ASSERT_( internal::is_reference::value || !internal::is_reference::value, cannot_convert_non_reference_arg_to_reference); // In case both T and U are arithmetic types, enforce that the // conversion is not lossy. typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT; typedef GTEST_REMOVE_REFERENCE_AND_CONST_(U) RawU; const bool kTIsOther = GMOCK_KIND_OF_(RawT) == internal::kOther; const bool kUIsOther = GMOCK_KIND_OF_(RawU) == internal::kOther; GTEST_COMPILE_ASSERT_( kTIsOther || kUIsOther || (internal::LosslessArithmeticConvertible::value), conversion_of_arithmetic_types_must_be_lossless); return MatcherCast(matcher); } }; template inline Matcher SafeMatcherCast(const M& polymorphic_matcher) { return SafeMatcherCastImpl::Cast(polymorphic_matcher); } // A() returns a matcher that matches any value of type T. template Matcher A(); // Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION // and MUST NOT BE USED IN USER CODE!!! namespace internal { // If the explanation is not empty, prints it to the ostream. inline void PrintIfNotEmpty(const std::string& explanation, ::std::ostream* os) { if (explanation != "" && os != NULL) { *os << ", " << explanation; } } // Returns true if the given type name is easy to read by a human. // This is used to decide whether printing the type of a value might // be helpful. inline bool IsReadableTypeName(const std::string& type_name) { // We consider a type name readable if it's short or doesn't contain // a template or function type. return (type_name.length() <= 20 || type_name.find_first_of("<(") == std::string::npos); } // Matches the value against the given matcher, prints the value and explains // the match result to the listener. Returns the match result. // 'listener' must not be NULL. // Value cannot be passed by const reference, because some matchers take a // non-const argument. template bool MatchPrintAndExplain(Value& value, const Matcher& matcher, MatchResultListener* listener) { if (!listener->IsInterested()) { // If the listener is not interested, we do not need to construct the // inner explanation. return matcher.Matches(value); } StringMatchResultListener inner_listener; const bool match = matcher.MatchAndExplain(value, &inner_listener); UniversalPrint(value, listener->stream()); #if GTEST_HAS_RTTI const std::string& type_name = GetTypeName(); if (IsReadableTypeName(type_name)) *listener->stream() << " (of type " << type_name << ")"; #endif PrintIfNotEmpty(inner_listener.str(), listener->stream()); return match; } // An internal helper class for doing compile-time loop on a tuple's // fields. template class TuplePrefix { public: // TuplePrefix::Matches(matcher_tuple, value_tuple) returns true // iff the first N fields of matcher_tuple matches the first N // fields of value_tuple, respectively. template static bool Matches(const MatcherTuple& matcher_tuple, const ValueTuple& value_tuple) { return TuplePrefix::Matches(matcher_tuple, value_tuple) && get(matcher_tuple).Matches(get(value_tuple)); } // TuplePrefix::ExplainMatchFailuresTo(matchers, values, os) // describes failures in matching the first N fields of matchers // against the first N fields of values. If there is no failure, // nothing will be streamed to os. template static void ExplainMatchFailuresTo(const MatcherTuple& matchers, const ValueTuple& values, ::std::ostream* os) { // First, describes failures in the first N - 1 fields. TuplePrefix::ExplainMatchFailuresTo(matchers, values, os); // Then describes the failure (if any) in the (N - 1)-th (0-based) // field. typename tuple_element::type matcher = get(matchers); typedef typename tuple_element::type Value; GTEST_REFERENCE_TO_CONST_(Value) value = get(values); StringMatchResultListener listener; if (!matcher.MatchAndExplain(value, &listener)) { // FIXME: include in the message the name of the parameter // as used in MOCK_METHOD*() when possible. *os << " Expected arg #" << N - 1 << ": "; get(matchers).DescribeTo(os); *os << "\n Actual: "; // We remove the reference in type Value to prevent the // universal printer from printing the address of value, which // isn't interesting to the user most of the time. The // matcher's MatchAndExplain() method handles the case when // the address is interesting. internal::UniversalPrint(value, os); PrintIfNotEmpty(listener.str(), os); *os << "\n"; } } }; // The base case. template <> class TuplePrefix<0> { public: template static bool Matches(const MatcherTuple& /* matcher_tuple */, const ValueTuple& /* value_tuple */) { return true; } template static void ExplainMatchFailuresTo(const MatcherTuple& /* matchers */, const ValueTuple& /* values */, ::std::ostream* /* os */) {} }; // TupleMatches(matcher_tuple, value_tuple) returns true iff all // matchers in matcher_tuple match the corresponding fields in // value_tuple. It is a compiler error if matcher_tuple and // value_tuple have different number of fields or incompatible field // types. template bool TupleMatches(const MatcherTuple& matcher_tuple, const ValueTuple& value_tuple) { // Makes sure that matcher_tuple and value_tuple have the same // number of fields. GTEST_COMPILE_ASSERT_(tuple_size::value == tuple_size::value, matcher_and_value_have_different_numbers_of_fields); return TuplePrefix::value>:: Matches(matcher_tuple, value_tuple); } // Describes failures in matching matchers against values. If there // is no failure, nothing will be streamed to os. template void ExplainMatchFailureTupleTo(const MatcherTuple& matchers, const ValueTuple& values, ::std::ostream* os) { TuplePrefix::value>::ExplainMatchFailuresTo( matchers, values, os); } // TransformTupleValues and its helper. // // TransformTupleValuesHelper hides the internal machinery that // TransformTupleValues uses to implement a tuple traversal. template class TransformTupleValuesHelper { private: typedef ::testing::tuple_size TupleSize; public: // For each member of tuple 't', taken in order, evaluates '*out++ = f(t)'. // Returns the final value of 'out' in case the caller needs it. static OutIter Run(Func f, const Tuple& t, OutIter out) { return IterateOverTuple()(f, t, out); } private: template struct IterateOverTuple { OutIter operator() (Func f, const Tup& t, OutIter out) const { *out++ = f(::testing::get(t)); return IterateOverTuple()(f, t, out); } }; template struct IterateOverTuple { OutIter operator() (Func /* f */, const Tup& /* t */, OutIter out) const { return out; } }; }; // Successively invokes 'f(element)' on each element of the tuple 't', // appending each result to the 'out' iterator. Returns the final value // of 'out'. template OutIter TransformTupleValues(Func f, const Tuple& t, OutIter out) { return TransformTupleValuesHelper::Run(f, t, out); } // Implements A(). template class AnyMatcherImpl : public MatcherInterface { public: virtual bool MatchAndExplain(GTEST_REFERENCE_TO_CONST_(T) /* x */, MatchResultListener* /* listener */) const { return true; } virtual void DescribeTo(::std::ostream* os) const { *os << "is anything"; } virtual void DescribeNegationTo(::std::ostream* os) const { // This is mostly for completeness' safe, as it's not very useful // to write Not(A()). However we cannot completely rule out // such a possibility, and it doesn't hurt to be prepared. *os << "never matches"; } }; // Implements _, a matcher that matches any value of any // type. This is a polymorphic matcher, so we need a template type // conversion operator to make it appearing as a Matcher for any // type T. class AnythingMatcher { public: template operator Matcher() const { return A(); } }; // Implements a matcher that compares a given value with a // pre-supplied value using one of the ==, <=, <, etc, operators. The // two values being compared don't have to have the same type. // // The matcher defined here is polymorphic (for example, Eq(5) can be // used to match an int, a short, a double, etc). Therefore we use // a template type conversion operator in the implementation. // // The following template definition assumes that the Rhs parameter is // a "bare" type (i.e. neither 'const T' nor 'T&'). template class ComparisonBase { public: explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {} template operator Matcher() const { return MakeMatcher(new Impl(rhs_)); } private: template class Impl : public MatcherInterface { public: explicit Impl(const Rhs& rhs) : rhs_(rhs) {} virtual bool MatchAndExplain( Lhs lhs, MatchResultListener* /* listener */) const { return Op()(lhs, rhs_); } virtual void DescribeTo(::std::ostream* os) const { *os << D::Desc() << " "; UniversalPrint(rhs_, os); } virtual void DescribeNegationTo(::std::ostream* os) const { *os << D::NegatedDesc() << " "; UniversalPrint(rhs_, os); } private: Rhs rhs_; GTEST_DISALLOW_ASSIGN_(Impl); }; Rhs rhs_; GTEST_DISALLOW_ASSIGN_(ComparisonBase); }; template class EqMatcher : public ComparisonBase, Rhs, AnyEq> { public: explicit EqMatcher(const Rhs& rhs) : ComparisonBase, Rhs, AnyEq>(rhs) { } static const char* Desc() { return "is equal to"; } static const char* NegatedDesc() { return "isn't equal to"; } }; template class NeMatcher : public ComparisonBase, Rhs, AnyNe> { public: explicit NeMatcher(const Rhs& rhs) : ComparisonBase, Rhs, AnyNe>(rhs) { } static const char* Desc() { return "isn't equal to"; } static const char* NegatedDesc() { return "is equal to"; } }; template class LtMatcher : public ComparisonBase, Rhs, AnyLt> { public: explicit LtMatcher(const Rhs& rhs) : ComparisonBase, Rhs, AnyLt>(rhs) { } static const char* Desc() { return "is <"; } static const char* NegatedDesc() { return "isn't <"; } }; template class GtMatcher : public ComparisonBase, Rhs, AnyGt> { public: explicit GtMatcher(const Rhs& rhs) : ComparisonBase, Rhs, AnyGt>(rhs) { } static const char* Desc() { return "is >"; } static const char* NegatedDesc() { return "isn't >"; } }; template class LeMatcher : public ComparisonBase, Rhs, AnyLe> { public: explicit LeMatcher(const Rhs& rhs) : ComparisonBase, Rhs, AnyLe>(rhs) { } static const char* Desc() { return "is <="; } static const char* NegatedDesc() { return "isn't <="; } }; template class GeMatcher : public ComparisonBase, Rhs, AnyGe> { public: explicit GeMatcher(const Rhs& rhs) : ComparisonBase, Rhs, AnyGe>(rhs) { } static const char* Desc() { return "is >="; } static const char* NegatedDesc() { return "isn't >="; } }; // Implements the polymorphic IsNull() matcher, which matches any raw or smart // pointer that is NULL. class IsNullMatcher { public: template bool MatchAndExplain(const Pointer& p, MatchResultListener* /* listener */) const { #if GTEST_LANG_CXX11 return p == nullptr; #else // GTEST_LANG_CXX11 return GetRawPointer(p) == NULL; #endif // GTEST_LANG_CXX11 } void DescribeTo(::std::ostream* os) const { *os << "is NULL"; } void DescribeNegationTo(::std::ostream* os) const { *os << "isn't NULL"; } }; // Implements the polymorphic NotNull() matcher, which matches any raw or smart // pointer that is not NULL. class NotNullMatcher { public: template bool MatchAndExplain(const Pointer& p, MatchResultListener* /* listener */) const { #if GTEST_LANG_CXX11 return p != nullptr; #else // GTEST_LANG_CXX11 return GetRawPointer(p) != NULL; #endif // GTEST_LANG_CXX11 } void DescribeTo(::std::ostream* os) const { *os << "isn't NULL"; } void DescribeNegationTo(::std::ostream* os) const { *os << "is NULL"; } }; // Ref(variable) matches any argument that is a reference to // 'variable'. This matcher is polymorphic as it can match any // super type of the type of 'variable'. // // The RefMatcher template class implements Ref(variable). It can // only be instantiated with a reference type. This prevents a user // from mistakenly using Ref(x) to match a non-reference function // argument. For example, the following will righteously cause a // compiler error: // // int n; // Matcher m1 = Ref(n); // This won't compile. // Matcher m2 = Ref(n); // This will compile. template class RefMatcher; template class RefMatcher { // Google Mock is a generic framework and thus needs to support // mocking any function types, including those that take non-const // reference arguments. Therefore the template parameter T (and // Super below) can be instantiated to either a const type or a // non-const type. public: // RefMatcher() takes a T& instead of const T&, as we want the // compiler to catch using Ref(const_value) as a matcher for a // non-const reference. explicit RefMatcher(T& x) : object_(x) {} // NOLINT template operator Matcher() const { // By passing object_ (type T&) to Impl(), which expects a Super&, // we make sure that Super is a super type of T. In particular, // this catches using Ref(const_value) as a matcher for a // non-const reference, as you cannot implicitly convert a const // reference to a non-const reference. return MakeMatcher(new Impl(object_)); } private: template class Impl : public MatcherInterface { public: explicit Impl(Super& x) : object_(x) {} // NOLINT // MatchAndExplain() takes a Super& (as opposed to const Super&) // in order to match the interface MatcherInterface. virtual bool MatchAndExplain( Super& x, MatchResultListener* listener) const { *listener << "which is located @" << static_cast(&x); return &x == &object_; } virtual void DescribeTo(::std::ostream* os) const { *os << "references the variable "; UniversalPrinter::Print(object_, os); } virtual void DescribeNegationTo(::std::ostream* os) const { *os << "does not reference the variable "; UniversalPrinter::Print(object_, os); } private: const Super& object_; GTEST_DISALLOW_ASSIGN_(Impl); }; T& object_; GTEST_DISALLOW_ASSIGN_(RefMatcher); }; // Polymorphic helper functions for narrow and wide string matchers. inline bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs) { return String::CaseInsensitiveCStringEquals(lhs, rhs); } inline bool CaseInsensitiveCStringEquals(const wchar_t* lhs, const wchar_t* rhs) { return String::CaseInsensitiveWideCStringEquals(lhs, rhs); } // String comparison for narrow or wide strings that can have embedded NUL // characters. template bool CaseInsensitiveStringEquals(const StringType& s1, const StringType& s2) { // Are the heads equal? if (!CaseInsensitiveCStringEquals(s1.c_str(), s2.c_str())) { return false; } // Skip the equal heads. const typename StringType::value_type nul = 0; const size_t i1 = s1.find(nul), i2 = s2.find(nul); // Are we at the end of either s1 or s2? if (i1 == StringType::npos || i2 == StringType::npos) { return i1 == i2; } // Are the tails equal? return CaseInsensitiveStringEquals(s1.substr(i1 + 1), s2.substr(i2 + 1)); } // String matchers. // Implements equality-based string matchers like StrEq, StrCaseNe, and etc. template class StrEqualityMatcher { public: StrEqualityMatcher(const StringType& str, bool expect_eq, bool case_sensitive) : string_(str), expect_eq_(expect_eq), case_sensitive_(case_sensitive) {} #if GTEST_HAS_ABSL bool MatchAndExplain(const absl::string_view& s, MatchResultListener* listener) const { if (s.data() == NULL) { return !expect_eq_; } // This should fail to compile if absl::string_view is used with wide // strings. const StringType& str = string(s); return MatchAndExplain(str, listener); } #endif // GTEST_HAS_ABSL // Accepts pointer types, particularly: // const char* // char* // const wchar_t* // wchar_t* template bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { if (s == NULL) { return !expect_eq_; } return MatchAndExplain(StringType(s), listener); } // Matches anything that can convert to StringType. // // This is a template, not just a plain function with const StringType&, // because absl::string_view has some interfering non-explicit constructors. template bool MatchAndExplain(const MatcheeStringType& s, MatchResultListener* /* listener */) const { const StringType& s2(s); const bool eq = case_sensitive_ ? s2 == string_ : CaseInsensitiveStringEquals(s2, string_); return expect_eq_ == eq; } void DescribeTo(::std::ostream* os) const { DescribeToHelper(expect_eq_, os); } void DescribeNegationTo(::std::ostream* os) const { DescribeToHelper(!expect_eq_, os); } private: void DescribeToHelper(bool expect_eq, ::std::ostream* os) const { *os << (expect_eq ? "is " : "isn't "); *os << "equal to "; if (!case_sensitive_) { *os << "(ignoring case) "; } UniversalPrint(string_, os); } const StringType string_; const bool expect_eq_; const bool case_sensitive_; GTEST_DISALLOW_ASSIGN_(StrEqualityMatcher); }; // Implements the polymorphic HasSubstr(substring) matcher, which // can be used as a Matcher as long as T can be converted to a // string. template class HasSubstrMatcher { public: explicit HasSubstrMatcher(const StringType& substring) : substring_(substring) {} #if GTEST_HAS_ABSL bool MatchAndExplain(const absl::string_view& s, MatchResultListener* listener) const { if (s.data() == NULL) { return false; } // This should fail to compile if absl::string_view is used with wide // strings. const StringType& str = string(s); return MatchAndExplain(str, listener); } #endif // GTEST_HAS_ABSL // Accepts pointer types, particularly: // const char* // char* // const wchar_t* // wchar_t* template bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { return s != NULL && MatchAndExplain(StringType(s), listener); } // Matches anything that can convert to StringType. // // This is a template, not just a plain function with const StringType&, // because absl::string_view has some interfering non-explicit constructors. template bool MatchAndExplain(const MatcheeStringType& s, MatchResultListener* /* listener */) const { const StringType& s2(s); return s2.find(substring_) != StringType::npos; } // Describes what this matcher matches. void DescribeTo(::std::ostream* os) const { *os << "has substring "; UniversalPrint(substring_, os); } void DescribeNegationTo(::std::ostream* os) const { *os << "has no substring "; UniversalPrint(substring_, os); } private: const StringType substring_; GTEST_DISALLOW_ASSIGN_(HasSubstrMatcher); }; // Implements the polymorphic StartsWith(substring) matcher, which // can be used as a Matcher as long as T can be converted to a // string. template class StartsWithMatcher { public: explicit StartsWithMatcher(const StringType& prefix) : prefix_(prefix) { } #if GTEST_HAS_ABSL bool MatchAndExplain(const absl::string_view& s, MatchResultListener* listener) const { if (s.data() == NULL) { return false; } // This should fail to compile if absl::string_view is used with wide // strings. const StringType& str = string(s); return MatchAndExplain(str, listener); } #endif // GTEST_HAS_ABSL // Accepts pointer types, particularly: // const char* // char* // const wchar_t* // wchar_t* template bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { return s != NULL && MatchAndExplain(StringType(s), listener); } // Matches anything that can convert to StringType. // // This is a template, not just a plain function with const StringType&, // because absl::string_view has some interfering non-explicit constructors. template bool MatchAndExplain(const MatcheeStringType& s, MatchResultListener* /* listener */) const { const StringType& s2(s); return s2.length() >= prefix_.length() && s2.substr(0, prefix_.length()) == prefix_; } void DescribeTo(::std::ostream* os) const { *os << "starts with "; UniversalPrint(prefix_, os); } void DescribeNegationTo(::std::ostream* os) const { *os << "doesn't start with "; UniversalPrint(prefix_, os); } private: const StringType prefix_; GTEST_DISALLOW_ASSIGN_(StartsWithMatcher); }; // Implements the polymorphic EndsWith(substring) matcher, which // can be used as a Matcher as long as T can be converted to a // string. template class EndsWithMatcher { public: explicit EndsWithMatcher(const StringType& suffix) : suffix_(suffix) {} #if GTEST_HAS_ABSL bool MatchAndExplain(const absl::string_view& s, MatchResultListener* listener) const { if (s.data() == NULL) { return false; } // This should fail to compile if absl::string_view is used with wide // strings. const StringType& str = string(s); return MatchAndExplain(str, listener); } #endif // GTEST_HAS_ABSL // Accepts pointer types, particularly: // const char* // char* // const wchar_t* // wchar_t* template bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { return s != NULL && MatchAndExplain(StringType(s), listener); } // Matches anything that can convert to StringType. // // This is a template, not just a plain function with const StringType&, // because absl::string_view has some interfering non-explicit constructors. template bool MatchAndExplain(const MatcheeStringType& s, MatchResultListener* /* listener */) const { const StringType& s2(s); return s2.length() >= suffix_.length() && s2.substr(s2.length() - suffix_.length()) == suffix_; } void DescribeTo(::std::ostream* os) const { *os << "ends with "; UniversalPrint(suffix_, os); } void DescribeNegationTo(::std::ostream* os) const { *os << "doesn't end with "; UniversalPrint(suffix_, os); } private: const StringType suffix_; GTEST_DISALLOW_ASSIGN_(EndsWithMatcher); }; // Implements polymorphic matchers MatchesRegex(regex) and // ContainsRegex(regex), which can be used as a Matcher as long as // T can be converted to a string. class MatchesRegexMatcher { public: MatchesRegexMatcher(const RE* regex, bool full_match) : regex_(regex), full_match_(full_match) {} #if GTEST_HAS_ABSL bool MatchAndExplain(const absl::string_view& s, MatchResultListener* listener) const { return s.data() && MatchAndExplain(string(s), listener); } #endif // GTEST_HAS_ABSL // Accepts pointer types, particularly: // const char* // char* // const wchar_t* // wchar_t* template bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { return s != NULL && MatchAndExplain(std::string(s), listener); } // Matches anything that can convert to std::string. // // This is a template, not just a plain function with const std::string&, // because absl::string_view has some interfering non-explicit constructors. template bool MatchAndExplain(const MatcheeStringType& s, MatchResultListener* /* listener */) const { const std::string& s2(s); return full_match_ ? RE::FullMatch(s2, *regex_) : RE::PartialMatch(s2, *regex_); } void DescribeTo(::std::ostream* os) const { *os << (full_match_ ? "matches" : "contains") << " regular expression "; UniversalPrinter::Print(regex_->pattern(), os); } void DescribeNegationTo(::std::ostream* os) const { *os << "doesn't " << (full_match_ ? "match" : "contain") << " regular expression "; UniversalPrinter::Print(regex_->pattern(), os); } private: const internal::linked_ptr regex_; const bool full_match_; GTEST_DISALLOW_ASSIGN_(MatchesRegexMatcher); }; // Implements a matcher that compares the two fields of a 2-tuple // using one of the ==, <=, <, etc, operators. The two fields being // compared don't have to have the same type. // // The matcher defined here is polymorphic (for example, Eq() can be // used to match a tuple, a tuple, // etc). Therefore we use a template type conversion operator in the // implementation. template class PairMatchBase { public: template operator Matcher< ::testing::tuple >() const { return MakeMatcher(new Impl< ::testing::tuple >); } template operator Matcher&>() const { return MakeMatcher(new Impl&>); } private: static ::std::ostream& GetDesc(::std::ostream& os) { // NOLINT return os << D::Desc(); } template class Impl : public MatcherInterface { public: virtual bool MatchAndExplain( Tuple args, MatchResultListener* /* listener */) const { return Op()(::testing::get<0>(args), ::testing::get<1>(args)); } virtual void DescribeTo(::std::ostream* os) const { *os << "are " << GetDesc; } virtual void DescribeNegationTo(::std::ostream* os) const { *os << "aren't " << GetDesc; } }; }; class Eq2Matcher : public PairMatchBase { public: static const char* Desc() { return "an equal pair"; } }; class Ne2Matcher : public PairMatchBase { public: static const char* Desc() { return "an unequal pair"; } }; class Lt2Matcher : public PairMatchBase { public: static const char* Desc() { return "a pair where the first < the second"; } }; class Gt2Matcher : public PairMatchBase { public: static const char* Desc() { return "a pair where the first > the second"; } }; class Le2Matcher : public PairMatchBase { public: static const char* Desc() { return "a pair where the first <= the second"; } }; class Ge2Matcher : public PairMatchBase { public: static const char* Desc() { return "a pair where the first >= the second"; } }; // Implements the Not(...) matcher for a particular argument type T. // We do not nest it inside the NotMatcher class template, as that // will prevent different instantiations of NotMatcher from sharing // the same NotMatcherImpl class. template class NotMatcherImpl : public MatcherInterface { public: explicit NotMatcherImpl(const Matcher& matcher) : matcher_(matcher) {} virtual bool MatchAndExplain(GTEST_REFERENCE_TO_CONST_(T) x, MatchResultListener* listener) const { return !matcher_.MatchAndExplain(x, listener); } virtual void DescribeTo(::std::ostream* os) const { matcher_.DescribeNegationTo(os); } virtual void DescribeNegationTo(::std::ostream* os) const { matcher_.DescribeTo(os); } private: const Matcher matcher_; GTEST_DISALLOW_ASSIGN_(NotMatcherImpl); }; // Implements the Not(m) matcher, which matches a value that doesn't // match matcher m. template class NotMatcher { public: explicit NotMatcher(InnerMatcher matcher) : matcher_(matcher) {} // This template type conversion operator allows Not(m) to be used // to match any type m can match. template operator Matcher() const { return Matcher(new NotMatcherImpl(SafeMatcherCast(matcher_))); } private: InnerMatcher matcher_; GTEST_DISALLOW_ASSIGN_(NotMatcher); }; // Implements the AllOf(m1, m2) matcher for a particular argument type // T. We do not nest it inside the BothOfMatcher class template, as // that will prevent different instantiations of BothOfMatcher from // sharing the same BothOfMatcherImpl class. template class AllOfMatcherImpl : public MatcherInterface { public: explicit AllOfMatcherImpl(std::vector > matchers) : matchers_(internal::move(matchers)) {} virtual void DescribeTo(::std::ostream* os) const { *os << "("; for (size_t i = 0; i < matchers_.size(); ++i) { if (i != 0) *os << ") and ("; matchers_[i].DescribeTo(os); } *os << ")"; } virtual void DescribeNegationTo(::std::ostream* os) const { *os << "("; for (size_t i = 0; i < matchers_.size(); ++i) { if (i != 0) *os << ") or ("; matchers_[i].DescribeNegationTo(os); } *os << ")"; } virtual bool MatchAndExplain(GTEST_REFERENCE_TO_CONST_(T) x, MatchResultListener* listener) const { // If either matcher1_ or matcher2_ doesn't match x, we only need // to explain why one of them fails. std::string all_match_result; for (size_t i = 0; i < matchers_.size(); ++i) { StringMatchResultListener slistener; if (matchers_[i].MatchAndExplain(x, &slistener)) { if (all_match_result.empty()) { all_match_result = slistener.str(); } else { std::string result = slistener.str(); if (!result.empty()) { all_match_result += ", and "; all_match_result += result; } } } else { *listener << slistener.str(); return false; } } // Otherwise we need to explain why *both* of them match. *listener << all_match_result; return true; } private: const std::vector > matchers_; GTEST_DISALLOW_ASSIGN_(AllOfMatcherImpl); }; #if GTEST_LANG_CXX11 // VariadicMatcher is used for the variadic implementation of // AllOf(m_1, m_2, ...) and AnyOf(m_1, m_2, ...). // CombiningMatcher is used to recursively combine the provided matchers // (of type Args...). template