diff --git a/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc b/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc index 85fdaf88840..89da6c92ae0 100644 --- a/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc +++ b/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc @@ -443,8 +443,8 @@ Performance Analyzer can read Perf data files generated in either frame pointer or dwarf mode. However, to generate the files correctly, numerous preconditions have to be met. All system images for the - \l{https://doc.qt.io/Boot2Qt/qtdc-supported-platforms.html} - {Boot2Qt:Supported Target Devices and Development Hosts} are correctly set + \l{Support Levels for Target Hardware}{supported embedded platforms} + are correctly set up for profiling in the dwarf mode. For other devices, check whether Perf can read back its own data in a sensible way by checking the output of \c {perf report} or \c {perf script} for the recorded Perf data files. diff --git a/doc/qtcreator/src/external-resources/external-resources.qdoc b/doc/qtcreator/src/external-resources/external-resources.qdoc index ea624ab1326..79122831560 100644 --- a/doc/qtcreator/src/external-resources/external-resources.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources.qdoc @@ -1,6 +1,14 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only +/*! + \externalpage https://doc.qt.io/Boot2Qt/index.html + \title Boot2Qt: Documentation +*/ +/*! + \externalpage https://doc.qt.io/Boot2Qt/b2qt-requirements-x11.html#setting-up-usb-access-to-embedded-devices + \title Boot2Qt: Setting Up USB Access to Embedded Devices +*/ /*! \externalpage https://doc.qt.io/qt/qtqml-index.html \title Qt Qml diff --git a/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc b/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc index 3f7499b5b5c..b8c2c6f9a47 100644 --- a/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc +++ b/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc @@ -47,8 +47,7 @@ \note On Ubuntu Linux, the development user account must have access to the plugged-in devices. To grant them access to the device via USB, create a new \c udev rule, as described in - \l{https://doc.qt.io/Boot2Qt/b2qt-requirements-x11.html#setting-up-usb-access-to-embedded-devices} - {Boot2Qt: Setting Up USB Access to Embedded Devices}. + \l{Boot2Qt: Setting Up USB Access to Embedded Devices}. You can edit the settings later in \preferences > \uicontrol Devices > \uicontrol Devices. @@ -149,4 +148,6 @@ application files to the device. For more information, see \l{Boot2Qt Run Settings}. \endlist + + \sa {Boot2Qt: Setting Up USB Access to Embedded Devices} */ diff --git a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc b/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc index 37c1c42e58a..0d3a7cd93a9 100644 --- a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc +++ b/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc @@ -46,17 +46,11 @@ tools to customize the contents of the stack and to take it into production hardware. - You need either Windows 10 64-bit or later or Ubuntu Linux 64-bit 20.04 LTS - or later to install and use Boot2Qt. - The following topics have more information about developing applications for Boot2Qt devices: \list - \li \l{https://doc.qt.io/Boot2Qt/qtdc-supported-platforms.html} - {Boot2Qt: Supported Target Devices and Development Hosts} - \li \l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html} - {Boot2Qt: Installation Guides} + \li \l{Boot2Qt: Documentation} \li \l{Connecting Boot2Qt Devices} \li \l{Boot2Qt Run Settings} \li \l{Boot2Qt Deploy Configuration} diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc index ac06f8cbd1e..4b13cbd630f 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc @@ -28,8 +28,7 @@ To run an example application on a Boot2Qt device, you must set up Boot2Qt on the development host and create connections between the host and devices. For more information, see - \l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html} - {Boot2Qt: Installation Guides} + \l{Boot2Qt: Documentation}. If you have \l{Qt Design Studio Manual}{\QDS} installed, you can open \QDS examples from \QC in \QDS. @@ -70,9 +69,8 @@ If build errors occur, check that you have a Qt version, a \l{Add compilers}{compiler}, and the necessary kits installed. If - you are building for an \l{Connecting Android Devices}{Android device} - or \l{Connecting iOS Devices}{iOS device}, check that you set up the - development environment correctly. + you are building for an Android or iOS device, check that you set up + the development environment correctly. The \uicontrol Build progress bar on the toolbar turns green when you build the project successfully. The application opens on the @@ -80,4 +78,7 @@ \endlist + \sa {Add compilers}, {Add kits}, {Add Qt versions}, + {Connecting Android Devices}, {Connecting iOS Devices}, + {Compile Output}, {Boot2Qt: Documentation}, {Qt Design Studio Manual} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc index 4d0e9038639..4654c4326fd 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc @@ -27,9 +27,7 @@ \list \li \l{Connecting Android Devices}{Android} \li \l{Connecting Bare Metal Devices}{Bare Metal} - \li \l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html} - {Boot2Qt} (commercial only) - \li \l{Emulator}{Boot2Qt Emulator} (commercial only) + \li \l{Boot2Qt: Documentation}{Boot2Q} (commercial only) \li \l{Adding Docker Devices}{Docker} (experimental) \li \l{Connecting iOS Devices}{iOS} \li iOS Simulator @@ -147,6 +145,9 @@ This setting is used to tell the code model which compiler is used. If your project type and build tool support it, \QC also tells the build tool to use this compiler for building the project. + + \note qmake ignores the value of this field and fetches the compiler + information from \uicontrol {Qt mkspec}, which you can change. \row \li \uicontrol Environment \li Select \uicontrol Change to modify environment variable values for diff --git a/doc/qtcreator/src/qtquick/qtquick-live-preview-devices.qdoc b/doc/qtcreator/src/qtquick/qtquick-live-preview-devices.qdoc index ae7a75eefea..7fc12b690f9 100644 --- a/doc/qtcreator/src/qtquick/qtquick-live-preview-devices.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-live-preview-devices.qdoc @@ -8,10 +8,10 @@ \title Previewing on Devices - To preview UIs on Android devices, you need to enable USB debugging on them + To preview UIs on Android devices, enable USB debugging on them and connect them to your system with a USB cable. - To preview UIs on Boot2Qt devices, you need to connect the devices to your + To preview UIs on Boot2Qt devices, connect the devices to your system with a USB cable, or a wired or wireless connection, depending on the device, and configure connections to them. The necessary kits have been predefined and you only need to enable them for your current project. @@ -20,8 +20,8 @@ necessary files to a location in a device where you want to run the executable at. - \note To preview on a wirelessly connected device, select \preferences > \uicontrol Devices - and connect the device. + \note To preview on a wirelessly connected device, go to \preferences > + \uicontrol Devices and connect the device. To preview a UI on a device: @@ -45,16 +45,16 @@ \section2 Previewing on Android Devices \if defined(qtcreator) - The USB debugging feature on Android devices enables creating connections - to the devices from \QDS and running the preview utility on them. + With the USB debugging feature on Android devices, you can create connections + to the devices from \QC and run the preview utility on them. - Debugging is enabled in different ways on different Android devices. + Debugging is turned on in different ways on different Android devices. Look for \uicontrol {USB Debugging} under \uicontrol {Developer Options}. - On some devices \uicontrol {Developer Options} is hidden and becomes visible + On some devices, \uicontrol {Developer Options} is hidden and becomes visible when you tap the \uicontrol {Build number} field in \uicontrol Settings > \uicontrol About several times. - After you have enabled debugging, connect the Android device to the system + After you turn on debugging, connect the Android device to the system with a USB cable. The first time you preview a UI on devices, the preview utility @@ -70,17 +70,8 @@ \section2 Previewing on Boot2Qt Devices - You can preview UIs on Boot2Qt devices. For a list of supported devices, see - \l{https://doc.qt.io/Boot2Qt/qtdc-supported-platforms.html} - {Boot2Qt: Supported Target Devices and Development Hosts}. + You can preview UIs on supported Boot2Qt devices that you configure as + instructed in the Boot2Qt documentation. - You must configure the device as instructed in the - \l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html} - {Boot2Qt: Installation Guides}. - - \note At the time of this writing, \macos is not supported as a development - host for Boot2Qt. This means that you cannot preview UIs on - devices if you are using \QC on \macos. For more information, see - \l {https://doc.qt.io/Boot2Qt/qtdc-supported-platforms.html#supported-development-hosts} - {Boot2Qt: Supported Development Hosts}. + \sa {Boot2Qt: Documentation}, {Support Levels for Target Hardware} */ diff --git a/share/qtcreator/android/sdk_definitions.json b/share/qtcreator/android/sdk_definitions.json index 22e4a322d84..ed1e42402f3 100644 --- a/share/qtcreator/android/sdk_definitions.json +++ b/share/qtcreator/android/sdk_definitions.json @@ -1,12 +1,12 @@ { "common": { "sdk_tools_url": { - "linux": "https://dl.google.com/android/repository/commandlinetools-linux-9123335_latest.zip", - "linux_sha256": "0bebf59339eaa534f4217f8aa0972d14dc49e7207be225511073c661ae01da0a", - "windows": "https://dl.google.com/android/repository/commandlinetools-win-9123335_latest.zip", - "windows_sha256": "8a90e6a3deb2fa13229b2e335efd07687dcc8a55a3c544da9f40b41404993e7d", - "mac": "https://dl.google.com/android/repository/commandlinetools-mac-9123335_latest.zip", - "mac_sha256": "d0192807f7e1cd4a001d13bb1e5904fc287b691211648877258aa44d1fa88275" + "linux": "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip", + "linux_sha256": "2d2d50857e4eb553af5a6dc3ad507a17adf43d115264b1afc116f95c92e5e258", + "windows": "https://dl.google.com/android/repository/commandlinetools-win-11076708_latest.zip", + "windows_sha256": "4d6931209eebb1bfb7c7e8b240a6a3cb3ab24479ea294f3539429574b1eec862", + "mac": "https://dl.google.com/android/repository/commandlinetools-mac-11076708_latest.zip", + "mac_sha256": "7bc5c72ba0275c80a8f19684fb92793b83a6b5c94d4d179fc5988930282d7e64" }, "sdk_essential_packages": { "default": ["platform-tools", "platforms;android-31", "cmdline-tools;latest"], diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index b399d86b36b..3f06e04fc1b 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -769,6 +769,7 @@ private: std::unique_ptr m_processHandler; mutable QMutex m_mutex; QList m_signals; + Guard m_guard; }; class ProcessPrivate : public QObject @@ -961,6 +962,10 @@ GeneralProcessBlockingImpl::GeneralProcessBlockingImpl(ProcessPrivate *parent) bool GeneralProcessBlockingImpl::waitForSignal(ProcessSignalType newSignal, QDeadlineTimer timeout) { + QTC_ASSERT(!m_guard.isLocked(), qWarning("Process::waitForSignal() called recursively. " + "The call is being ignored."); return false); + GuardLocker locker(m_guard); + m_processHandler->setParent(nullptr); QThread thread; diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index 1de5fc0c8ad..293094cbbe4 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -125,10 +125,8 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar bool assertionFound = false; proc.setStdOutCallback([offset, progressQuota, &proc, &assertionFound, &promise](const QString &out) { int progressPercent = parseProgress(out, assertionFound); - if (assertionFound) { + if (assertionFound) proc.stop(); - proc.waitForFinished(); - } if (progressPercent != -1) promise.setProgressValue(offset + qRound((progressPercent / 100.0) * progressQuota)); }); diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index cfe17d19d46..4134da09e7c 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -701,7 +701,7 @@ static void addCompileGroups(ProjectNode *targetRoot, node->setIsGenerated(true); const bool showSourceFolders = settings().showSourceSubFolders() - && sourcesOrHeadersFolder(td.sourceGroups[si.sourceGroup]); + && defaultCMakeSourceGroupFolder(td.sourceGroups[si.sourceGroup]); // Where does the file node need to go? if (showSourceFolders && sourcePath.isChildOf(buildDirectory) && !inSourceBuild) { @@ -715,7 +715,7 @@ static void addCompileGroups(ProjectNode *targetRoot, for (size_t i = 0; i < sourceGroupFileNodes.size(); ++i) { const bool showSourceFolders = settings().showSourceSubFolders() - && sourcesOrHeadersFolder(td.sourceGroups[i]); + && defaultCMakeSourceGroupFolder(td.sourceGroups[i]); std::vector> ¤t = sourceGroupFileNodes[i]; FolderNode *insertNode = td.sourceGroups[i] == "TREE" diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp index d23f6a4f972..c25cc69378f 100644 --- a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp +++ b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp @@ -18,9 +18,12 @@ using namespace ProjectExplorer; namespace CMakeProjectManager::Internal { -bool sourcesOrHeadersFolder(const QString &displayName) +bool defaultCMakeSourceGroupFolder(const QString &displayName) { - return displayName == "Source Files" || displayName == "Header Files"; + return displayName == "Source Files" || displayName == "Header Files" + || displayName == "Resources" || displayName == "" + || displayName == "Precompile Header File" || displayName == "CMake Rules" + || displayName == "Object Files"; } std::unique_ptr createCMakeVFolder(const Utils::FilePath &basePath, @@ -30,7 +33,7 @@ std::unique_ptr createCMakeVFolder(const Utils::FilePath &basePath, auto newFolder = std::make_unique(basePath); newFolder->setPriority(priority); newFolder->setDisplayName(displayName); - newFolder->setIsSourcesOrHeaders(sourcesOrHeadersFolder(displayName)); + newFolder->setIsSourcesOrHeaders(defaultCMakeSourceGroupFolder(displayName)); return newFolder; } diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.h b/src/plugins/cmakeprojectmanager/projecttreehelper.h index 258e0a63231..35f7cf16a43 100644 --- a/src/plugins/cmakeprojectmanager/projecttreehelper.h +++ b/src/plugins/cmakeprojectmanager/projecttreehelper.h @@ -11,7 +11,7 @@ namespace CMakeProjectManager::Internal { -bool sourcesOrHeadersFolder(const QString &displayName); +bool defaultCMakeSourceGroupFolder(const QString &displayName); std::unique_ptr createCMakeVFolder(const Utils::FilePath &basePath, int priority, diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp index 41bbef61bd3..d4f4361f78c 100644 --- a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp +++ b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp @@ -100,10 +100,10 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target) }; setSummaryText(); - connect(&m_cppAspect, &BaseAspect::changed, this, setSummaryText); - connect(&m_qmlAspect, &BaseAspect::changed, this, setSummaryText); - connect(&m_pythonAspect, &BaseAspect::changed, this, setSummaryText); - connect(&m_overrideStartupAspect, &BaseAspect::changed, this, setSummaryText); + connect(&m_cppAspect, &BaseAspect::changed, details, setSummaryText); + connect(&m_qmlAspect, &BaseAspect::changed, details, setSummaryText); + connect(&m_pythonAspect, &BaseAspect::changed, details, setSummaryText); + connect(&m_overrideStartupAspect, &BaseAspect::changed, details, setSummaryText); return details; }); diff --git a/src/plugins/projectexplorer/projectexplorersettings.cpp b/src/plugins/projectexplorer/projectexplorersettings.cpp index 54cbc57a8cc..850b9b8dc9c 100644 --- a/src/plugins/projectexplorer/projectexplorersettings.cpp +++ b/src/plugins/projectexplorer/projectexplorersettings.cpp @@ -53,7 +53,8 @@ const char ABORT_BUILD_ALL_ON_ERROR_SETTINGS_KEY[] = "ProjectExplorer/Settings/AbortBuildAllOnError"; const char LOW_BUILD_PRIORITY_SETTINGS_KEY[] = "ProjectExplorer/Settings/LowBuildPriority"; const char APP_ENV_CHANGES_SETTINGS_KEY[] = "ProjectExplorer/Settings/AppEnvChanges"; -const char WARN_AGAINST_NON_ASCII_BUILD_DIR_SETTINGS_KEY[] = "ProjectExplorer/Settings/LowBuildPriority"; +const char WARN_AGAINST_NON_ASCII_BUILD_DIR_SETTINGS_KEY[] + = "ProjectExplorer/Settings/WarnAgainstNonAsciiBuildDir"; } // Constants diff --git a/src/shared/qbs b/src/shared/qbs index 5c88b6b11b7..ca74c524363 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 5c88b6b11b762cf5861c9d1570df4f1f050c826e +Subproject commit ca74c524363d17c689bb0ec4ca39c744df8d036e diff --git a/tests/system/shared/utils.py b/tests/system/shared/utils.py index afb609292c3..cb2f6875f38 100644 --- a/tests/system/shared/utils.py +++ b/tests/system/shared/utils.py @@ -81,6 +81,14 @@ def selectFromCombo(objectSpec, itemName): mouseClick(waitForObjectItem(comboObject, itemName.replace(".", "\\."))) test.verify(waitFor("str(comboObject.currentText)==itemName", 5000), "Switched combo item to '%s'" % itemName) + def __collapsed__(): + try: + waitForObject("{container='%s' type='QModelIndex'}" % objectSpec, 100) + return False + except: + return True + + waitFor(__collapsed__, 1000) return True def selectFromLocator(filter, itemName = None): diff --git a/tests/system/suite_HELP/tst_HELP02/test.py b/tests/system/suite_HELP/tst_HELP02/test.py index 31a3e7c2de4..b6246e4f05e 100644 --- a/tests/system/suite_HELP/tst_HELP02/test.py +++ b/tests/system/suite_HELP/tst_HELP02/test.py @@ -53,10 +53,6 @@ def checkQtCreatorHelpVersion(expectedVersion): test.fail("Missing Qt Creator Manual.") -def _shortcutMatches_(shortcutEdit, expectedText): - return str(findObject(shortcutEdit).text) == expectedText - - def setKeyboardShortcutForAboutQtC(): invokeMenuItem("Edit", "Preferences...") mouseClick(waitForObjectItem(":Options_QListView", "Environment")) @@ -78,16 +74,16 @@ def setKeyboardShortcutForAboutQtC(): "placeholderText='Enter key sequence as text'}" % shortcutGB) clickButton(record) nativeType(keysToType) - waitFor("_shortcutMatches_(shortcut, expectedKeys)", 5000) + waitFor(lambda: str(findObject(shortcut).text) == expectedKeys, 5000) clickButton(record) - gotExpectedShortcut = _shortcutMatches_(shortcut, expectedKeys) - if not gotExpectedShortcut and platform.system() == 'Darwin': + foundShortcut = str(findObject(shortcut).text) + if foundShortcut != expectedKeys and platform.system() == 'Darwin': test.warning("Squish Issue: shortcut was set to %s - entering it manually now" % waitForObject(shortcut).text) replaceEditorContent(shortcut, expectedKeys) else: - test.verify(gotExpectedShortcut, "Expected key sequence is displayed.") + test.compare(foundShortcut, expectedKeys, "Expected key sequence is displayed?") clickButton(waitForObject(":Options.OK_QPushButton")) def main(): diff --git a/tests/system/suite_general/tst_create_proj_wizard/test.py b/tests/system/suite_general/tst_create_proj_wizard/test.py index 4da9d39d732..78178d82bb8 100644 --- a/tests/system/suite_general/tst_create_proj_wizard/test.py +++ b/tests/system/suite_general/tst_create_proj_wizard/test.py @@ -41,9 +41,6 @@ def main(): # needed because categoriesView and templatesView using same model for template in dumpItems(templatesView.model(), templatesView.rootIndex()): template = template.replace(".", "\\.") - # FIXME this needs Qt6.2+ - if template == "Qt Quick 2 Extension Plugin": - continue # skip non-configurable if template not in ["Qt Quick UI Prototype", "Qt Creator Plugin"]: availableProjectTypes.append({category:template}) @@ -119,6 +116,9 @@ def handleBuildSystemVerifyKits(category, template, kits, displayedPlatforms, if template == 'Qt Quick Application': fixedBuildSystems.remove('qmake') test.log("Skipped qmake (not supported).") + elif template == 'Qt Quick 2 Extension Plugin': + fixedBuildSystems.remove('Qbs') + test.log("Skipped Qbs (not supported).") for counter, buildSystem in enumerate(fixedBuildSystems): test.log("Using build system '%s'" % buildSystem) diff --git a/tests/system/suite_general/tst_openqt_creator/test.py b/tests/system/suite_general/tst_openqt_creator/test.py index c0cbc815a22..3c55342fc21 100644 --- a/tests/system/suite_general/tst_openqt_creator/test.py +++ b/tests/system/suite_general/tst_openqt_creator/test.py @@ -21,13 +21,13 @@ def main(): openQmakeProject(os.path.join(pathSpeedcrunch, "src", "speedcrunch.pro"), [Targets.DESKTOP_5_14_1_DEFAULT]) # Wait for parsing to complete - waitFor("runButton.enabled", 30000) + waitFor(lambda: runButton.enabled, 30000) # Starting before opening, because this is where Creator froze (QTCREATORBUG-10733) startopening = datetime.utcnow() openQmakeProject(pathCreator, [Targets.DESKTOP_5_14_1_DEFAULT]) # Wait for parsing to complete startreading = datetime.utcnow() - waitFor("runButton.enabled", 300000) + waitFor(lambda: runButton.enabled, 300000) secondsOpening = (datetime.utcnow() - startopening).seconds secondsReading = (datetime.utcnow() - startreading).seconds timeoutOpen = 45