Merge lp:~cjwatson/ubuntu-app-launch/libclick-manifest into lp:ubuntu-app-launch/14.04

Proposed by Colin Watson
Status: Merged
Approved by: Ted Gould
Approved revision: 139
Merged at revision: 141
Proposed branch: lp:~cjwatson/ubuntu-app-launch/libclick-manifest
Merge into: lp:ubuntu-app-launch/14.04
Prerequisite: lp:~cjwatson/ubuntu-app-launch/libclick-pkgdir
Diff against target: 417 lines (+83/-117)
9 files modified
CMakeLists.txt (+2/-2)
debian/control (+0/-2)
helpers.c (+33/-35)
libupstart-app-launch/CMakeLists.txt (+1/-0)
libupstart-app-launch/upstart-app-launch.c (+41/-58)
tests/click (+0/-7)
tests/exec-util-test.cc (+0/-4)
tests/helper-test.cc (+3/-4)
tests/libual-test.cc (+3/-5)
To merge this branch: bzr merge lp:~cjwatson/ubuntu-app-launch/libclick-manifest
Reviewer Review Type Date Requested Status
Ted Gould (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+210520@code.launchpad.net

Commit message

Use libclick to get package manifests, saving about 0.7 seconds from Click application startup (on mako).

Description of the change

This is the other half of the work started in https://code.launchpad.net/~cjwatson/upstart-app-launch/libclick-pkgdir/+merge/209909, now that click 0.4.18 has landed. It converts the remaining parts of upstart-app-launch so that it no longer needs to go through the click binary at all, and does everything click-related using libclick, thereby avoiding the need to start a Python interpreter.

I've tested this manually on mako after building on porter-armhf.c.c, and run some timings. Including previous results, the median of nine runs of "time upstart-app-launch com.ubuntu.calculator_calculator_0.1.3.224" (followed by "upstart-app-stop com.ubuntu.calculator_calculator_0.1.3.224" to clean up) behaves as follows:

  Before any libclick work: 1.772 seconds
  With libclick-pkgdir: 1.037 seconds
  With libclick-pkgdir and libclick-manifest: 0.355 seconds

The combined result of my two branches is therefore a total saving of about 1.4 seconds, or about 80% of upstart-app-launch's run-time.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ted Gould (ted) wrote :

Looks great! Excited about the slightly faster app startup ;-)

review: Approve
Revision history for this message
Charles Kerr (charlesk) wrote :

