iOS: Remove need for separate qtiosmain library

We can combine the hybrid and non-hybrid use-cases into a single static
library if we are careful about which symbols are included in which
object files. By limiting the main() and qt_user_main() functions to
their own translation units, the linker will only pick them up if they
are missing at link time (the user's program do not provide them).

This technique is resilient to the -ObjC linker flag, which includes all
object files that implement an ObjectiveC class or category, but will
fail if the -all_load flag is passed to the linker, as we'll then have
duplicate symbols for either main() or qt_user_main(). The latter should
not happen unless the user provides the flag manually, and in the case
he or she does, there's ways to work around it by providing less global
flags such as -ObjC or -force_load.

Change-Id: Ie2f8e10a7265d007bf45cb1dd83f19cff0693551
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@digia.com>
This commit is contained in:
Tor Arne Vestbø 2013-06-06 13:48:36 +02:00 committed by The Qt Project
parent e99fc91c58
commit 5c8aa27111
7 changed files with 153 additions and 100 deletions

View File

@ -28,14 +28,8 @@ gui_app {
# need to generate an import for it.
CONFIG -= import_qpa_plugin
!no_main_wrapper {
# We link the iosmain library manually as well, since it's not really a plugin
lib_name = qiosmain
lib_path_and_base = $$[QT_INSTALL_PLUGINS/get]/platforms/lib$${lib_name}$$qtPlatformTargetSuffix()
LIBS += -L$$[QT_INSTALL_PLUGINS/get]/platforms -l$${lib_name}$$qtPlatformTargetSuffix()
LIBS += $$fromfile($${lib_path_and_base}.prl, QMAKE_PRL_LIBS)
DEFINES += main=qt_main
}
# FIXME: Solve using 'ld -r -alias -unexported_symbol' instead
!no_main_wrapper: DEFINES += main=qt_user_main
}
contains(MAKEFILE_GENERATOR, XCODE) {

View File

@ -1,3 +1,38 @@
TEMPLATE = subdirs
TARGET = qios
SUBDIRS += plugin.pro qtmain.pro
PLUGIN_TYPE = platforms
load(qt_plugin)
QT += core-private gui-private platformsupport-private
LIBS += -framework Foundation -framework UIKit -framework QuartzCore
OBJECTIVE_SOURCES = \
plugin.mm \
qiosmain_wrapper.mm \
qiosmain_dummy.mm \
qiosintegration.mm \
qioswindow.mm \
qiosscreen.mm \
qioseventdispatcher.mm \
qiosbackingstore.mm \
qiosapplicationdelegate.mm \
qiosviewcontroller.mm \
qioscontext.mm \
qiosinputcontext.mm \
qiostheme.mm \
qiosglobal.mm
HEADERS = \
qiosintegration.h \
qioswindow.h \
qiosscreen.h \
qioseventdispatcher.h \
qiosbackingstore.h \
qiosapplicationdelegate.h \
qiosviewcontroller.h \
qioscontext.h \
qiosinputcontext.h \
qiostheme.h \
qiosglobal.h
#HEADERS = qiossoftwareinputhandler.h

View File

@ -1,36 +0,0 @@
TARGET = qios
PLUGIN_TYPE = platforms
load(qt_plugin)
QT += core-private gui-private platformsupport-private
LIBS += -framework Foundation -framework UIKit -framework QuartzCore
OBJECTIVE_SOURCES = \
plugin.mm \
qiosintegration.mm \
qioswindow.mm \
qiosscreen.mm \
qioseventdispatcher.mm \
qiosbackingstore.mm \
qiosapplicationdelegate.mm \
qiosviewcontroller.mm \
qioscontext.mm \
qiosinputcontext.mm \
qiostheme.mm \
qiosglobal.mm
HEADERS = \
qiosintegration.h \
qioswindow.h \
qiosscreen.h \
qioseventdispatcher.h \
qiosbackingstore.h \
qiosapplicationdelegate.h \
qiosviewcontroller.h \
qioscontext.h \
qiosinputcontext.h \
qiostheme.h \
qiosglobal.h
#HEADERS = qiossoftwareinputhandler.h

View File

@ -39,8 +39,11 @@
**
****************************************************************************/
#import "qiosapplicationdelegate.h"
#include "qiosapplicationdelegate.h"
#include "qiosviewcontroller.h"
#include "qioswindow.h"
#include <QtCore/QtCore>
@implementation QIOSApplicationDelegate
@ -93,4 +96,47 @@
@end
extern int qt_user_main(int argc, char *argv[]);
@implementation QIOSMainWrapperApplicationDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.qiosViewController = [[[QIOSViewController alloc] init] autorelease];
self.window.rootViewController = self.qiosViewController;
#ifdef QT_DEBUG
self.window.backgroundColor = [UIColor cyanColor];
#endif
[self.window makeKeyAndVisible];
// We schedule the main-redirection for the next eventloop pass so that we
// can return from this function and let UIApplicationMain finish its job.
[NSTimer scheduledTimerWithTimeInterval:.01f target:self
selector:@selector(runUserMain) userInfo:nil repeats:NO];
if ([QIOSApplicationDelegate instancesRespondToSelector:_cmd])
return [super application:application didFinishLaunchingWithOptions:launchOptions];
else
return YES;
}
- (void)runUserMain
{
NSArray *arguments = [[NSProcessInfo processInfo] arguments];
int argc = arguments.count;
char **argv = new char*[argc];
for (int i = 0; i < argc; ++i) {
NSString *arg = [arguments objectAtIndex:i];
argv[i] = reinterpret_cast<char *>(malloc([arg lengthOfBytesUsingEncoding:[NSString defaultCStringEncoding]]));
strcpy(argv[i], [arg cStringUsingEncoding:[NSString defaultCStringEncoding]]);
}
qt_user_main(argc, argv);
delete[] argv;
}
@end

View File

@ -0,0 +1,56 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins 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 Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore/qglobal.h>
/*
This file provides a dummy implementation of qt_user_main, so that
we don't get an undefined symbol in the hybrid use-case, where we
don't rename main() to qt_user_main(). As long as the linker is not
passed -all_load, this translation unit is only picked up and used
if qt_user_main is not defined by the user's code.
*/
int qt_user_main(int, char **)
{
qFatal("Hit dummy qt_user_main, this should never happen!");
return 0;
}

View File

@ -40,7 +40,17 @@
****************************************************************************/
#include "qiosapplicationdelegate.h"
#include "qiosviewcontroller.h"
/*
This file provides a wrapper implementation of main() for the non-
hybrid use-case. The user's main is renamed to qt_user_main by the
build rules, and we'll call out to that main at the appropriate time.
This file purposly only exports a single symbol, _main, so that
when the linker considers the translation unit for inclusion it
will discard it when main has already been defined in the user's
application for the hybrid use-case.
*/
int main(int argc, char *argv[])
{
@ -48,47 +58,3 @@ int main(int argc, char *argv[])
return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSMainWrapperApplicationDelegate class]));
}
}
extern int qt_main(int argc, char *argv[]);
@implementation QIOSMainWrapperApplicationDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.qiosViewController = [[[QIOSViewController alloc] init] autorelease];
self.window.rootViewController = self.qiosViewController;
#ifdef QT_DEBUG
self.window.backgroundColor = [UIColor cyanColor];
#endif
[self.window makeKeyAndVisible];
// We schedule the main-redirection for the next eventloop pass so that we
// can return from this function and let UIApplicationMain finish its job.
[NSTimer scheduledTimerWithTimeInterval:.01f target:self
selector:@selector(runUserMain) userInfo:nil repeats:NO];
if ([QIOSApplicationDelegate instancesRespondToSelector:_cmd])
return [super application:application didFinishLaunchingWithOptions:launchOptions];
else
return YES;
}
- (void)runUserMain
{
NSArray *arguments = [[NSProcessInfo processInfo] arguments];
int argc = arguments.count;
char **argv = new char*[argc];
for (int i = 0; i < argc; ++i) {
NSString *arg = [arguments objectAtIndex:i];
argv[i] = reinterpret_cast<char *>(malloc([arg lengthOfBytesUsingEncoding:[NSString defaultCStringEncoding]]));
strcpy(argv[i], [arg cStringUsingEncoding:[NSString defaultCStringEncoding]]);
}
qt_main(argc, argv);
delete[] argv;
}
@end

View File

@ -1,8 +0,0 @@
TARGET = qiosmain
PLUGIN_TYPE = platforms
load(qt_plugin)
QT += gui-private
OBJECTIVE_SOURCES = qtmain.mm