Qt for Tizen: Steps required to create Qt Creator SDK for Tizen
When developing a Tizen plugin for Qt Creator the main rule is to help yourself by analyzing existing plugins. Android and Madde are the platforms closest to Tizen thus it is best to base your plugin on them.
It is assumed that the user already has a working Qt version compiled for the Tizen platform and that he is able to build applications for Tizen by manually setting up appropriate kit in Qt Creator settings. It is also assumed he has Tizen-SDK installed or has otherwise obtained access to Tizen emulator and Smart Development Bridge (SDB) tool.
- be able to access Tizen runtime (aka 'device')
- be able to build applications for Tizen
- be able to deploy applications for Tizen
- be able to run/debug deployed applications from Tizen 'device' (or emulator)
First create a new project in Qt Creator and choose to create a new Creator plugin. The skeleton code generated for you will be the plugin class itself with a demo action added to the Tools menu. You can start development by deleting the demo action.
At this point you should update the pluginspec.in file for the plugin and make sure that Core, ProjectExplorer and Qt4ProjectManager are marked as dependencies of your plugin since the goal is to be able to manipulate qmake-based projects which is functionality delivered by Qt4ProjectManger plugin that handles both Qt4 and Qt5 projects.
To obtain objective 1 you need to prepare a subclass of ProjectExplorer::IDeviceFactory that will manage a new device type – Tizen devices. This is so that all further steps can check whether they are working on a Tizen-compliant target and can bail out otherwise. Basically this class needs to handle two pairs of situations – one for creating new devices (handled by reimplementing canCreate() and create() methods) and the other for restoring devices created in previous sessions of Qt Creator (handled by reimplementing canRestore() and restore()). create() and restore() should return instances of a subclass of ProjectExplorer::IDevice which is the second class that needs to be implemented. These two classes cooperate to set the device state, type, etc. – whether it is a real device or an emulator and how to access it using Smart Development Bridge tool. All settings are saved and loaded by reimplementing toMap() and fromMap() methods.
To obtain objective 2, you don't need to do anything apart from properly setting up kits in Qt Creator settings. It is possible to automate this from within the plugin but since it is additional work, it is suggested to ignore automatic setup and simply provide instructions to the user on how to do it manually, since it is then a one-time job.
To obtain objective 3, you need to implement deployment steps for: 1) creating a TPK bundle and 2) installing it in the emulator/device. For this another factory class needs to be implemented and this time you need to base it on ProjectExplorer::IBuildStepFactory. The factory is to return instances of ProjectExplorer::BuildStep subclasses. Since you need two – one for package creation, another for package deployment, you need to have two factory classes and two build step classes.
Package creation build step
Implementing build steps boils down to reimplementing the run() method that performs a number of actions and reports the result. TPK files are really ZIP archives with a predefined structure. The whole packaging process is described in Tizen Application Packaging Overview. The most important part is that application binary goes to the bin directory, application data goes to data directory and application manifest file goes to info directory.
Ideally the manifest should be created automatically when Creator detects the user wants to deploy the app in Tizen environment. The way to do that will be described later in this document. The manifest file is really an XML document. Its structure is presented in Tizen Application Packaging Overview. You can model the manifest editor after its counter part in Tizen SDK and embed it into Creator by making ProjectExplorer::BuildConfigWidget the base class of your editor and returning its instance from createConfigWidget(). Since the manifest is really an XML document, it is best to create and update it using QXmlStreamWriter.
To create a TPK package, you need to make your plugin do the following steps:
- create a temporary directory
- create bin, info, data, icons, lib, res, setting and share subdirectories in this directory
- copy the executable from build directory to bin with the exe extension appended to the application file name (so that <builddir>/myapp becomes bin/myapp.exe)
- copy application data to data
- copy manifest file to info
- optionally strip the binary to reduce its size
- invoke zip command on the temporary directory
Package deployment build step
Package deployment is easy. You just need to invoke sdb install passing it your package name. This will unpack all the files into appropriate places in the runtime and create launcher icon for the project. Since no special configuration is required here, for the build config widget you can return an instance of ProjectExplorer::SimpleBuildStepConfigWidget.
Both deployment steps can be created automatically for a target. For that the plugin needs to implement a ProjectExplorer::DeploymentConfiguration interface (and a factory for it). The configuration itself doesn't have to do anything special apart holding any configuration data that is needed (e.g. data needed to regenerate the manifest) and serialization methods for storing the data (fromMap and toMap). When creating the configuration, the factory class should attach both build steps defined earlier to the configuration object.
At this point the plugin can also generate the initial manifest for the project. To add the manifest file to the project, ProjectExplorer::ProjectExplorerPlugin::addExistingFiles() should be called. The caveat when calling this method is that it needs to be done only when the project file is already parsed by Creator (which happens only some time after the deployment configuration is created). To work around this, the plugin should connect to fileListChanged() signal of the project (accessible via target()->project()) and then perform the processing. The slot invoked from this signal should disconnect itself from the signal to avoid being called again and again when files are added to the project.
To obtain objective 4, you have to first make sure the emulator is running (if targeting an emulated device) and then launch the application via sdb shell. The former can be modeled after Android plugin. For the latter you need to implement subclasses of ProjectExplorer::RunControl and ProjectExplorer::RunConfiguration and their respective factory classes.
Running applications on Tizen
Tizen contains a special security layer that allows the system to control applications. This is done by not executing application binaries directly but rather launching a special proxy (installed automatically during sdb install) that then loads the application binary into its own memory space using dlopen, locates and executes a function in the application object called OspMain(int, char*). For that applications that are going to be launched this way need to comply to the following two requirements:
- Application is loadable into another process via dlopen() – this is achieved by passing the -pie -rdynamic options to the linker while building the application (e.g. via QMAKE_LFLAGS+=-pie -rdynamic).
- Application contains a OspMain symbol resolvable via dlsym() (extern "C") that behaves like a regular main function.
If needed for reference, code for the loader can be found in framework/osp/loader.git repository (file osp-ui-app-loader/uiapp_loader.c) on https://review.tizen.org/git.
The whole launching process can be repeated by calling the following command from within sdb shell:
$ launch_app <appId>.<appBinary>
where <appId> is the 10 characters-long application code and <appBinary> is the application name (without .exe). The current working directory of the application is set to the data subdirectory inside the application installation (which resides in /opt/usr/apps/AppId/)
Invoking the Smart Development Bridge
SDB tool is a part of Tizen SDK. It simplifies development tasks therefore it is recommended that the Creator plugin makes use of it. It is suggested that a dedicated class is implemented that calls SDB through QProcess synchronously or asynchronously depending on the task for easy calling from within different parts of the plugin. The class can be modeled after its Android counterpart.
The plugin should expose the following interfaces to QtCreator:
- → ProjectExplorer::IDeviceFactory
' → creates ProjectExplorer::IDevice * → holds device settings (e.g. arguments for sdb -d/-e)
- → ProjectExplorer::DeployConfigurationFactory
→ creates ProjectExplorer::DeployConfiguration * → holds manifest data and other info useful during whole deployment process * → manages the manifest file
- → ProjectExplorer::IBuildStepFactory
' → creates ProjectExplorer::BuildStep (for package creation) * → copies files into temporary directory and zips it into a tpk file
- → ProjectExplorer::IBuildStepFactory
→ creates ProjectExplorer::BuildStep (for package deployment) * → calls sdb install passing the tpk file created in previous step
- → ProjectExplorer::IRunConfigurationFactory (or directly Qt4ProjectManager::QmakeRunConfigurationFactory)
→ creates ProjectExplorer::RunConfiguration * → holds settings for running the application in emulator/device
- → ProjectExplorer::IRunControlFactory
→ creates ProjectExplorer::RunControl