This looks great. Thanks Colin!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2014-03-12 00:34:52 +0000
+++ CMakeLists.txt 2014-03-12 00:34:52 +0000
@@ -52,7 +52,7 @@
52pkg_check_modules(ZEITGEIST REQUIRED zeitgeist-2.0)52pkg_check_modules(ZEITGEIST REQUIRED zeitgeist-2.0)
53include_directories(${ZEITGEIST_INCLUDE_DIRS})53include_directories(${ZEITGEIST_INCLUDE_DIRS})
5454
55pkg_check_modules(CLICK REQUIRED click-0.4)55pkg_check_modules(CLICK REQUIRED click-0.4>=0.4.18)
56include_directories(${CLICK_INCLUDE_DIRS})56include_directories(${CLICK_INCLUDE_DIRS})
5757
58pkg_check_modules(LIBUPSTART REQUIRED libupstart)58pkg_check_modules(LIBUPSTART REQUIRED libupstart)
@@ -76,7 +76,7 @@
76####################76####################
7777
78add_library(helpers STATIC helpers.c helpers-keyfile.c)78add_library(helpers STATIC helpers.c helpers-keyfile.c)
79target_link_libraries(helpers ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES})79target_link_libraries(helpers ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES} ${CLICK_LIBRARIES})
8080
81####################81####################
82# desktop-exec82# desktop-exec
8383
=== modified file 'debian/control'
--- debian/control 2014-03-12 00:34:52 +0000
+++ debian/control 2014-03-12 00:34:52 +0000
@@ -33,7 +33,6 @@
33Architecture: any33Architecture: any
34Depends: ${shlibs:Depends},34Depends: ${shlibs:Depends},
35 ${misc:Depends},35 ${misc:Depends},
36 click (>= 0.4.9),
37 click-apparmor,36 click-apparmor,
38 upstart (>= 1.11),37 upstart (>= 1.11),
39 zeitgeist,38 zeitgeist,
@@ -57,7 +56,6 @@
57Architecture: any56Architecture: any
58Depends: ${misc:Depends},57Depends: ${misc:Depends},
59 ${shlibs:Depends},58 ${shlibs:Depends},
60 click (>= 0.4.9),
61Pre-Depends: ${misc:Pre-Depends},59Pre-Depends: ${misc:Pre-Depends},
62Multi-Arch: same60Multi-Arch: same
63Description: library for sending requests to the upstart app launch61Description: library for sending requests to the upstart app launch
6462
=== modified file 'helpers.c'
--- helpers.c 2014-02-07 14:56:27 +0000
+++ helpers.c 2014-03-12 00:34:52 +0000
@@ -18,6 +18,7 @@
18 */18 */
1919
20#include <json-glib/json-glib.h>20#include <json-glib/json-glib.h>
21#include <click.h>
21#include <upstart.h>22#include <upstart.h>
22#include "helpers.h"23#include "helpers.h"
2324
@@ -62,62 +63,54 @@
62manifest_to_desktop (const gchar * app_dir, const gchar * app_id)63manifest_to_desktop (const gchar * app_dir, const gchar * app_id)
63{64{
64 gchar * package = NULL;65 gchar * package = NULL;
65 gchar * output = NULL;
66 gchar * application = NULL;66 gchar * application = NULL;
67 gchar * version = NULL;67 gchar * version = NULL;
68 JsonParser * parser = NULL;68 ClickDB * db = NULL;
69 GError * error = NULL;69 ClickUser * user = NULL;
70 JsonObject * manifest = NULL;
70 gchar * desktoppath = NULL;71 gchar * desktoppath = NULL;
72 GError * error = NULL;
7173
72 if (!app_id_to_triplet(app_id, &package, &application, &version)) {74 if (!app_id_to_triplet(app_id, &package, &application, &version)) {
73 g_warning("Unable to parse triplet: %s", app_id);75 g_warning("Unable to parse triplet: %s", app_id);
74 return NULL;76 return NULL;
75 }77 }
7678
77 gchar * cmdline = g_strdup_printf("click info \"%s\"", package);79 db = click_db_new();
78 g_spawn_command_line_sync(cmdline, &output, NULL, NULL, &error);80 /* If TEST_CLICK_DB is unset, this reads the system database. */
79 g_free(cmdline);81 click_db_read(db, g_getenv("TEST_CLICK_DB"), &error);
8082 if (error != NULL) {
83 g_warning("Unable to read Click database: %s", error->message);
84 goto manifest_out;
85 }
86 /* If TEST_CLICK_USER is unset, this uses the current user name. */
87 user = click_user_new_for_user(db, g_getenv("TEST_CLICK_USER"), &error);
88 if (error != NULL) {
89 g_warning("Unable to read Click database: %s", error->message);
90 goto manifest_out;
91 }
92 manifest = click_user_get_manifest(user, package, &error);
81 if (error != NULL) {93 if (error != NULL) {
82 g_warning("Unable to get manifest for '%s': %s", package, error->message);94 g_warning("Unable to get manifest for '%s': %s", package, error->message);
83 g_error_free(error);95 goto manifest_out;
84 goto manifest_out;96 }
85 }97
8698 if (!json_object_has_member(manifest, "version")) {
87 parser = json_parser_new();
88
89 json_parser_load_from_data(parser, output, -1, &error);
90 g_free(output);
91
92 if (error != NULL) {
93 g_warning("Unable to load manifest data '%s': %s", package, error->message);
94 g_error_free(error);
95 goto manifest_out;
96 }
97
98 JsonNode * root = json_parser_get_root(parser);
99 if (json_node_get_node_type(root) != JSON_NODE_OBJECT) {
100 g_warning("Manifest '%s' doesn't start with an object", package);
101 goto manifest_out;
102 }
103
104 JsonObject * rootobj = json_node_get_object(root);
105 if (!json_object_has_member(rootobj, "version")) {
106 g_warning("Manifest '%s' doesn't have a version", package);99 g_warning("Manifest '%s' doesn't have a version", package);
107 goto manifest_out;100 goto manifest_out;
108 }101 }
109102
110 if (g_strcmp0(json_object_get_string_member(rootobj, "version"), version) != 0) {103 if (g_strcmp0(json_object_get_string_member(manifest, "version"), version) != 0) {
111 g_warning("Manifest '%s' version '%s' doesn't match AppID version '%s'", package, json_object_get_string_member(rootobj, "version"), version);104 g_warning("Manifest '%s' version '%s' doesn't match AppID version '%s'", package, json_object_get_string_member(manifest, "version"), version);
112 goto manifest_out;105 goto manifest_out;
113 }106 }
114107
115 if (!json_object_has_member(rootobj, "hooks")) {108 if (!json_object_has_member(manifest, "hooks")) {
116 g_warning("Manifest '%s' doesn't have an hooks section", package);109 g_warning("Manifest '%s' doesn't have an hooks section", package);
117 goto manifest_out;110 goto manifest_out;
118 }111 }
119112
120 JsonObject * appsobj = json_object_get_object_member(rootobj, "hooks");113 JsonObject * appsobj = json_object_get_object_member(manifest, "hooks");
121 if (appsobj == NULL) {114 if (appsobj == NULL) {
122 g_warning("Manifest '%s' has an hooks section that is not a JSON object", package);115 g_warning("Manifest '%s' has an hooks section that is not a JSON object", package);
123 goto manifest_out;116 goto manifest_out;
@@ -151,7 +144,12 @@
151 }144 }
152145
153manifest_out:146manifest_out:
154 g_clear_object(&parser);147 if (error != NULL)
148 g_error_free(error);
149 if (manifest != NULL)
150 json_object_unref(manifest);
151 g_clear_object(&user);
152 g_clear_object(&db);
155 g_free(package);153 g_free(package);
156 g_free(application);154 g_free(application);
157 g_free(version);155 g_free(version);
158156
=== modified file 'libupstart-app-launch/CMakeLists.txt'
--- libupstart-app-launch/CMakeLists.txt 2014-02-11 03:21:25 +0000
+++ libupstart-app-launch/CMakeLists.txt 2014-03-12 00:34:52 +0000
@@ -44,6 +44,7 @@
44 ${GIO2_LIBRARIES}44 ${GIO2_LIBRARIES}
45 ${LTTNG_LIBRARIES}45 ${LTTNG_LIBRARIES}
46 ${JSONGLIB_LIBRARIES}46 ${JSONGLIB_LIBRARIES}
47 ${CLICK_LIBRARIES}
47 -Wl,--no-undefined48 -Wl,--no-undefined
48)49)
4950
5051
=== modified file 'libupstart-app-launch/upstart-app-launch.c'
--- libupstart-app-launch/upstart-app-launch.c 2014-02-19 21:43:04 +0000
+++ libupstart-app-launch/upstart-app-launch.c 2014-03-12 00:34:52 +0000
@@ -19,6 +19,7 @@
1919
20#include "upstart-app-launch.h"20#include "upstart-app-launch.h"
21#include <json-glib/json-glib.h>21#include <json-glib/json-glib.h>
22#include <click.h>
22#include <upstart.h>23#include <upstart.h>
23#include <gio/gio.h>24#include <gio/gio.h>
24#include <string.h>25#include <string.h>
@@ -1092,60 +1093,46 @@
1092 return TRUE;1093 return TRUE;
1093}1094}
10941095
1095/* Try and get a manifest file and do a couple sanity checks on it */1096/* Try and get a manifest and do a couple sanity checks on it */
1096static JsonParser *1097static JsonObject *
1097get_manifest_file (const gchar * pkg)1098get_manifest (const gchar * pkg)
1098{1099{
1099 /* Get the directory from click */1100 /* Get the directory from click */
1100 GError * error = NULL;1101 GError * error = NULL;
1101 const gchar * click_exec = NULL;1102
11021103 ClickDB * db = click_db_new();
1103 if (g_getenv("UAL_CLICK_EXEC") != NULL) {1104 /* If TEST_CLICK_DB is unset, this reads the system database. */
1104 click_exec = g_getenv("UAL_CLICK_EXEC");1105 click_db_read(db, g_getenv("TEST_CLICK_DB"), &error);
1105 } else {1106 if (error != NULL) {
1106 click_exec = "click";1107 g_warning("Unable to read Click database: %s", error->message);
1107 }1108 g_error_free(error);
11081109 return NULL;
1109 gchar * cmdline = g_strdup_printf("%s info \"%s\"",1110 }
1110 click_exec, pkg);1111 /* If TEST_CLICK_USER is unset, this uses the current user name. */
11111112 ClickUser * user = click_user_new_for_user(db, g_getenv("TEST_CLICK_USER"), &error);
1112 gchar * output = NULL;1113 if (error != NULL) {
1113 g_spawn_command_line_sync(cmdline, &output, NULL, NULL, &error);1114 g_warning("Unable to read Click database: %s", error->message);
1114 g_free(cmdline);1115 g_error_free(error);
11151116 g_object_unref(db);
1117 return NULL;
1118 }
1119 g_object_unref(db);
1120 JsonObject * manifest = click_user_get_manifest(user, pkg, &error);
1116 if (error != NULL) {1121 if (error != NULL) {
1117 g_warning("Unable to get manifest for '%s' package: %s", pkg, error->message);1122 g_warning("Unable to get manifest for '%s' package: %s", pkg, error->message);
1118 g_error_free(error);1123 g_error_free(error);
1119 g_free(output);1124 g_object_unref(user);
1120 return NULL;1125 return NULL;
1121 }1126 }
11221127 g_object_unref(user);
1123 /* Let's look at that manifest file */1128
1124 JsonParser * parser = json_parser_new();1129 if (!json_object_has_member(manifest, "version")) {
1125 json_parser_load_from_data(parser, output, -1, &error);
1126 g_free(output);
1127
1128 if (error != NULL) {
1129 g_warning("Unable to load manifest for '%s': %s", pkg, error->message);
1130 g_error_free(error);
1131 g_object_unref(parser);
1132 return NULL;
1133 }
1134 JsonNode * root = json_parser_get_root(parser);
1135 if (json_node_get_node_type(root) != JSON_NODE_OBJECT) {
1136 g_warning("Manifest file for package '%s' does not have an object as its root node", pkg);
1137 g_object_unref(parser);
1138 return NULL;
1139 }
1140
1141 JsonObject * rootobj = json_node_get_object(root);
1142 if (!json_object_has_member(rootobj, "version")) {
1143 g_warning("Manifest file for package '%s' does not have a version", pkg);1130 g_warning("Manifest file for package '%s' does not have a version", pkg);
1144 g_object_unref(parser);1131 json_object_unref(manifest);
1145 return NULL;1132 return NULL;
1146 }1133 }
11471134
1148 return parser;1135 return manifest;
1149}1136}
11501137
1151/* Types of search we can do for an app name */1138/* Types of search we can do for an app name */
@@ -1158,7 +1145,7 @@
11581145
1159/* Figure out the app name if it's one of the keywords */1146/* Figure out the app name if it's one of the keywords */
1160static const gchar *1147static const gchar *
1161manifest_app_name (JsonParser ** manifest, const gchar * pkg, const gchar * original_app)1148manifest_app_name (JsonObject ** manifest, const gchar * pkg, const gchar * original_app)
1162{1149{
1163 app_name_t app_type = APP_NAME_FIRST;1150 app_name_t app_type = APP_NAME_FIRST;
11641151
@@ -1175,12 +1162,10 @@
1175 }1162 }
11761163
1177 if (*manifest == NULL) {1164 if (*manifest == NULL) {
1178 *manifest = get_manifest_file(pkg);1165 *manifest = get_manifest(pkg);
1179 }1166 }
11801167
1181 JsonNode * root_node = json_parser_get_root(*manifest);1168 JsonObject * hooks = json_object_get_object_member(*manifest, "hooks");
1182 JsonObject * root_obj = json_node_get_object(root_node);
1183 JsonObject * hooks = json_object_get_object_member(root_obj, "hooks");
11841169
1185 if (hooks == NULL) {1170 if (hooks == NULL) {
1186 return NULL;1171 return NULL;
@@ -1216,20 +1201,17 @@
12161201
1217/* Figure out the app version using the manifest */1202/* Figure out the app version using the manifest */
1218static const gchar *1203static const gchar *
1219manifest_version (JsonParser ** manifest, const gchar * pkg, const gchar * original_ver)1204manifest_version (JsonObject ** manifest, const gchar * pkg, const gchar * original_ver)
1220{1205{
1221 if (original_ver != NULL && g_strcmp0(original_ver, "current-user-version") != 0) {1206 if (original_ver != NULL && g_strcmp0(original_ver, "current-user-version") != 0) {
1222 return original_ver;1207 return original_ver;
1223 } else {1208 } else {
1224 if (*manifest == NULL) {1209 if (*manifest == NULL) {
1225 *manifest = get_manifest_file(pkg);1210 *manifest = get_manifest(pkg);
1226 }1211 }
1227 g_return_val_if_fail(*manifest != NULL, NULL);1212 g_return_val_if_fail(*manifest != NULL, NULL);
12281213
1229 JsonNode * node = json_parser_get_root(*manifest);1214 return g_strdup(json_object_get_string_member(*manifest, "version"));
1230 JsonObject * obj = json_node_get_object(node);
1231
1232 return g_strdup(json_object_get_string_member(obj, "version"));
1233 }1215 }
12341216
1235 return NULL;1217 return NULL;
@@ -1242,7 +1224,7 @@
12421224
1243 const gchar * version = NULL;1225 const gchar * version = NULL;
1244 const gchar * application = NULL;1226 const gchar * application = NULL;
1245 JsonParser * manifest = NULL;1227 JsonObject * manifest = NULL;
12461228
1247 version = manifest_version(&manifest, pkg, ver);1229 version = manifest_version(&manifest, pkg, ver);
1248 g_return_val_if_fail(version != NULL, NULL);1230 g_return_val_if_fail(version != NULL, NULL);
@@ -1252,8 +1234,9 @@
12521234
1253 gchar * retval = g_strdup_printf("%s_%s_%s", pkg, application, version);1235 gchar * retval = g_strdup_printf("%s_%s_%s", pkg, application, version);
12541236
1255 /* The parser may hold allocation for some of our strings used above */1237 /* The object may hold allocation for some of our strings used above */
1256 g_clear_object(&manifest);1238 if (manifest)
1239 json_object_unref(manifest);
12571240
1258 return retval;1241 return retval;
1259}1242}
12601243
=== removed file 'tests/click'
--- tests/click 2014-03-12 00:34:52 +0000
+++ tests/click 1970-01-01 00:00:00 +0000
@@ -1,7 +0,0 @@
1#!/bin/bash
2
3if [ ${1} == "info" ] ; then
4 /bin/cat "${XDG_DATA_DIRS}/click-app-dir/.click/info/${2}.manifest"
5else
6 exit 1
7fi
80
=== modified file 'tests/exec-util-test.cc'
--- tests/exec-util-test.cc 2014-03-12 00:34:52 +0000
+++ tests/exec-util-test.cc 2014-03-12 00:34:52 +0000
@@ -37,10 +37,6 @@
37 virtual void SetUp() {37 virtual void SetUp() {
38 g_setenv("UPSTART_JOB", "made-up-job", TRUE);38 g_setenv("UPSTART_JOB", "made-up-job", TRUE);
39 g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE);39 g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE);
40 const gchar * oldpath = g_getenv("PATH");
41 gchar * newpath = g_strjoin(":", CMAKE_SOURCE_DIR, oldpath, NULL);
42 g_setenv("PATH", newpath, TRUE);
43 g_free(newpath);
4440
45 service = dbus_test_service_new(NULL);41 service = dbus_test_service_new(NULL);
4642
4743
=== modified file 'tests/helper-test.cc'
--- tests/helper-test.cc 2013-12-12 22:14:42 +0000
+++ tests/helper-test.cc 2014-03-12 00:34:52 +0000
@@ -33,10 +33,6 @@
33 protected:33 protected:
34 virtual void SetUp() {34 virtual void SetUp() {
35 g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE);35 g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE);
36 const gchar * oldpath = g_getenv("PATH");
37 gchar * newpath = g_strjoin(":", CMAKE_SOURCE_DIR, oldpath, NULL);
38 g_setenv("PATH", newpath, TRUE);
39 g_free(newpath);
40 g_setenv("DATA_WRITE_DIR", CMAKE_BINARY_DIR, TRUE);36 g_setenv("DATA_WRITE_DIR", CMAKE_BINARY_DIR, TRUE);
41 g_setenv("UPSTART_JOB", "made-up-job", TRUE);37 g_setenv("UPSTART_JOB", "made-up-job", TRUE);
42 return;38 return;
@@ -422,6 +418,9 @@
422{418{
423 gchar * desktop = NULL;419 gchar * desktop = NULL;
424420
421 g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE);
422 g_setenv("TEST_CLICK_USER", "test-user", TRUE);
423
425 desktop = manifest_to_desktop(CMAKE_SOURCE_DIR "/click-app-dir/", "com.test.good_application_1.2.3");424 desktop = manifest_to_desktop(CMAKE_SOURCE_DIR "/click-app-dir/", "com.test.good_application_1.2.3");
426 ASSERT_STREQ(desktop, CMAKE_SOURCE_DIR "/click-app-dir/application.desktop");425 ASSERT_STREQ(desktop, CMAKE_SOURCE_DIR "/click-app-dir/application.desktop");
427 g_free(desktop);426 g_free(desktop);
428427
=== modified file 'tests/libual-test.cc'
--- tests/libual-test.cc 2014-02-19 21:43:04 +0000
+++ tests/libual-test.cc 2014-03-12 00:34:52 +0000
@@ -77,11 +77,6 @@
7777
78 service = dbus_test_service_new(NULL);78 service = dbus_test_service_new(NULL);
7979
80 const gchar * oldpath = g_getenv("PATH");
81 gchar * newpath = g_strjoin(":", CMAKE_SOURCE_DIR, oldpath, NULL);
82 g_setenv("PATH", newpath, TRUE);
83 g_free(newpath);
84
85 debugConnection();80 debugConnection();
8681
87 mock = dbus_test_dbus_mock_new("com.ubuntu.Upstart");82 mock = dbus_test_dbus_mock_new("com.ubuntu.Upstart");
@@ -416,6 +411,9 @@
416411
417TEST_F(LibUAL, ApplicationId)412TEST_F(LibUAL, ApplicationId)
418{413{
414 g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE);
415 g_setenv("TEST_CLICK_USER", "test-user", TRUE);
416
419 /* Test with current-user-version, should return the version in the manifest */417 /* Test with current-user-version, should return the version in the manifest */
420 EXPECT_STREQ("com.test.good_application_1.2.3", upstart_app_launch_triplet_to_app_id("com.test.good", "application", "current-user-version"));418 EXPECT_STREQ("com.test.good_application_1.2.3", upstart_app_launch_triplet_to_app_id("com.test.good", "application", "current-user-version"));
421419

Subscribers

People subscribed via source and target branches