Merge lp:click into lp:~ubuntu-managed-branches/click/click

Proposed by Michael Vogt
Status: Merged
Merged at revision: 351
Proposed branch: lp:click
Merge into: lp:~ubuntu-managed-branches/click/click
Diff against target: 485 lines (+269/-70)
11 files modified
README (+1/-0)
click/build.py (+10/-22)
click/framework.py (+138/-0)
click/install.py (+8/-28)
click/paths.py.in (+1/-0)
click/tests/helpers.py (+29/-0)
click/tests/test_build.py (+39/-5)
click/tests/test_install.py (+2/-4)
debian/changelog (+8/-0)
doc/file-format.rst (+1/-2)
doc/index.rst (+32/-9)
To merge this branch: bzr merge lp:click
Reviewer Review Type Date Requested Status
Colin Watson Approve
Ubuntu CI managed package branches Pending
Review via email: mp+219244@code.launchpad.net

Commit message

Support for declaring dependencies on multiple frameworks in a click package; Small documentation updates

Description of the change

* Add support for declaring dependencies on multiple frameworks in a click package (LP: #1318757)
* Small documentation updates

To post a comment you must log in.
lp:click updated
423. By Colin Watson

note closure of LP: #1318757

Revision history for this message
Colin Watson (cjwatson) :
review: Approve
lp:click updated
424. By Colin Watson

merge lp:~mvo/click/avoid-exposing-envvars

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'README'
--- README 1970-01-01 00:00:00 +0000
+++ README 2014-05-13 21:05:36 +0000
@@ -0,0 +1,1 @@
1See doc/index.rst to get started.
02
=== modified file 'click/build.py'
--- click/build.py 2014-01-23 17:19:09 +0000
+++ click/build.py 2014-05-13 21:05:36 +0000
@@ -50,6 +50,11 @@
50from click.preinst import static_preinst50from click.preinst import static_preinst
51from click.versions import spec_version51from click.versions import spec_version
5252
53from click.framework import (
54 validate_framework,
55 ClickFrameworkInvalid,
56)
57
5358
54@contextlib.contextmanager59@contextlib.contextmanager
55def make_temp_dir():60def make_temp_dir():
@@ -196,30 +201,13 @@
196 package.add_file("control.tar.gz", control_tar_path)201 package.add_file("control.tar.gz", control_tar_path)
197 package.add_file("data.tar.gz", data_tar_path)202 package.add_file("data.tar.gz", data_tar_path)
198203
199 def _validate_framework(self, framework):204 def _validate_framework(self, framework_string):
200 """Apply policy checks to framework declarations."""205 """Apply policy checks to framework declarations."""
201 try:206 try:
202 apt_pkg207 validate_framework(
203 except NameError:208 framework_string, ignore_missing_frameworks=True)
204 return209 except ClickFrameworkInvalid as e:
205210 raise ClickBuildError(str(e))
206 try:
207 parsed_framework = apt_pkg.parse_depends(framework)
208 except ValueError:
209 raise ClickBuildError('Could not parse framework "%s"' % framework)
210 if len(parsed_framework) > 1:
211 raise ClickBuildError(
212 'Multiple dependencies in framework "%s" not yet allowed' %
213 framework)
214 for or_dep in parsed_framework:
215 if len(or_dep) > 1:
216 raise ClickBuildError(
217 'Alternative dependencies in framework "%s" not yet '
218 'allowed' % framework)
219 if or_dep[0][1] or or_dep[0][2]:
220 raise ClickBuildError(
221 'Version relationship in framework "%s" not yet allowed' %
222 framework)
223211
224 def build(self, dest_dir, manifest_path="manifest.json"):212 def build(self, dest_dir, manifest_path="manifest.json"):
225 with make_temp_dir() as temp_dir:213 with make_temp_dir() as temp_dir:
226214
=== added file 'click/framework.py'
--- click/framework.py 1970-01-01 00:00:00 +0000
+++ click/framework.py 2014-05-13 21:05:36 +0000
@@ -0,0 +1,138 @@
1# Copyright (C) 2014 Canonical Ltd.
2# Author: Michael Vogt <michael.vogt@canonical.com>
3
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; version 3 of the License.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16"""Pure python click framework handling support."""
17
18import logging
19import os
20import re
21
22try:
23 import apt_pkg
24except:
25 pass
26
27import click.paths
28
29
30class ClickFrameworkInvalid(Exception):
31 pass
32
33
34# FIXME: use native lib if available
35#from gi.repository import Click
36#click_framework_get_base_version = Click.framework_get_base_version
37#click_framework_has_framework = Click.has_framework
38
39
40# python version of the vala parse_deb822_file()
41def parse_deb822_file(filename):
42 data = {}
43 with open(filename) as f:
44 for line in f:
45 line = line.strip()
46 # from deb822.vala
47 field_re_posix = r'^([^:[:space:]]+)[[:space:]]*:[[:space:]]'\
48 '([^[:space:]].*?)[[:space:]]*$'
49 # python does not do posix char classes
50 field_re = field_re_posix.replace("[:space:]", "\s")
51 blank_re_posix = r'^[[:space:]]*$'
52 blank_re = blank_re_posix.replace("[:space:]", "\s")
53 if re.match(blank_re, line):
54 break
55 match = re.match(field_re, line)
56 if match and match.group(1) and match.group(2):
57 data[match.group(1).lower()] = match.group(2)
58 return data
59
60
61# python version of vala get_frameworks_dir
62def get_frameworks_dir():
63 return click.paths.frameworks_dir
64
65
66def get_framework_path(framework_name):
67 framework_path = os.path.join(
68 get_frameworks_dir(), framework_name+".framework")
69 return framework_path
70
71
72# python version of the vala click_framework_get_base_version()
73def click_framework_get_base_version(framework_name):
74 deb822 = parse_deb822_file(get_framework_path(framework_name))
75 return deb822.get("base-version", None)
76
77
78# python version of the vala click_framework_has_framework
79def click_framework_has_framework(framework_name):
80 return os.path.exists(get_framework_path(framework_name))
81
82
83def validate_framework(framework_string, ignore_missing_frameworks=False):
84 try:
85 apt_pkg
86 except NameError:
87 logging.warning("No apt_pkg module, skipping validate_framework")
88 return
89
90 try:
91 parsed_framework = apt_pkg.parse_depends(framework_string)
92 except ValueError:
93 raise ClickFrameworkInvalid(
94 'Could not parse framework "%s"' % framework_string)
95
96 framework_base_versions = set()
97 missing_frameworks = []
98 for or_dep in parsed_framework:
99 if len(or_dep) > 1:
100 raise ClickFrameworkInvalid(
101 'Alternative dependencies in framework "%s" not yet '
102 'allowed' % framework_string)
103 if or_dep[0][1] or or_dep[0][2]:
104 raise ClickFrameworkInvalid(
105 'Version relationship in framework "%s" not yet allowed' %
106 framework_string)
107 # now verify that different base versions are not mixed
108 framework_name = or_dep[0][0]
109 if not click_framework_has_framework(framework_name):
110 missing_frameworks.append(framework_name)
111 continue
112 framework_base_version = click_framework_get_base_version(
113 framework_name)
114 framework_base_versions.add(framework_base_version)
115
116 if not ignore_missing_frameworks:
117 if len(missing_frameworks) > 1:
118 raise ClickFrameworkInvalid(
119 'Frameworks %s not present on system (use '
120 '--force-missing-framework option to override)' %
121 ", ".join('"%s"' % f for f in missing_frameworks))
122 elif missing_frameworks:
123 raise ClickFrameworkInvalid(
124 'Framework "%s" not present on system (use '
125 '--force-missing-framework option to override)' %
126 missing_frameworks[0])
127 else:
128 if len(missing_frameworks) > 1:
129 logging.warning("Ignoring missing frameworks %s" % (
130 ", ".join('"%s"' % f for f in missing_frameworks)))
131 elif missing_frameworks:
132 logging.warning('Ignoring missing framework "%s"' % (
133 missing_frameworks[0]))
134
135 if len(framework_base_versions) > 1:
136 raise ClickFrameworkInvalid(
137 'Multiple frameworks with different base versions are not '
138 'allowed. Found: {0}'.format(framework_base_versions))
0139
=== modified file 'click/install.py'
--- click/install.py 2014-04-03 08:52:02 +0000
+++ click/install.py 2014-05-13 21:05:36 +0000
@@ -49,6 +49,11 @@
49from click.preinst import static_preinst_matches49from click.preinst import static_preinst_matches
50from click.versions import spec_version50from click.versions import spec_version
5151
52from click.framework import (
53 validate_framework,
54 ClickFrameworkInvalid,
55)
56
5257
53try:58try:
54 _DebFile.close59 _DebFile.close
@@ -189,34 +194,9 @@
189 raise ClickInstallerAuditError(194 raise ClickInstallerAuditError(
190 'No "framework" entry in manifest')195 'No "framework" entry in manifest')
191 try:196 try:
192 parsed_framework = apt_pkg.parse_depends(framework)197 validate_framework(framework, self.force_missing_framework)
193 except ValueError:198 except ClickFrameworkInvalid as e:
194 raise ClickInstallerAuditError(199 raise ClickInstallerAuditError(str(e))
195 'Could not parse framework "%s"' % framework)
196 for or_dep in parsed_framework:
197 if len(or_dep) > 1:
198 raise ClickInstallerAuditError(
199 'Alternative dependencies in framework "%s" not yet '
200 'allowed' % framework)
201 if or_dep[0][1] or or_dep[0][2]:
202 raise ClickInstallerAuditError(
203 'Version relationship in framework "%s" not yet '
204 'allowed' % framework)
205 if not self.force_missing_framework:
206 missing_frameworks = []
207 for or_dep in parsed_framework:
208 if not Click.Framework.has_framework(or_dep[0][0]):
209 missing_frameworks.append(or_dep[0][0])
210 if len(missing_frameworks) > 1:
211 raise ClickInstallerAuditError(
212 'Frameworks %s not present on system (use '
213 '--force-missing-framework option to override)' %
214 ", ".join('"%s"' % f for f in missing_frameworks))
215 elif missing_frameworks:
216 raise ClickInstallerAuditError(
217 'Framework "%s" not present on system (use '
218 '--force-missing-framework option to override)' %
219 missing_frameworks[0])
220200
221 if check_arch:201 if check_arch:
222 architecture = manifest.get("architecture", "all")202 architecture = manifest.get("architecture", "all")
223203
=== modified file 'click/paths.py.in'
--- click/paths.py.in 2014-03-10 14:49:27 +0000
+++ click/paths.py.in 2014-05-13 21:05:36 +0000
@@ -16,3 +16,4 @@
16"""Click paths."""16"""Click paths."""
1717
18preload_path = "@pkglibdir@/libclickpreload.so"18preload_path = "@pkglibdir@/libclickpreload.so"
19frameworks_dir = "@pkgdatadir@/frameworks"
1920
=== modified file 'click/tests/helpers.py'
--- click/tests/helpers.py 2014-03-10 14:49:27 +0000
+++ click/tests/helpers.py 2014-05-13 21:05:36 +0000
@@ -30,6 +30,7 @@
3030
31import contextlib31import contextlib
32import os32import os
33import re
33import shutil34import shutil
34import sys35import sys
35import tempfile36import tempfile
@@ -107,6 +108,34 @@
107 self.assertRaisesGError(108 self.assertRaisesGError(
108 "click_user_error-quark", code, callableObj, *args, **kwargs)109 "click_user_error-quark", code, callableObj, *args, **kwargs)
109110
111 def _create_mock_framework_dir(self, frameworks_dir=None):
112 if frameworks_dir is None:
113 frameworks_dir = os.path.join(self.temp_dir, "frameworks")
114 patcher = mock.patch('click.paths.frameworks_dir', frameworks_dir)
115 patcher.start()
116 self.addCleanup(patcher.stop)
117 Click.ensuredir(frameworks_dir)
118 return frameworks_dir
119
120 def _create_mock_framework_file(self, framework_name):
121 self.use_temp_dir()
122 self._create_mock_framework_dir()
123 r = r'(?P<name>[a-z]+-sdk)-(?P<ver>[0-9.]+)(-[a-z0-9-]+)?'
124 match = re.match(r, framework_name)
125 if match is None:
126 name = "unknown"
127 ver = "1.0"
128 else:
129 name = match.group("name")
130 ver = match.group("ver")
131 framework_filename = os.path.join(
132 self.temp_dir, "frameworks",
133 "{0}.framework".format(framework_name))
134 with open(framework_filename, "w") as f:
135 f.write("Base-Name: {0}\n".format(name))
136 f.write("Base-Version: {0}\n".format(ver))
137
138
110139
111if not hasattr(mock, "call"):140if not hasattr(mock, "call"):
112 # mock 0.7.2, the version in Ubuntu 12.04 LTS, lacks mock.ANY and141 # mock 0.7.2, the version in Ubuntu 12.04 LTS, lacks mock.ANY and
113142
=== modified file 'click/tests/test_build.py'
--- click/tests/test_build.py 2014-01-23 17:19:09 +0000
+++ click/tests/test_build.py 2014-05-13 21:05:36 +0000
@@ -245,6 +245,7 @@
245 del target_json["installed-size"]245 del target_json["installed-size"]
246 self.assertEqual(source_json, target_json)246 self.assertEqual(source_json, target_json)
247247
248 # FIXME: DRY violation with test_build_multiple_architectures etc
248 def test_build_multiple_frameworks(self):249 def test_build_multiple_frameworks(self):
249 self.use_temp_dir()250 self.use_temp_dir()
250 scratch = os.path.join(self.temp_dir, "scratch")251 scratch = os.path.join(self.temp_dir, "scratch")
@@ -259,11 +260,44 @@
259 "ubuntu-sdk-14.04-basic, ubuntu-sdk-14.04-webapps",260 "ubuntu-sdk-14.04-basic, ubuntu-sdk-14.04-webapps",
260 }, f)261 }, f)
261 self.builder.add_file(scratch, "/")262 self.builder.add_file(scratch, "/")
262 self.assertRaisesRegex(263 path = self.builder.build(self.temp_dir)
263 ClickBuildError,264 control_path = os.path.join(self.temp_dir, "control")
264 'Multiple dependencies in framework "ubuntu-sdk-14.04-basic, '265 subprocess.check_call(["dpkg-deb", "-e", path, control_path])
265 'ubuntu-sdk-14.04-webapps" not yet allowed',266 manifest_path = os.path.join(control_path, "manifest")
266 self.builder.build, self.temp_dir)267 with open(os.path.join(scratch, "manifest.json")) as source, \
268 open(manifest_path) as target:
269 source_json = json.load(source)
270 target_json = json.load(target)
271 del target_json["installed-size"]
272 self.assertEqual(source_json, target_json)
273
274
275class TestClickFrameworkValidation(TestCase):
276 def setUp(self):
277 super(TestClickFrameworkValidation, self).setUp()
278 self.builder = ClickBuilder()
279 for framework_name in ("ubuntu-sdk-13.10",
280 "ubuntu-sdk-14.04-papi",
281 "ubuntu-sdk-14.04-html"):
282 self._create_mock_framework_file(framework_name)
283
284 def test_validate_framework_good(self):
285 valid_framework_values = (
286 "ubuntu-sdk-13.10",
287 "ubuntu-sdk-14.04-papi, ubuntu-sdk-14.04-html",
288 )
289 for framework in valid_framework_values:
290 self.builder._validate_framework(framework)
291
292 def test_validate_framework_bad(self):
293 invalid_framework_values = (
294 "ubuntu-sdk-13.10, ubuntu-sdk-14.04-papi",
295 "ubuntu-sdk-13.10 (>= 13.10)",
296 "ubuntu-sdk-13.10 | ubuntu-sdk-14.04",
297 )
298 for framework in invalid_framework_values:
299 with self.assertRaises(ClickBuildError):
300 self.builder._validate_framework(framework)
267301
268302
269class TestClickSourceBuilder(TestCase, TestClickBuilderBaseMixin):303class TestClickSourceBuilder(TestCase, TestClickBuilderBaseMixin):
270304
=== modified file 'click/tests/test_install.py'
--- click/tests/test_install.py 2014-04-03 08:52:02 +0000
+++ click/tests/test_install.py 2014-05-13 21:05:36 +0000
@@ -104,12 +104,10 @@
104 return package_path104 return package_path
105105
106 def _setup_frameworks(self, preloads, frameworks_dir=None, frameworks=[]):106 def _setup_frameworks(self, preloads, frameworks_dir=None, frameworks=[]):
107 if frameworks_dir is None:107 frameworks_dir = self._create_mock_framework_dir(frameworks_dir)
108 frameworks_dir = os.path.join(self.temp_dir, "frameworks")
109 shutil.rmtree(frameworks_dir, ignore_errors=True)108 shutil.rmtree(frameworks_dir, ignore_errors=True)
110 Click.ensuredir(frameworks_dir)
111 for framework in frameworks:109 for framework in frameworks:
112 touch(os.path.join(frameworks_dir, "%s.framework" % framework))110 self._create_mock_framework_file(framework)
113 preloads["click_get_frameworks_dir"].side_effect = (111 preloads["click_get_frameworks_dir"].side_effect = (
114 lambda: self.make_string(frameworks_dir))112 lambda: self.make_string(frameworks_dir))
115113
116114
=== modified file 'debian/changelog'
--- debian/changelog 2014-04-08 09:41:55 +0000
+++ debian/changelog 2014-05-13 21:05:36 +0000
@@ -1,3 +1,11 @@
1click (0.4.22) UNRELEASED; urgency=medium
2
3 [ Michael Vogt ]
4 * Update documentation for building the project.
5 * Add support for multiple frameworks (LP: #1318757).
6
7 -- Colin Watson <cjwatson@ubuntu.com> Thu, 08 May 2014 13:47:55 +0100
8
1click (0.4.21.1) trusty; urgency=medium9click (0.4.21.1) trusty; urgency=medium
210
3 [ Colin Watson ]11 [ Colin Watson ]
412
=== modified file 'doc/file-format.rst'
--- doc/file-format.rst 2014-03-07 15:09:17 +0000
+++ doc/file-format.rst 2014-05-13 21:05:36 +0000
@@ -103,8 +103,7 @@
103"framework": "ubuntu-sdk-13.10", or a list of simple names all of which must103"framework": "ubuntu-sdk-13.10", or a list of simple names all of which must
104be satisfied, e.g. "framework": "ubuntu-sdk-14.04-qml,104be satisfied, e.g. "framework": "ubuntu-sdk-14.04-qml,
105ubuntu-sdk-14.04-webapps"; version relationships and alternative105ubuntu-sdk-14.04-webapps"; version relationships and alternative
106dependencies are not currently allowed. At the moment, ``click build`` will106dependencies are not currently allowed.
107only allow building with a single simple name, pending policy decisions.
108107
109The manifest may contain arbitrary additional optional keys; new optional108The manifest may contain arbitrary additional optional keys; new optional
110keys may be defined without changing the version number of this109keys may be defined without changing the version number of this
111110
=== modified file 'doc/index.rst'
--- doc/index.rst 2014-03-13 10:38:22 +0000
+++ doc/index.rst 2014-05-13 21:05:36 +0000
@@ -25,20 +25,34 @@
25Currently, this package should remain compatible with Python 2.7, 3.2, 3.3,25Currently, this package should remain compatible with Python 2.7, 3.2, 3.3,
26and 3.4; Ubuntu 12.04 LTS, Ubuntu 13.10, and Ubuntu 14.04 LTS.26and 3.4; Ubuntu 12.04 LTS, Ubuntu 13.10, and Ubuntu 14.04 LTS.
2727
28Build
29=====
30
31If you run from a fresh bzr checkout, please ensure you have the required
32build dependencies first by running::
33
34 $ dpkg-checkbuilddeps
35
36and installing anything that is missing here.
37
38Then run::
39
40 $ ./autogen.sh
41 $ ./configure --with-systemdsystemunitdir=/lib/systemd/system \
42 --with-systemduserunitdir=/usr/lib/systemd/user
43 $ make
44
45to build the project.
46
2847
29Dependencies48Dependencies
30------------49------------
3150
32For Ubuntu 13.10, make sure you have the *python2.7* and *python3.3*51For Ubuntu 14.04, make sure you have the *python2.7* and *python3.4*
33packages installed. Unless you upgraded from a previous version of Ubuntu52packages installed. Unless you upgraded from a previous version of Ubuntu
34and haven't removed it yet, you won't have Python 3.2 available. Build it53and haven't removed it yet, you won't have Python 3.3 and Python 3.2
35from source if necessary, install them say into ``/usr/local``, and make54available. Build them from source if necessary, install them say into
36sure it is on your ``$PATH``.55``/usr/local``, and make sure they are on your ``$PATH``.
37
38You'll need *gcc* in order to build the preload shared library. Assuming you
39have this, do the following::
40
41 $ (cd preload && make)
4256
43You'll need *tox* (Ubuntu package *python-tox*) installed in order to run the57You'll need *tox* (Ubuntu package *python-tox*) installed in order to run the
44full test suite. You should be able to just say::58full test suite. You should be able to just say::
@@ -66,6 +80,15 @@
66an additional check to make sure you've got the preload shared library80an additional check to make sure you've got the preload shared library
67built.81built.
6882
83To run a specific testcase, use the standard python unittest syntax like::
84
85 $ python3 -m unittest click.tests.test_install
86
87or::
88
89 $ python2 -m unittest click.tests.test_build.TestClickBuilder.test_build
90
91
6992
70Documentation93Documentation
71=============94=============

Subscribers

People subscribed via source and target branches