Provide sensible defaults for QCoreApplication::applicationVersion

[ChangeLog][QtCore] QCoreApplication::applicationVersion now defaults to
an appropriate platform-specific value. On Windows, it defaults to the
PRODUCTVERSION parameter of the VERSIONINFO resource for classic desktop
apps, and the version attribute of the application package manifest for
Univeral Windows Platform apps. On Apple Platforms (macOS, iOS, tvOS,
watchOS), it defaults to the CFBundleVersion property of the information
property list (Info.plist) file. On Android, it defaults to the
android:versionName attribute of the AndroidManifest.xml manifest
element. On other platforms, the default remains an empty string.

Task-number: QTBUG-57715
Change-Id: I26f83dd00737e06f4321cf962aa5fab8398104ec
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Jake Petroules 2016-12-19 18:56:46 -08:00
parent 5865e582fd
commit 2b928ef6f9
8 changed files with 196 additions and 11 deletions

View File

@ -88,6 +88,8 @@ win32 {
SOURCES += kernel/qeventdispatcher_win.cpp
HEADERS += kernel/qeventdispatcher_win_p.h
}
!winrt: LIBS_PRIVATE += -lversion
}
winrt {

View File

@ -95,6 +95,11 @@
#endif
#endif // QT_NO_QOBJECT
#if defined(Q_OS_ANDROID)
# include <private/qjni_p.h>
# include <private/qjnihelpers_p.h>
#endif
#ifdef Q_OS_MAC
# include "qcore_mac_p.h"
#endif
@ -144,11 +149,13 @@ int QCoreApplicationPrivate::app_compile_version = 0x050000; //we don't know exa
bool QCoreApplicationPrivate::setuidAllowed = false;
#if !defined(Q_OS_WIN)
#ifdef Q_OS_MAC
QString QCoreApplicationPrivate::macMenuBarName()
#ifdef Q_OS_DARWIN
QString QCoreApplicationPrivate::infoDictionaryStringProperty(const QString &propertyName)
{
QString bundleName;
CFTypeRef string = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("CFBundleName"));
QCFString cfPropertyName = propertyName.toCFString();
CFTypeRef string = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
cfPropertyName);
if (string)
bundleName = QString::fromCFString(static_cast<CFStringRef>(string));
return bundleName;
@ -157,8 +164,8 @@ QString QCoreApplicationPrivate::macMenuBarName()
QString QCoreApplicationPrivate::appName() const
{
QString applicationName;
#ifdef Q_OS_MAC
applicationName = macMenuBarName();
#ifdef Q_OS_DARWIN
applicationName = infoDictionaryStringProperty(QStringLiteral("CFBundleName"));
#endif
if (applicationName.isEmpty() && argv[0]) {
char *p = strrchr(argv[0], '/');
@ -167,6 +174,34 @@ QString QCoreApplicationPrivate::appName() const
return applicationName;
}
QString QCoreApplicationPrivate::appVersion() const
{
QString applicationVersion;
#ifndef QT_BOOTSTRAPPED
# ifdef Q_OS_DARWIN
applicationVersion = infoDictionaryStringProperty(QStringLiteral("CFBundleVersion"));
# elif defined(Q_OS_ANDROID)
QJNIObjectPrivate context(QtAndroidPrivate::context());
if (context.isValid()) {
QJNIObjectPrivate pm = context.callObjectMethod(
"getPackageManager", "()Landroid/content/pm/PackageManager;");
QJNIObjectPrivate pn = context.callObjectMethod<jstring>("getPackageName");
if (pm.isValid() && pn.isValid()) {
QJNIObjectPrivate packageInfo = pm.callObjectMethod(
"getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;",
pn.object(), 0);
if (packageInfo.isValid()) {
QJNIObjectPrivate versionName = packageInfo.getObjectField(
"versionName", "Ljava/lang/String;");
if (versionName.isValid())
return versionName.toString();
}
}
}
# endif
#endif
return applicationVersion;
}
#endif
QString *QCoreApplicationPrivate::cachedApplicationFilePath = 0;
@ -332,6 +367,7 @@ uint QCoreApplicationPrivate::attribs =
struct QCoreApplicationData {
QCoreApplicationData() Q_DECL_NOTHROW {
applicationNameSet = false;
applicationVersionSet = false;
}
~QCoreApplicationData() {
#ifndef QT_NO_QOBJECT
@ -347,6 +383,7 @@ struct QCoreApplicationData {
QString application; // application name, initially from argv[0], can then be modified.
QString applicationVersion;
bool applicationNameSet; // true if setApplicationName was called
bool applicationVersionSet; // true if setApplicationVersion was called
#ifndef QT_NO_LIBRARY
QScopedPointer<QStringList> app_libpaths;
@ -742,10 +779,13 @@ void QCoreApplicationPrivate::init()
Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
QCoreApplication::self = q;
// Store app name (so it's still available after QCoreApplication is destroyed)
// Store app name/version (so they're still available after QCoreApplication is destroyed)
if (!coreappdata()->applicationNameSet)
coreappdata()->application = appName();
if (!coreappdata()->applicationVersionSet)
coreappdata()->applicationVersion = appVersion();
QLoggingRegistry::instance()->init();
#ifndef QT_NO_LIBRARY
@ -2403,6 +2443,29 @@ Q_CORE_EXPORT QString qt_applicationName_noFallback()
\since 4.4
\brief the version of this application
If not set, the application version defaults to a platform-specific value
determined from the main application executable or package (since Qt 5.9):
\table
\header
\li Platform
\li Source
\row
\li Windows (classic desktop)
\li PRODUCTVERSION parameter of the VERSIONINFO resource
\row
\li Universal Windows Platform
\li version attribute of the application package manifest
\row
\li macOS, iOS, tvOS, watchOS
\li CFBundleVersion property of the information property list
\row
\li Android
\li android:versionName property of the AndroidManifest.xml manifest element
\endtable
On other platforms, the default is the empty string.
\sa applicationName, organizationName, organizationDomain
*/
/*!
@ -2413,9 +2476,13 @@ Q_CORE_EXPORT QString qt_applicationName_noFallback()
*/
void QCoreApplication::setApplicationVersion(const QString &version)
{
if (coreappdata()->applicationVersion == version)
coreappdata()->applicationVersionSet = !version.isEmpty();
QString newVersion = version;
if (newVersion.isEmpty() && QCoreApplication::self)
newVersion = QCoreApplication::self->d_func()->appVersion();
if (coreappdata()->applicationVersion == newVersion)
return;
coreappdata()->applicationVersion = version;
coreappdata()->applicationVersion = newVersion;
#ifndef QT_NO_QOBJECT
if (QCoreApplication::self)
emit QCoreApplication::self->applicationVersionChanged();
@ -2424,7 +2491,7 @@ void QCoreApplication::setApplicationVersion(const QString &version)
QString QCoreApplication::applicationVersion()
{
return coreappdata()->applicationVersion;
return coreappdata() ? coreappdata()->applicationVersion : QString();
}
#ifndef QT_NO_LIBRARY

View File

@ -83,9 +83,10 @@ public:
void init();
QString appName() const;
QString appVersion() const;
#ifdef Q_OS_MAC
static QString macMenuBarName();
#ifdef Q_OS_DARWIN
static QString infoDictionaryStringProperty(const QString &propertyName);
#endif
static void initLocale();

View File

@ -51,6 +51,16 @@
#include <ctype.h>
#include <qt_windows.h>
#ifdef Q_OS_WINRT
#include <qfunctions_winrt.h>
#include <wrl.h>
#include <Windows.ApplicationModel.core.h>
#include <windows.foundation.h>
using namespace ABI::Windows::ApplicationModel;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
#endif
QT_BEGIN_NAMESPACE
int appCmdShow = 0;
@ -118,6 +128,64 @@ QString QCoreApplicationPrivate::appName() const
return QFileInfo(qAppFileName()).baseName();
}
QString QCoreApplicationPrivate::appVersion() const
{
QString applicationVersion;
#ifndef QT_BOOTSTRAPPED
# ifdef Q_OS_WINRT
HRESULT hr;
ComPtr<IPackageStatics> packageFactory;
hr = RoGetActivationFactory(
HString::MakeReference(RuntimeClass_Windows_ApplicationModel_Package).Get(),
IID_PPV_ARGS(&packageFactory));
RETURN_IF_FAILED("Failed to create package instance", return QString());
ComPtr<IPackage> package;
packageFactory->get_Current(&package);
RETURN_IF_FAILED("Failed to get current application package", return QString());
ComPtr<IPackageId> packageId;
package->get_Id(&packageId);
RETURN_IF_FAILED("Failed to get current application package ID", return QString());
PackageVersion version;
packageId->get_Version(&version);
RETURN_IF_FAILED("Failed to get current application package version", return QString());
applicationVersion = QStringLiteral("%1.%2.%3.%4")
.arg(version.Major)
.arg(version.Minor)
.arg(version.Build)
.arg(version.Revision);
# else
const QString appFileName = qAppFileName();
QVarLengthArray<wchar_t> buffer(appFileName.size() + 1);
buffer[appFileName.toWCharArray(buffer.data())] = 0;
DWORD versionInfoSize = GetFileVersionInfoSize(buffer.data(), nullptr);
if (versionInfoSize) {
QVarLengthArray<BYTE> info(static_cast<int>(versionInfoSize));
if (GetFileVersionInfo(buffer.data(), 0, versionInfoSize, info.data())) {
UINT size;
DWORD *fi;
if (VerQueryValue(info.data(), __TEXT("\\"),
reinterpret_cast<void **>(&fi), &size) && size) {
const VS_FIXEDFILEINFO *verInfo = reinterpret_cast<const VS_FIXEDFILEINFO *>(fi);
applicationVersion = QStringLiteral("%1.%2.%3.%4")
.arg(HIWORD(verInfo->dwProductVersionMS))
.arg(LOWORD(verInfo->dwProductVersionMS))
.arg(HIWORD(verInfo->dwProductVersionLS))
.arg(LOWORD(verInfo->dwProductVersionLS));
}
}
}
# endif
#endif
return applicationVersion;
}
#endif // !(defined(Q_OS_WINRT) && _MSC_VER < 1900)
#ifndef Q_OS_WINRT

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleVersion</key>
<string>1.2.3</string>
</dict>
</plist>

View File

@ -3,4 +3,7 @@ TARGET = tst_qcoreapplication
QT = core testlib core-private
SOURCES = tst_qcoreapplication.cpp
HEADERS = tst_qcoreapplication.h
win32: VERSION = 1.2.3.4
else: VERSION = 1.2.3
darwin: QMAKE_LFLAGS += -Wl,-sectcreate,__TEXT,__info_plist,$$shell_quote($$PWD/Info.plist)
requires(qtConfig(private_tests))

View File

@ -153,6 +153,41 @@ void tst_QCoreApplication::qAppName()
QCOMPARE(QCoreApplication::applicationName(), QString());
}
void tst_QCoreApplication::qAppVersion()
{
#if defined(Q_OS_WIN)
const char appVersion[] = "1.2.3.4";
#elif defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID)
const char appVersion[] = "1.2.3";
#else
const char appVersion[] = "";
#endif
{
int argc = 0;
char *argv[] = { nullptr };
TestApplication app(argc, argv);
QCOMPARE(QCoreApplication::applicationVersion(), QString::fromLatin1(appVersion));
}
// The application version should still be available after destruction
QCOMPARE(QCoreApplication::applicationVersion(), QString::fromLatin1(appVersion));
// Setting the appversion before creating the application should work
const QString wantedAppVersion("0.0.1");
{
int argc = 0;
char *argv[] = { nullptr };
QCoreApplication::setApplicationVersion(wantedAppVersion);
TestApplication app(argc, argv);
QCOMPARE(QCoreApplication::applicationVersion(), wantedAppVersion);
}
QCOMPARE(QCoreApplication::applicationVersion(), wantedAppVersion);
// Restore to initial value
QCoreApplication::setApplicationVersion(QString());
QCOMPARE(QCoreApplication::applicationVersion(), QString());
}
void tst_QCoreApplication::argc()
{
#if defined(Q_OS_WINRT)

View File

@ -39,6 +39,7 @@ private slots:
void sendEventsOnProcessEvents(); // this must be the first test
void getSetCheck();
void qAppName();
void qAppVersion();
void argc();
void postEvent();
void removePostedEvents();