Merge lp:~cjwatson/click/native-chroots into lp:click

Proposed by Colin Watson
Status: Superseded
Proposed branch: lp:~cjwatson/click/native-chroots
Merge into: lp:click
Diff against target: 1772 lines (+726/-253)
14 files modified
click/chroot.py (+76/-25)
click/commands/__init__.py (+1/-0)
click/commands/chroot.py (+69/-9)
click/commands/framework.py (+42/-0)
click/tests/helpers.py (+22/-0)
click/tests/test_build.py (+10/-1)
click/tests/test_chroot.py (+123/-0)
click/tests/test_hooks.py (+261/-196)
click/tests/test_install.py (+9/-9)
debian/changelog (+30/-0)
debian/control (+1/-1)
doc/index.rst (+4/-2)
doc/manpage.rst (+6/-0)
lib/click/hooks.vala (+72/-10)
To merge this branch: bzr merge lp:~cjwatson/click/native-chroots
Reviewer Review Type Date Requested Status
click hackers Pending
Review via email: mp+220231@code.launchpad.net

This proposal has been superseded by a proposal from 2014-05-20.

Commit message

chroot: Handle the case where we can execute binaries for the target architecture directly and thus don't need a cross-compiler (LP: #1319153).

Description of the change

chroot: Handle the case where we can execute binaries for the target architecture directly and thus don't need a cross-compiler (LP: #1319153).

This implements my suggestion from here:

  https://bugs.launchpad.net/ubuntu/+source/click/+bug/1319153/comments/3

To post a comment you must log in.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'click/chroot.py'
2--- click/chroot.py 2014-03-31 11:11:26 +0000
3+++ click/chroot.py 2014-05-20 11:54:03 +0000
4@@ -22,6 +22,8 @@
5 __all__ = [
6 "ClickChroot",
7 "ClickChrootException",
8+ "ClickChrootAlreadyExistsException",
9+ "ClickChrootDoesNotExistException",
10 ]
11
12
13@@ -72,6 +74,7 @@
14 "libqt5svg5-dev:TARGET",
15 "libqt5webkit5-dev:TARGET",
16 "libqt5xmlpatterns5-dev:TARGET",
17+ "libunity-scopes-dev:TARGET",
18 "qt3d5-dev:TARGET",
19 "qt5-default:TARGET",
20 "qtbase5-dev:TARGET",
21@@ -104,6 +107,17 @@
22
23
24 class ClickChrootException(Exception):
25+ """A generic issue with the chroot"""
26+ pass
27+
28+
29+class ClickChrootAlreadyExistsException(ClickChrootException):
30+ """The chroot already exists"""
31+ pass
32+
33+
34+class ClickChrootDoesNotExistException(ClickChrootException):
35+ """A chroot with that name does not exist yet"""
36 pass
37
38
39@@ -118,9 +132,10 @@
40 series = framework_series[self.framework_base]
41 self.series = series
42 self.session = session
43- self.native_arch = subprocess.check_output(
44+ system_arch = subprocess.check_output(
45 ["dpkg", "--print-architecture"],
46 universal_newlines=True).strip()
47+ self.native_arch = self._get_native_arch(system_arch, self.target_arch)
48 self.chroots_dir = "/var/lib/schroot/chroots"
49 # this doesn't work because we are running this under sudo
50 if 'DEBOOTSTRAP_MIRROR' in os.environ:
51@@ -135,6 +150,24 @@
52 self.user = pwd.getpwuid(os.getuid()).pw_name
53 self.dpkg_architecture = self._dpkg_architecture()
54
55+ def _get_native_arch(self, system_arch, target_arch):
56+ """Determine the proper native architecture for a chroot.
57+
58+ Some combinations of system and target architecture do not require
59+ cross-building, so in these cases we just create a chroot suitable
60+ for native building.
61+ """
62+ if (system_arch, target_arch) in (
63+ ("amd64", "i386"),
64+ # This will only work if the system is running a 64-bit
65+ # kernel; but there's no alternative since no i386-to-amd64
66+ # cross-compiler is available in the Ubuntu archive.
67+ ("i386", "amd64"),
68+ ):
69+ return target_arch
70+ else:
71+ return system_arch
72+
73 def _dpkg_architecture(self):
74 dpkg_architecture = {}
75 command = ["dpkg-architecture", "-a%s" % self.target_arch]
76@@ -156,18 +189,23 @@
77 for pocket in ['updates', 'security']:
78 pockets.append('%s-%s' % (series, pocket))
79 sources = []
80- if target_arch not in primary_arches:
81- for pocket in pockets:
82- sources.append("deb [arch=%s] %s %s %s" %
83- (target_arch, ports_mirror, pocket, components))
84- sources.append("deb-src %s %s %s" %
85- (ports_mirror, pocket, components))
86- if native_arch in primary_arches:
87- for pocket in pockets:
88- sources.append("deb [arch=%s] %s %s %s" %
89- (native_arch, self.archive, pocket, components))
90- sources.append("deb-src %s %s %s" %
91- (self.archive, pocket, components))
92+ # write binary lines
93+ arches = [target_arch]
94+ if native_arch != target_arch:
95+ arches.append(native_arch)
96+ for arch in arches:
97+ if arch not in primary_arches:
98+ mirror = ports_mirror
99+ else:
100+ mirror = self.archive
101+ for pocket in pockets:
102+ sources.append("deb [arch=%s] %s %s %s" %
103+ (arch, mirror, pocket, components))
104+ # write source lines
105+ for pocket in pockets:
106+ sources.append("deb-src %s %s %s" %
107+ (self.archive, pocket, components))
108+
109 return sources
110
111 @property
112@@ -195,9 +233,16 @@
113 mode = stat.S_IMODE(os.stat(path).st_mode)
114 os.chmod(path, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
115
116- def create(self):
117+ def _make_cross_package(self, prefix):
118+ if self.native_arch == self.target_arch:
119+ return prefix
120+ else:
121+ target_tuple = self.dpkg_architecture["DEB_HOST_GNU_TYPE"]
122+ return "%s-%s" % (prefix, target_tuple)
123+
124+ def create(self, keep_broken_chroot_on_fail=False):
125 if self.exists():
126- raise ClickChrootException(
127+ raise ClickChrootAlreadyExistsException(
128 "Chroot %s already exists" % self.full_name)
129 components = ["main", "restricted", "universe", "multiverse"]
130 mount = "%s/%s" % (self.chroots_dir, self.full_name)
131@@ -208,11 +253,10 @@
132 proxy = subprocess.check_output(
133 'unset x; eval "$(apt-config shell x Acquire::HTTP::Proxy)"; echo "$x"',
134 shell=True, universal_newlines=True).strip()
135- target_tuple = self.dpkg_architecture["DEB_HOST_GNU_TYPE"]
136 build_pkgs = [
137 "build-essential", "fakeroot",
138- "apt-utils", "g++-%s" % target_tuple,
139- "pkg-config-%s" % target_tuple, "cmake",
140+ "apt-utils", self._make_cross_package("g++"),
141+ self._make_cross_package("pkg-config"), "cmake",
142 "dpkg-cross", "libc-dev:%s" % self.target_arch
143 ]
144 for package in extra_packages.get(self.framework_base, []):
145@@ -312,11 +356,18 @@
146 print("apt-get clean", file=finish)
147 self._make_executable(finish_script)
148 command = ["/finish.sh"]
149- return self.maint(*command)
150+ ret_code = self.maint(*command)
151+ if ret_code != 0 and not keep_broken_chroot_on_fail:
152+ # cleanup on failure
153+ self.destroy()
154+ raise ClickChrootException(
155+ "Failed to create chroot '{}' (exit status {})".format(
156+ self.full_name, ret_code))
157+ return ret_code
158
159 def run(self, *args):
160 if not self.exists():
161- raise ClickChrootException(
162+ raise ClickChrootDoesNotExistException(
163 "Chroot %s does not exist" % self.full_name)
164 command = ["schroot", "-c"]
165 if self.session:
166@@ -353,7 +404,7 @@
167
168 def install(self, *pkgs):
169 if not self.exists():
170- raise ClickChrootException(
171+ raise ClickChrootDoesNotExistException(
172 "Chroot %s does not exist" % self.full_name)
173 ret = self.update()
174 if ret != 0:
175@@ -375,7 +426,7 @@
176
177 def upgrade(self):
178 if not self.exists():
179- raise ClickChrootException(
180+ raise ClickChrootDoesNotExistException(
181 "Chroot %s does not exist" % self.full_name)
182 ret = self.update()
183 if ret != 0:
184@@ -388,7 +439,7 @@
185
186 def destroy(self):
187 if not self.exists():
188- raise ClickChrootException(
189+ raise ClickChrootDoesNotExistException(
190 "Chroot %s does not exist" % self.full_name)
191 chroot_config = "/etc/schroot/chroot.d/%s" % self.full_name
192 os.remove(chroot_config)
193@@ -398,7 +449,7 @@
194
195 def begin_session(self):
196 if not self.exists():
197- raise ClickChrootException(
198+ raise ClickChrootDoesNotExistException(
199 "Chroot %s does not exist" % self.full_name)
200 command = ["schroot", "-c", self.full_name, "--begin-session",
201 "--session-name", self.full_session_name]
202@@ -407,7 +458,7 @@
203
204 def end_session(self):
205 if not self.exists():
206- raise ClickChrootException(
207+ raise ClickChrootDoesNotExistException(
208 "Chroot %s does not exist" % self.full_name)
209 command = ["schroot", "-c", self.full_session_name, "--end-session"]
210 subprocess.check_call(command)
211
212=== modified file 'click/commands/__init__.py'
213--- click/commands/__init__.py 2013-10-31 20:17:19 +0000
214+++ click/commands/__init__.py 2014-05-20 11:54:03 +0000
215@@ -24,6 +24,7 @@
216 "chroot",
217 "contents",
218 "desktophook",
219+ "framework",
220 "hook",
221 "info",
222 "install",
223
224=== modified file 'click/commands/chroot.py'
225--- click/commands/chroot.py 2014-03-31 11:11:26 +0000
226+++ click/commands/chroot.py 2014-05-20 11:54:03 +0000
227@@ -20,9 +20,14 @@
228 from __future__ import print_function
229
230 from argparse import ArgumentParser, REMAINDER
231+from contextlib import contextmanager
232 import os
233
234-from click.chroot import ClickChroot
235+from click.chroot import (
236+ ClickChroot,
237+ ClickChrootAlreadyExistsException,
238+ ClickChrootDoesNotExistException,
239+)
240 from click import osextras
241
242
243@@ -31,6 +36,25 @@
244 parser.error("must be run as root; try sudo")
245
246
247+@contextmanager
248+def message_on_error(exc, msg):
249+ """
250+ Context Manager that prints the error message 'msg' on exception 'exc'
251+ """
252+ try:
253+ yield
254+ except exc:
255+ print(msg)
256+
257+
258+# FIXME: i18n(?)
259+class ErrorMessages:
260+ EXISTS = """A chroot for that name and architecture already exists.
261+Please see the man-page how to use it."""
262+ NOT_EXISTS = """A chroot for that name and architecture does not exist.
263+Please use 'create' to create it."""
264+
265+
266 def create(parser, args):
267 if not osextras.find_on_path("debootstrap"):
268 parser.error(
269@@ -38,20 +62,32 @@
270 "debootstrap")
271 requires_root(parser)
272 chroot = ClickChroot(args.architecture, args.framework, series=args.series)
273- return chroot.create()
274+ with message_on_error(
275+ ClickChrootAlreadyExistsException, ErrorMessages.EXISTS):
276+ return chroot.create(args.keep_broken_chroot)
277+ # if we reach this point there was a error so return exit_status 1
278+ return 1
279
280
281 def install(parser, args):
282 packages = args.packages
283 chroot = ClickChroot(args.architecture, args.framework)
284- return chroot.install(*packages)
285+ with message_on_error(
286+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
287+ return chroot.install(*packages)
288+ # if we reach this point there was a error so return exit_status 1
289+ return 1
290
291
292 def destroy(parser, args):
293 requires_root(parser)
294 # ask for confirmation?
295 chroot = ClickChroot(args.architecture, args.framework)
296- return chroot.destroy()
297+ with message_on_error(
298+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
299+ return chroot.destroy()
300+ # if we reach this point there was a error so return exit_status 1
301+ return 1
302
303
304 def execute(parser, args):
305@@ -60,7 +96,11 @@
306 program = ["/bin/bash"]
307 chroot = ClickChroot(
308 args.architecture, args.framework, session=args.session)
309- return chroot.run(*program)
310+ with message_on_error(
311+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
312+ return chroot.run(*program)
313+ # if we reach this point there was a error so return exit_status 1
314+ return 1
315
316
317 def maint(parser, args):
318@@ -69,24 +109,40 @@
319 program = ["/bin/bash"]
320 chroot = ClickChroot(
321 args.architecture, args.framework, session=args.session)
322- return chroot.maint(*program)
323+ with message_on_error(
324+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
325+ return chroot.maint(*program)
326+ # if we reach this point there was a error so return exit_status 1
327+ return 1
328
329
330 def upgrade(parser, args):
331 chroot = ClickChroot(args.architecture, args.framework)
332- return chroot.upgrade()
333+ with message_on_error(
334+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
335+ return chroot.upgrade()
336+ # if we reach this point there was a error so return exit_status 1
337+ return 1
338
339
340 def begin_session(parser, args):
341 chroot = ClickChroot(
342 args.architecture, args.framework, session=args.session)
343- return chroot.begin_session()
344+ with message_on_error(
345+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
346+ return chroot.begin_session()
347+ # if we reach this point there was a error so return exit_status 1
348+ return 1
349
350
351 def end_session(parser, args):
352 chroot = ClickChroot(
353 args.architecture, args.framework, session=args.session)
354- return chroot.end_session()
355+ with message_on_error(
356+ ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS):
357+ return chroot.end_session()
358+ # if we reach this point there was a error so return exit_status 1
359+ return 1
360
361
362 def run(argv):
363@@ -107,6 +163,10 @@
364 create_parser = subparsers.add_parser(
365 "create",
366 help="create a chroot of the provided architecture")
367+ create_parser.add_argument(
368+ "-k", "--keep-broken-chroot", default=False, action="store_true",
369+ help="Keep the chroot even if creating it fails (default is to delete "
370+ "it)")
371 create_parser.set_defaults(func=create)
372 destroy_parser = subparsers.add_parser(
373 "destroy",
374
375=== added file 'click/commands/framework.py'
376--- click/commands/framework.py 1970-01-01 00:00:00 +0000
377+++ click/commands/framework.py 2014-05-20 11:54:03 +0000
378@@ -0,0 +1,42 @@
379+# Copyright (C) 2014 Canonical Ltd.
380+# Author: Michael Vogt <mvo@ubuntu.com>
381+
382+# This program is free software: you can redistribute it and/or modify
383+# it under the terms of the GNU General Public License as published by
384+# the Free Software Foundation; version 3 of the License.
385+#
386+# This program is distributed in the hope that it will be useful,
387+# but WITHOUT ANY WARRANTY; without even the implied warranty of
388+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
389+# GNU General Public License for more details.
390+#
391+# You should have received a copy of the GNU General Public License
392+# along with this program. If not, see <http://www.gnu.org/licenses/>.
393+
394+"""List available frameworks."""
395+
396+from __future__ import print_function
397+
398+from argparse import ArgumentParser
399+
400+from gi.repository import Click
401+
402+
403+def list(parser, args):
404+ for framework in Click.Framework.get_frameworks():
405+ print("%s" % framework.props.name)
406+ return 0
407+
408+
409+def run(argv):
410+ parser = ArgumentParser("click framework")
411+ subparsers = parser.add_subparsers()
412+ list_parser = subparsers.add_parser(
413+ "list",
414+ help="list available frameworks")
415+ list_parser.set_defaults(func=list)
416+ args = parser.parse_args(argv)
417+ if not hasattr(args, "func"):
418+ parser.print_help()
419+ return 1
420+ return args.func(parser, args)
421
422=== modified file 'click/tests/helpers.py'
423--- click/tests/helpers.py 2014-05-08 18:18:39 +0000
424+++ click/tests/helpers.py 2014-05-20 11:54:03 +0000
425@@ -29,6 +29,7 @@
426
427
428 import contextlib
429+from functools import wraps
430 import os
431 import re
432 import shutil
433@@ -45,6 +46,19 @@
434 from click.tests import gimock
435
436
437+def disable_logging(func):
438+ """Decorator to disable logging e.g. during a test"""
439+ @wraps(func)
440+ def wrapper(*args, **kwargs):
441+ import logging
442+ logging.disable(logging.CRITICAL)
443+ try:
444+ return func(*args, **kwargs)
445+ finally:
446+ logging.disable(logging.NOTSET)
447+ return wrapper
448+
449+
450 class TestCase(gimock.GIMockTestCase):
451 def setUp(self):
452 super(TestCase, self).setUp()
453@@ -108,6 +122,14 @@
454 self.assertRaisesGError(
455 "click_user_error-quark", code, callableObj, *args, **kwargs)
456
457+ def _setup_frameworks(self, preloads, frameworks_dir=None, frameworks=[]):
458+ frameworks_dir = self._create_mock_framework_dir(frameworks_dir)
459+ shutil.rmtree(frameworks_dir, ignore_errors=True)
460+ for framework in frameworks:
461+ self._create_mock_framework_file(framework)
462+ preloads["click_get_frameworks_dir"].side_effect = (
463+ lambda: self.make_string(frameworks_dir))
464+
465 def _create_mock_framework_dir(self, frameworks_dir=None):
466 if frameworks_dir is None:
467 frameworks_dir = os.path.join(self.temp_dir, "frameworks")
468
469=== modified file 'click/tests/test_build.py'
470--- click/tests/test_build.py 2014-05-05 13:10:19 +0000
471+++ click/tests/test_build.py 2014-05-20 11:54:03 +0000
472@@ -33,7 +33,12 @@
473
474 from click.build import ClickBuildError, ClickBuilder, ClickSourceBuilder
475 from click.preinst import static_preinst
476-from click.tests.helpers import TestCase, mkfile, touch
477+from click.tests.helpers import (
478+ disable_logging,
479+ mkfile,
480+ TestCase,
481+ touch,
482+)
483
484
485 # BAW 2013-04-15: Some tests require umask 022. Use this decorator to
486@@ -118,6 +123,7 @@
487 ["dpkg-deb", "-f", path, name],
488 universal_newlines=True).rstrip("\n")
489
490+ @disable_logging
491 @umask(0o22)
492 def test_build(self):
493 self.use_temp_dir()
494@@ -199,6 +205,7 @@
495 self.assertEqual(
496 "foo", os.readlink(os.path.join(extract_path, "bin", "bar")))
497
498+ @disable_logging
499 def test_build_excludes_dot_click(self):
500 self.use_temp_dir()
501 scratch = os.path.join(self.temp_dir, "scratch")
502@@ -218,6 +225,7 @@
503 subprocess.check_call(["dpkg-deb", "-x", path, extract_path])
504 self.assertEqual([], os.listdir(extract_path))
505
506+ @disable_logging
507 def test_build_multiple_architectures(self):
508 self.use_temp_dir()
509 scratch = os.path.join(self.temp_dir, "scratch")
510@@ -246,6 +254,7 @@
511 self.assertEqual(source_json, target_json)
512
513 # FIXME: DRY violation with test_build_multiple_architectures etc
514+ @disable_logging
515 def test_build_multiple_frameworks(self):
516 self.use_temp_dir()
517 scratch = os.path.join(self.temp_dir, "scratch")
518
519=== added file 'click/tests/test_chroot.py'
520--- click/tests/test_chroot.py 1970-01-01 00:00:00 +0000
521+++ click/tests/test_chroot.py 2014-05-20 11:54:03 +0000
522@@ -0,0 +1,123 @@
523+# Copyright (C) 2014 Canonical Ltd.
524+# Author: Michael Vogt
525+
526+# This program is free software: you can redistribute it and/or modify
527+# it under the terms of the GNU General Public License as published by
528+# the Free Software Foundation; version 3 of the License.
529+#
530+# This program is distributed in the hope that it will be useful,
531+# but WITHOUT ANY WARRANTY; without even the implied warranty of
532+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
533+# GNU General Public License for more details.
534+#
535+# You should have received a copy of the GNU General Public License
536+# along with this program. If not, see <http://www.gnu.org/licenses/>.
537+
538+"""Unit tests for click.chroot."""
539+
540+from __future__ import print_function
541+
542+__metaclass__ = type
543+__all__ = [
544+ 'TestClickChroot',
545+ ]
546+
547+
548+from click.tests.helpers import TestCase
549+from click.chroot import (
550+ ClickChroot,
551+)
552+
553+
554+class TestClickChroot(TestCase):
555+ def test_get_native_arch_amd64_to_amd64(self):
556+ chroot = ClickChroot("amd64", "ubuntu-sdk-14.04", series="trusty")
557+ self.assertEqual("amd64", chroot._get_native_arch("amd64", "amd64"))
558+
559+ def test_get_native_arch_amd64_to_armhf(self):
560+ chroot = ClickChroot("armhf", "ubuntu-sdk-14.04", series="trusty")
561+ self.assertEqual("amd64", chroot._get_native_arch("amd64", "armhf"))
562+
563+ def test_get_native_arch_amd64_to_i386(self):
564+ chroot = ClickChroot("i386", "ubuntu-sdk-14.04", series="trusty")
565+ self.assertEqual("i386", chroot._get_native_arch("amd64", "i386"))
566+
567+ def test_gen_sources_archive_only(self):
568+ chroot = ClickChroot("amd64", "ubuntu-sdk-13.10", series="trusty")
569+ chroot.native_arch = "i386"
570+ sources = chroot._generate_sources(
571+ chroot.series, chroot.native_arch, chroot.target_arch,
572+ "main")
573+ self.assertEqual([
574+ 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu trusty main',
575+ 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu trusty-updates main',
576+ 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu trusty-security main',
577+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty main',
578+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-updates main',
579+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-security main',
580+ 'deb-src http://archive.ubuntu.com/ubuntu trusty main',
581+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main',
582+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main',
583+ ], sources)
584+
585+ def test_gen_sources_mixed_archive_ports(self):
586+ chroot = ClickChroot("armhf", "ubuntu-sdk-13.10", series="trusty")
587+ chroot.native_arch = "i386"
588+ sources = chroot._generate_sources(
589+ chroot.series, chroot.native_arch, chroot.target_arch,
590+ "main")
591+ self.assertEqual([
592+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty main',
593+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-updates main',
594+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-security main',
595+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty main',
596+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-updates main',
597+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-security main',
598+ 'deb-src http://archive.ubuntu.com/ubuntu trusty main',
599+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main',
600+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main',
601+ ], sources)
602+
603+ def test_gen_sources_ports_only(self):
604+ chroot = ClickChroot("armhf", "ubuntu-sdk-13.10", series="trusty")
605+ chroot.native_arch = "armel"
606+ sources = chroot._generate_sources(
607+ chroot.series, chroot.native_arch, chroot.target_arch,
608+ "main")
609+ self.assertEqual([
610+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty main',
611+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-updates main',
612+ 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-security main',
613+ 'deb [arch=armel] http://ports.ubuntu.com/ubuntu-ports trusty main',
614+ 'deb [arch=armel] http://ports.ubuntu.com/ubuntu-ports trusty-updates main',
615+ 'deb [arch=armel] http://ports.ubuntu.com/ubuntu-ports trusty-security main',
616+ 'deb-src http://archive.ubuntu.com/ubuntu trusty main',
617+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main',
618+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main',
619+ ], sources)
620+
621+ def test_gen_sources_native(self):
622+ chroot = ClickChroot("i386", "ubuntu-sdk-14.04", series="trusty")
623+ chroot.native_arch = "i386"
624+ sources = chroot._generate_sources(
625+ chroot.series, chroot.native_arch, chroot.target_arch,
626+ "main")
627+ self.assertEqual([
628+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty main',
629+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-updates main',
630+ 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-security main',
631+ 'deb-src http://archive.ubuntu.com/ubuntu trusty main',
632+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main',
633+ 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main',
634+ ], sources)
635+
636+ def test_make_cross_package_native(self):
637+ chroot = ClickChroot("amd64", "ubuntu-sdk-14.04", series="trusty")
638+ chroot.native_arch = "amd64"
639+ self.assertEqual("g++", chroot._make_cross_package("g++"))
640+
641+ def test_make_cross_package_cross(self):
642+ chroot = ClickChroot("armhf", "ubuntu-sdk-14.04", series="trusty")
643+ chroot.native_arch = "amd64"
644+ self.assertEqual(
645+ "g++-arm-linux-gnueabihf", chroot._make_cross_package("g++"))
646
647=== modified file 'click/tests/test_hooks.py'
648--- click/tests/test_hooks.py 2014-04-03 08:52:02 +0000
649+++ click/tests/test_hooks.py 2014-05-20 11:54:03 +0000
650@@ -95,6 +95,9 @@
651
652
653 class TestClickHookBase(TestCase):
654+
655+ TEST_USER = "test-user"
656+
657 def setUp(self):
658 super(TestClickHookBase, self).setUp()
659 self.use_temp_dir()
660@@ -102,11 +105,33 @@
661 self.db.add(self.temp_dir)
662 self.spawn_calls = []
663
664+ def _make_installed_click(self, package="test-1",version="1.0",
665+ json_data={},
666+ make_current=True,
667+ all_users=False):
668+ with mkfile_utf8(os.path.join(
669+ self.temp_dir, package, version, ".click", "info",
670+ "%s.manifest" % package)) as f:
671+ json.dump(json_data, f, ensure_ascii=False)
672+ if make_current:
673+ os.symlink(version, os.path.join(self.temp_dir, package, "current"))
674+ if all_users:
675+ db = Click.User.for_all_users(self.db)
676+ else:
677+ db = Click.User.for_user(self.db, self.TEST_USER)
678+ db.set_version(package, version)
679+
680+ def _make_hook_file(self, content, hookname="test"):
681+ hook_file = os.path.join(self.hooks_dir, "%s.hook" % hookname)
682+ with mkfile(hook_file) as f:
683+ print(content, file=f)
684+
685 def _setup_hooks_dir(self, preloads, hooks_dir=None):
686 if hooks_dir is None:
687 hooks_dir = self.temp_dir
688 preloads["click_get_hooks_dir"].side_effect = (
689 lambda: self.make_string(hooks_dir))
690+ self.hooks_dir = hooks_dir
691
692 def g_spawn_sync_side_effect(self, status_map, working_directory, argv,
693 envp, flags, child_setup, user_data,
694@@ -126,13 +151,12 @@
695 "click_get_hooks_dir") as (enter, preloads):
696 enter()
697 self._setup_hooks_dir(preloads)
698- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
699- print(dedent("""\
700+ self._make_hook_file(dedent("""\
701 Pattern: /usr/share/test/${id}.test
702 # Comment
703 Exec: test-update
704 User: root
705- """), file=f)
706+ """))
707 hook = Click.Hook.open(self.db, "test")
708 self.assertCountEqual(
709 ["pattern", "exec", "user"], hook.get_fields())
710@@ -146,8 +170,8 @@
711 "click_get_hooks_dir") as (enter, preloads):
712 enter()
713 self._setup_hooks_dir(preloads)
714- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
715- print("Pattern: /usr/share/test/${id}.test", file=f)
716+ self._make_hook_file(
717+ "Pattern: /usr/share/test/${id}.test")
718 hook = Click.Hook.open(self.db, "test")
719 self.assertEqual("test", hook.get_hook_name())
720
721@@ -156,9 +180,10 @@
722 "click_get_hooks_dir") as (enter, preloads):
723 enter()
724 self._setup_hooks_dir(preloads)
725- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
726- print("Pattern: /usr/share/test/${id}.test", file=f)
727- print("Hook-Name: other", file=f)
728+ self._make_hook_file(dedent("""\
729+ Pattern: /usr/share/test/${id}.test
730+ Hook-Name: other""")
731+ )
732 hook = Click.Hook.open(self.db, "test")
733 self.assertEqual("other", hook.get_hook_name())
734
735@@ -167,13 +192,12 @@
736 "click_get_hooks_dir") as (enter, preloads):
737 enter()
738 self._setup_hooks_dir(preloads)
739- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
740- print(dedent("""\
741+ self._make_hook_file(dedent("""\
742 Pattern: /usr/share/test/${id}.test
743 # Comment
744 Exec: test-update
745 User: root
746- """), file=f)
747+ """))
748 hook = Click.Hook.open(self.db, "test")
749 self.assertRaisesHooksError(
750 Click.HooksError.BAD_APP_NAME, hook.get_app_id,
751@@ -187,8 +211,8 @@
752 "click_get_hooks_dir") as (enter, preloads):
753 enter()
754 self._setup_hooks_dir(preloads)
755- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
756- print("Pattern: /usr/share/test/${short-id}.test", file=f)
757+ self._make_hook_file(
758+ "Pattern: /usr/share/test/${short-id}.test")
759 hook = Click.Hook.open(self.db, "test")
760 # It would perhaps be better if unrecognised $-expansions raised
761 # KeyError, but they don't right now.
762@@ -201,9 +225,9 @@
763 "click_get_hooks_dir") as (enter, preloads):
764 enter()
765 self._setup_hooks_dir(preloads)
766- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
767- print("Pattern: /usr/share/test/${short-id}.test", file=f)
768- print("Single-Version: yes", file=f)
769+ self._make_hook_file(dedent("""\
770+ Pattern: /usr/share/test/${short-id}.test
771+ Single-Version: yes"""))
772 hook = Click.Hook.open(self.db, "test")
773 self.assertEqual(
774 "/usr/share/test/package_app-name.test",
775@@ -231,8 +255,8 @@
776 "click_get_hooks_dir") as (enter, preloads):
777 enter()
778 self._setup_hooks_dir(preloads)
779- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
780- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
781+ self._make_hook_file(
782+ "Pattern: %s/${id}.test" % self.temp_dir)
783 os.makedirs(
784 os.path.join(self.temp_dir, "org.example.package", "1.0"))
785 hook = Click.Hook.open(self.db, "test")
786@@ -251,8 +275,8 @@
787 "click_get_hooks_dir") as (enter, preloads):
788 enter()
789 self._setup_hooks_dir(preloads)
790- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
791- print("Pattern: %s/${id}/" % self.temp_dir, file=f)
792+ self._make_hook_file(
793+ "Pattern: %s/${id}/" % self.temp_dir)
794 os.makedirs(
795 os.path.join(self.temp_dir, "org.example.package", "1.0"))
796 hook = Click.Hook.open(self.db, "test")
797@@ -276,8 +300,8 @@
798 "click_get_hooks_dir") as (enter, preloads):
799 enter()
800 self._setup_hooks_dir(preloads)
801- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
802- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
803+ self._make_hook_file(
804+ "Pattern: %s/${id}.test" % self.temp_dir)
805 underlay = os.path.join(self.temp_dir, "underlay")
806 overlay = os.path.join(self.temp_dir, "overlay")
807 db = Click.DB()
808@@ -304,8 +328,8 @@
809 "click_get_hooks_dir") as (enter, preloads):
810 enter()
811 self._setup_hooks_dir(preloads)
812- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
813- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
814+ self._make_hook_file(
815+ "Pattern: %s/${id}.test" % self.temp_dir)
816 symlink_path = os.path.join(
817 self.temp_dir, "org.example.package_test-app_1.0.test")
818 os.symlink("old-target", symlink_path)
819@@ -325,8 +349,8 @@
820 "click_get_hooks_dir") as (enter, preloads):
821 enter()
822 self._setup_hooks_dir(preloads)
823- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
824- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
825+ self._make_hook_file(
826+ "Pattern: %s/${id}.test" % self.temp_dir)
827 symlink_path = os.path.join(
828 self.temp_dir, "org.example.package_test-app_1.0.test")
829 os.symlink("old-target", symlink_path)
830@@ -341,28 +365,20 @@
831 enter()
832 self._setup_hooks_dir(
833 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
834- with mkfile(os.path.join(self.temp_dir, "hooks", "new.hook")) as f:
835- print("Pattern: %s/${id}.new" % self.temp_dir, file=f)
836- with mkfile_utf8(os.path.join(
837- self.temp_dir, "test-1", "1.0", ".click", "info",
838- "test-1.manifest")) as f:
839- json.dump({
840+ self._make_hook_file(
841+ "Pattern: %s/${id}.new" % self.temp_dir,
842+ hookname="new")
843+ self._make_installed_click("test-1", "1.0", json_data={
844 "maintainer":
845 b"Unic\xc3\xb3de <unicode@example.org>".decode(
846 "UTF-8"),
847- "hooks": {"test1-app": {"new": "target-1"}},
848- }, f, ensure_ascii=False)
849- os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current"))
850- with mkfile_utf8(os.path.join(
851- self.temp_dir, "test-2", "2.0", ".click", "info",
852- "test-2.manifest")) as f:
853- json.dump({
854+ "hooks": {"test1-app": {"new": "target-1"}}})
855+ self._make_installed_click("test-2", "2.0", json_data={
856 "maintainer":
857 b"Unic\xc3\xb3de <unicode@example.org>".decode(
858 "UTF-8"),
859 "hooks": {"test1-app": {"new": "target-2"}},
860- }, f, ensure_ascii=False)
861- os.symlink("2.0", os.path.join(self.temp_dir, "test-2", "current"))
862+ })
863 hook = Click.Hook.open(self.db, "new")
864 hook.install(user_name=None)
865 path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.new")
866@@ -382,22 +398,17 @@
867 enter()
868 self._setup_hooks_dir(
869 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
870- with mkfile(os.path.join(self.temp_dir, "hooks", "old.hook")) as f:
871- print("Pattern: %s/${id}.old" % self.temp_dir, file=f)
872- with mkfile(os.path.join(
873- self.temp_dir, "test-1", "1.0", ".click", "info",
874- "test-1.manifest")) as f:
875- json.dump({"hooks": {"test1-app": {"old": "target-1"}}}, f)
876- os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current"))
877+ self._make_hook_file(
878+ "Pattern: %s/${id}.old" % self.temp_dir,
879+ hookname="old")
880+ self._make_installed_click("test-1", "1.0", json_data={
881+ "hooks": {"test1-app": {"old": "target-1"}}})
882 path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.old")
883 os.symlink(
884 os.path.join(self.temp_dir, "test-1", "1.0", "target-1"),
885 path_1)
886- with mkfile(os.path.join(
887- self.temp_dir, "test-2", "2.0", ".click", "info",
888- "test-2.manifest")) as f:
889- json.dump({"hooks": {"test2-app": {"old": "target-2"}}}, f)
890- os.symlink("2.0", os.path.join(self.temp_dir, "test-2", "current"))
891+ self._make_installed_click("test-2", "2.0", json_data={
892+ "hooks": {"test2-app": {"old": "target-2"}}})
893 path_2 = os.path.join(self.temp_dir, "test-2_test2-app_2.0.old")
894 os.symlink(
895 os.path.join(self.temp_dir, "test-2", "2.0", "target-2"),
896@@ -413,23 +424,14 @@
897 enter()
898 self._setup_hooks_dir(
899 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
900- with mkfile(os.path.join(
901- self.temp_dir, "hooks", "test.hook")) as f:
902- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
903- with mkfile(os.path.join(
904- self.temp_dir, "test-1", "1.0", ".click", "info",
905- "test-1.manifest")) as f:
906- json.dump({"hooks": {"test1-app": {"test": "target-1"}}}, f)
907- os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current"))
908- with mkfile(os.path.join(
909- self.temp_dir, "test-2", "1.0", ".click", "info",
910- "test-2.manifest")) as f:
911- json.dump({"hooks": {"test2-app": {"test": "target-2"}}}, f)
912- with mkfile(os.path.join(
913- self.temp_dir, "test-2", "1.1", ".click", "info",
914- "test-2.manifest")) as f:
915- json.dump({"hooks": {"test2-app": {"test": "target-2"}}}, f)
916- os.symlink("1.1", os.path.join(self.temp_dir, "test-2", "current"))
917+ self._make_hook_file(
918+ "Pattern: %s/${id}.test" % self.temp_dir)
919+ self._make_installed_click("test-1", "1.0", json_data={
920+ "hooks": {"test1-app": {"test": "target-1"}}})
921+ self._make_installed_click("test-2", "1.0", make_current=False,
922+ json_data={"hooks": {"test2-app": {"test": "target-2"}}})
923+ self._make_installed_click("test-2", "1.1", json_data={
924+ "hooks": {"test2-app": {"test": "target-2"}}})
925 path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.test")
926 os.symlink(
927 os.path.join(self.temp_dir, "test-1", "1.0", "target-1"),
928@@ -465,13 +467,12 @@
929 "click_get_hooks_dir") as (enter, preloads):
930 enter()
931 self._setup_hooks_dir(preloads)
932- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
933- print(dedent("""\
934+ self._make_hook_file(dedent("""\
935 User-Level: yes
936 Pattern: ${home}/.local/share/test/${id}.test
937 # Comment
938 Exec: test-update
939- """), file=f)
940+ """))
941 hook = Click.Hook.open(self.db, "test")
942 self.assertCountEqual(
943 ["user-level", "pattern", "exec"], hook.get_fields())
944@@ -486,9 +487,10 @@
945 "click_get_hooks_dir") as (enter, preloads):
946 enter()
947 self._setup_hooks_dir(preloads)
948- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
949- print("User-Level: yes", file=f)
950- print("Pattern: ${home}/.local/share/test/${id}.test", file=f)
951+ self._make_hook_file(dedent("""\
952+ User-Level: yes
953+ Pattern: ${home}/.local/share/test/${id}.test""")
954+ )
955 hook = Click.Hook.open(self.db, "test")
956 self.assertEqual("test", hook.get_hook_name())
957
958@@ -497,10 +499,11 @@
959 "click_get_hooks_dir") as (enter, preloads):
960 enter()
961 self._setup_hooks_dir(preloads)
962- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
963- print("User-Level: yes", file=f)
964- print("Pattern: ${home}/.local/share/test/${id}.test", file=f)
965- print("Hook-Name: other", file=f)
966+ self._make_hook_file(dedent("""\
967+ User-Level: yes
968+ Pattern: ${home}/.local/share/test/${id}.test
969+ Hook-Name: other""")
970+ )
971 hook = Click.Hook.open(self.db, "test")
972 self.assertEqual("other", hook.get_hook_name())
973
974@@ -509,13 +512,12 @@
975 "click_get_hooks_dir") as (enter, preloads):
976 enter()
977 self._setup_hooks_dir(preloads)
978- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
979- print(dedent("""\
980+ self._make_hook_file(dedent("""\
981 User-Level: yes
982 Pattern: ${home}/.local/share/test/${id}.test
983 # Comment
984- Exec: test-update
985- """), file=f)
986+ Exec: test-update""")
987+ )
988 hook = Click.Hook.open(self.db, "test")
989 self.assertRaisesHooksError(
990 Click.HooksError.BAD_APP_NAME, hook.get_app_id,
991@@ -531,11 +533,10 @@
992 self._setup_hooks_dir(preloads)
993 preloads["getpwnam"].side_effect = (
994 lambda name: self.make_pointer(Passwd(pw_dir=b"/mock")))
995- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
996- print("User-Level: yes", file=f)
997- print(
998- "Pattern: ${home}/.local/share/test/${short-id}.test",
999- file=f)
1000+ self._make_hook_file(dedent("""\
1001+ User-Level: yes
1002+ Pattern: ${home}/.local/share/test/${short-id}.test
1003+ """))
1004 hook = Click.Hook.open(self.db, "test")
1005 self.assertEqual(
1006 "/mock/.local/share/test/package_app-name.test",
1007@@ -554,8 +555,8 @@
1008 print("Exec: test-update", file=f)
1009 hook = Click.Hook.open(self.db, "test")
1010 self.assertEqual(
1011- "test-user", hook.get_run_commands_user(user_name="test-user"))
1012- hook.run_commands(user_name="test-user")
1013+ self.TEST_USER, hook.get_run_commands_user(user_name=self.TEST_USER))
1014+ hook.run_commands(user_name=self.TEST_USER)
1015 self.assertEqual(
1016 [[b"/bin/sh", b"-c", b"test-update"]], self.spawn_calls)
1017
1018@@ -568,19 +569,20 @@
1019 preloads["click_get_user_home"].return_value = "/home/test-user"
1020 os.makedirs(os.path.join(
1021 self.temp_dir, "org.example.package", "1.0"))
1022- user_db = Click.User.for_user(self.db, "test-user")
1023+ user_db = Click.User.for_user(self.db, self.TEST_USER)
1024 user_db.set_version("org.example.package", "1.0")
1025- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1026- print("User-Level: yes", file=f)
1027- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1028+ self._make_hook_file(dedent("""\
1029+ User-Level: yes
1030+ Pattern: %s/${id}.test""") % self.temp_dir
1031+ )
1032 hook = Click.Hook.open(self.db, "test")
1033 hook.install_package(
1034 "org.example.package", "1.0", "test-app", "foo/bar",
1035- user_name="test-user")
1036+ user_name=self.TEST_USER)
1037 symlink_path = os.path.join(
1038 self.temp_dir, "org.example.package_test-app_1.0.test")
1039 target_path = os.path.join(
1040- self.temp_dir, ".click", "users", "test-user",
1041+ self.temp_dir, ".click", "users", self.TEST_USER,
1042 "org.example.package", "foo", "bar")
1043 self.assertTrue(os.path.islink(symlink_path))
1044 self.assertEqual(target_path, os.readlink(symlink_path))
1045@@ -594,19 +596,20 @@
1046 preloads["click_get_user_home"].return_value = "/home/test-user"
1047 os.makedirs(os.path.join(
1048 self.temp_dir, "org.example.package", "1.0"))
1049- user_db = Click.User.for_user(self.db, "test-user")
1050+ user_db = Click.User.for_user(self.db, self.TEST_USER)
1051 user_db.set_version("org.example.package", "1.0")
1052- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1053- print("User-Level: yes", file=f)
1054- print("Pattern: %s/${id}/" % self.temp_dir, file=f)
1055+ self._make_hook_file(dedent("""\
1056+ User-Level: yes
1057+ Pattern: %s/${id}/""") % self.temp_dir
1058+ )
1059 hook = Click.Hook.open(self.db, "test")
1060 hook.install_package(
1061 "org.example.package", "1.0", "test-app", "foo",
1062- user_name="test-user")
1063+ user_name=self.TEST_USER)
1064 symlink_path = os.path.join(
1065 self.temp_dir, "org.example.package_test-app_1.0")
1066 target_path = os.path.join(
1067- self.temp_dir, ".click", "users", "test-user",
1068+ self.temp_dir, ".click", "users", self.TEST_USER,
1069 "org.example.package", "foo")
1070 self.assertTrue(os.path.islink(symlink_path))
1071 self.assertEqual(target_path, os.readlink(symlink_path))
1072@@ -622,18 +625,19 @@
1073 self.temp_dir, "org.example.package", "1.0"))
1074 os.makedirs(os.path.join(
1075 self.temp_dir, "org.example.package", "1.1"))
1076- user_db = Click.User.for_user(self.db, "test-user")
1077+ user_db = Click.User.for_user(self.db, self.TEST_USER)
1078 user_db.set_version("org.example.package", "1.0")
1079- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1080- print("User-Level: yes", file=f)
1081- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1082+ self._make_hook_file(dedent("""\
1083+ User-Level: yes
1084+ Pattern: %s/${id}.test""") % self.temp_dir
1085+ )
1086 hook = Click.Hook.open(self.db, "test")
1087 hook.install_package(
1088 "org.example.package", "1.0", "test-app", "foo/bar",
1089- user_name="test-user")
1090+ user_name=self.TEST_USER)
1091 hook.install_package(
1092 "org.example.package", "1.1", "test-app", "foo/bar",
1093- user_name="test-user")
1094+ user_name=self.TEST_USER)
1095 old_symlink_path = os.path.join(
1096 self.temp_dir, "org.example.package_test-app_1.0.test")
1097 symlink_path = os.path.join(
1098@@ -641,7 +645,7 @@
1099 self.assertFalse(os.path.islink(old_symlink_path))
1100 self.assertTrue(os.path.islink(symlink_path))
1101 target_path = os.path.join(
1102- self.temp_dir, ".click", "users", "test-user",
1103+ self.temp_dir, ".click", "users", self.TEST_USER,
1104 "org.example.package", "foo", "bar")
1105 self.assertEqual(target_path, os.readlink(symlink_path))
1106
1107@@ -657,17 +661,18 @@
1108 os.symlink("old-target", symlink_path)
1109 os.makedirs(os.path.join(
1110 self.temp_dir, "org.example.package", "1.0"))
1111- user_db = Click.User.for_user(self.db, "test-user")
1112+ user_db = Click.User.for_user(self.db, self.TEST_USER)
1113 user_db.set_version("org.example.package", "1.0")
1114- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1115- print("User-Level: yes", file=f)
1116- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1117+ self._make_hook_file(dedent("""\
1118+ User-Level: yes
1119+ Pattern: %s/${id}.test""") % self.temp_dir
1120+ )
1121 hook = Click.Hook.open(self.db, "test")
1122 hook.install_package(
1123 "org.example.package", "1.0", "test-app", "foo/bar",
1124- user_name="test-user")
1125+ user_name=self.TEST_USER)
1126 target_path = os.path.join(
1127- self.temp_dir, ".click", "users", "test-user",
1128+ self.temp_dir, ".click", "users", self.TEST_USER,
1129 "org.example.package", "foo", "bar")
1130 self.assertTrue(os.path.islink(symlink_path))
1131 self.assertEqual(target_path, os.readlink(symlink_path))
1132@@ -679,16 +684,17 @@
1133 enter()
1134 self._setup_hooks_dir(preloads)
1135 preloads["click_get_user_home"].return_value = "/home/test-user"
1136- with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
1137- print("User-Level: yes", file=f)
1138- print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1139+ self._make_hook_file(dedent("""\
1140+ User-Level: yes
1141+ Pattern: %s/${id}.test""") % self.temp_dir
1142+ )
1143 symlink_path = os.path.join(
1144 self.temp_dir, "org.example.package_test-app_1.0.test")
1145 os.symlink("old-target", symlink_path)
1146 hook = Click.Hook.open(self.db, "test")
1147 hook.remove_package(
1148 "org.example.package", "1.0", "test-app",
1149- user_name="test-user")
1150+ user_name=self.TEST_USER)
1151 self.assertFalse(os.path.exists(symlink_path))
1152
1153 def test_install(self):
1154@@ -696,32 +702,26 @@
1155 "click_get_hooks_dir", "click_get_user_home",
1156 ) as (enter, preloads):
1157 enter()
1158+ # Don't tell click about the hooks directory yet.
1159 self._setup_hooks_dir(preloads)
1160 preloads["click_get_user_home"].return_value = "/home/test-user"
1161 with mkfile(os.path.join(self.temp_dir, "hooks", "new.hook")) as f:
1162 print("User-Level: yes", file=f)
1163 print("Pattern: %s/${id}.new" % self.temp_dir, file=f)
1164- user_db = Click.User.for_user(self.db, "test-user")
1165- with mkfile_utf8(os.path.join(
1166- self.temp_dir, "test-1", "1.0", ".click", "info",
1167- "test-1.manifest")) as f:
1168- json.dump({
1169+ self._make_installed_click("test-1", "1.0", json_data={
1170 "maintainer":
1171 b"Unic\xc3\xb3de <unicode@example.org>".decode(
1172 "UTF-8"),
1173 "hooks": {"test1-app": {"new": "target-1"}},
1174- }, f, ensure_ascii=False)
1175- user_db.set_version("test-1", "1.0")
1176- with mkfile_utf8(os.path.join(
1177- self.temp_dir, "test-2", "2.0", ".click", "info",
1178- "test-2.manifest")) as f:
1179- json.dump({
1180+ })
1181+ self._make_installed_click("test-2", "2.0", json_data={
1182 "maintainer":
1183 b"Unic\xc3\xb3de <unicode@example.org>".decode(
1184 "UTF-8"),
1185 "hooks": {"test1-app": {"new": "target-2"}},
1186- }, f, ensure_ascii=False)
1187- user_db.set_version("test-2", "2.0")
1188+ })
1189+ # Now tell click about the hooks directory and make sure it
1190+ # catches up correctly.
1191 self._setup_hooks_dir(
1192 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
1193 hook = Click.Hook.open(self.db, "new")
1194@@ -730,14 +730,14 @@
1195 self.assertTrue(os.path.lexists(path_1))
1196 self.assertEqual(
1197 os.path.join(
1198- self.temp_dir, ".click", "users", "test-user", "test-1",
1199+ self.temp_dir, ".click", "users", self.TEST_USER, "test-1",
1200 "target-1"),
1201 os.readlink(path_1))
1202 path_2 = os.path.join(self.temp_dir, "test-2_test1-app_2.0.new")
1203 self.assertTrue(os.path.lexists(path_2))
1204 self.assertEqual(
1205 os.path.join(
1206- self.temp_dir, ".click", "users", "test-user", "test-2",
1207+ self.temp_dir, ".click", "users", self.TEST_USER, "test-2",
1208 "target-2"),
1209 os.readlink(path_2))
1210
1211@@ -747,17 +747,17 @@
1212 self.assertFalse(os.path.lexists(path_1))
1213 self.assertFalse(os.path.lexists(path_2))
1214
1215- hook.install(user_name="test-user")
1216+ hook.install(user_name=self.TEST_USER)
1217 self.assertTrue(os.path.lexists(path_1))
1218 self.assertEqual(
1219 os.path.join(
1220- self.temp_dir, ".click", "users", "test-user", "test-1",
1221+ self.temp_dir, ".click", "users", self.TEST_USER, "test-1",
1222 "target-1"),
1223 os.readlink(path_1))
1224 self.assertTrue(os.path.lexists(path_2))
1225 self.assertEqual(
1226 os.path.join(
1227- self.temp_dir, ".click", "users", "test-user", "test-2",
1228+ self.temp_dir, ".click", "users", self.TEST_USER, "test-2",
1229 "target-2"),
1230 os.readlink(path_2))
1231
1232@@ -766,29 +766,25 @@
1233 "click_get_hooks_dir", "click_get_user_home",
1234 ) as (enter, preloads):
1235 enter()
1236+ # Don't tell click about the hooks directory yet.
1237 self._setup_hooks_dir(preloads)
1238 preloads["click_get_user_home"].return_value = "/home/test-user"
1239 with mkfile(os.path.join(self.temp_dir, "hooks", "old.hook")) as f:
1240 print("User-Level: yes", file=f)
1241 print("Pattern: %s/${id}.old" % self.temp_dir, file=f)
1242- user_db = Click.User.for_user(self.db, "test-user")
1243- with mkfile(os.path.join(
1244- self.temp_dir, "test-1", "1.0", ".click", "info",
1245- "test-1.manifest")) as f:
1246- json.dump({"hooks": {"test1-app": {"old": "target-1"}}}, f)
1247- user_db.set_version("test-1", "1.0")
1248- os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current"))
1249+ user_db = Click.User.for_user(self.db, self.TEST_USER)
1250+ self._make_installed_click("test-1", "1.0", json_data={
1251+ "hooks": {"test1-app": {"old": "target-1"}}})
1252 path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.old")
1253 os.symlink(
1254 os.path.join(user_db.get_path("test-1"), "target-1"), path_1)
1255- with mkfile(os.path.join(
1256- self.temp_dir, "test-2", "2.0", ".click", "info",
1257- "test-2.manifest")) as f:
1258- json.dump({"hooks": {"test2-app": {"old": "target-2"}}}, f)
1259- user_db.set_version("test-2", "2.0")
1260+ self._make_installed_click("test-2", "2.0", json_data={
1261+ "hooks": {"test2-app": {"old": "target-2"}}})
1262 path_2 = os.path.join(self.temp_dir, "test-2_test2-app_2.0.old")
1263 os.symlink(
1264 os.path.join(user_db.get_path("test-2"), "target-2"), path_2)
1265+ # Now tell click about the hooks directory and make sure it
1266+ # catches up correctly.
1267 self._setup_hooks_dir(
1268 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
1269 hook = Click.Hook.open(self.db, "old")
1270@@ -807,44 +803,37 @@
1271 os.path.join(self.temp_dir, "hooks", "test.hook")) as f:
1272 print("User-Level: yes", file=f)
1273 print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1274- user_db = Click.User.for_user(self.db, "test-user")
1275- with mkfile(os.path.join(
1276- self.temp_dir, "test-1", "1.0", ".click", "info",
1277- "test-1.manifest")) as f:
1278- json.dump({"hooks": {"test1-app": {"test": "target-1"}}}, f)
1279- user_db.set_version("test-1", "1.0")
1280- with mkfile(os.path.join(
1281- self.temp_dir, "test-2", "1.1", ".click", "info",
1282- "test-2.manifest")) as f:
1283- json.dump({"hooks": {"test2-app": {"test": "target-2"}}}, f)
1284- user_db.set_version("test-2", "1.1")
1285+ self._make_installed_click("test-1", "1.0", json_data={
1286+ "hooks": {"test1-app": {"test": "target-1"}}})
1287+ self._make_installed_click("test-2", "1.1", json_data={
1288+ "hooks": {"test2-app": {"test": "target-2"}}})
1289 path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.test")
1290 os.symlink(
1291 os.path.join(
1292- self.temp_dir, ".click", "users", "test-user", "test-1",
1293+ self.temp_dir, ".click", "users", self.TEST_USER, "test-1",
1294 "target-1"),
1295 path_1)
1296 path_2 = os.path.join(self.temp_dir, "test-2_test2-app_1.1.test")
1297 path_3 = os.path.join(self.temp_dir, "test-3_test3-app_1.0.test")
1298 os.symlink(
1299 os.path.join(
1300- self.temp_dir, ".click", "users", "test-user", "test-3",
1301+ self.temp_dir, ".click", "users", self.TEST_USER, "test-3",
1302 "target-3"),
1303 path_3)
1304 self._setup_hooks_dir(
1305 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
1306 hook = Click.Hook.open(self.db, "test")
1307- hook.sync(user_name="test-user")
1308+ hook.sync(user_name=self.TEST_USER)
1309 self.assertTrue(os.path.lexists(path_1))
1310 self.assertEqual(
1311 os.path.join(
1312- self.temp_dir, ".click", "users", "test-user", "test-1",
1313+ self.temp_dir, ".click", "users", self.TEST_USER, "test-1",
1314 "target-1"),
1315 os.readlink(path_1))
1316 self.assertTrue(os.path.lexists(path_2))
1317 self.assertEqual(
1318 os.path.join(
1319- self.temp_dir, ".click", "users", "test-user", "test-2",
1320+ self.temp_dir, ".click", "users", self.TEST_USER, "test-2",
1321 "target-2"),
1322 os.readlink(path_2))
1323 self.assertFalse(os.path.lexists(path_3))
1324@@ -859,18 +848,15 @@
1325 os.path.join(self.temp_dir, "hooks", "test.hook")) as f:
1326 print("User-Level: yes", file=f)
1327 print("Pattern: %s/${id}.test" % self.temp_dir, file=f)
1328- with mkfile(os.path.join(
1329- self.temp_dir, "test-package", "1.0", ".click", "info",
1330- "test-package.manifest")) as f:
1331- json.dump({"hooks": {"test-app": {"test": "target"}}}, f)
1332- all_users_db = Click.User.for_all_users(self.db)
1333- all_users_db.set_version("test-package", "1.0")
1334+ self._make_installed_click(
1335+ "test-package", "1.0", all_users=True, json_data={
1336+ "hooks": {"test-app": {"test": "target"}}})
1337 self._setup_hooks_dir(
1338 preloads, hooks_dir=os.path.join(self.temp_dir, "hooks"))
1339 hook = Click.Hook.open(self.db, "test")
1340- hook.sync(user_name="test-user")
1341+ hook.sync(user_name=self.TEST_USER)
1342 self.assertFalse(os.path.exists(os.path.join(
1343- self.temp_dir, ".click", "users", "test-user",
1344+ self.temp_dir, ".click", "users", self.TEST_USER,
1345 "test-package")))
1346
1347 def test_sync_uses_deepest_copy(self):
1348@@ -911,7 +897,7 @@
1349 underlay_user_link = os.path.join(
1350 underlay, ".click", "users", "@all", "test-package")
1351 overlay_user_link = os.path.join(
1352- overlay, ".click", "users", "test-user", "test-package")
1353+ overlay, ".click", "users", self.TEST_USER, "test-package")
1354 Click.ensuredir(os.path.dirname(underlay_user_link))
1355 os.symlink(underlay_unpacked, underlay_user_link)
1356 Click.ensuredir(os.path.dirname(overlay_user_link))
1357@@ -922,7 +908,7 @@
1358 overlay_target_path = os.path.join(overlay_user_link, "foo")
1359 os.symlink(overlay_target_path, symlink_path)
1360 hook = Click.Hook.open(db, "test")
1361- hook.sync(user_name="test-user")
1362+ hook.sync(user_name=self.TEST_USER)
1363 self.assertTrue(os.path.islink(underlay_user_link))
1364 self.assertEqual(
1365 underlay_unpacked, os.readlink(underlay_user_link))
1366@@ -962,17 +948,9 @@
1367 yelp_other_path = os.path.join(
1368 self.temp_dir, "yelp", "other-test_app_1.0.txt")
1369 os.symlink("dummy", yelp_other_path)
1370- package_dir = os.path.join(self.temp_dir, "test")
1371- with mkfile(os.path.join(
1372- package_dir, "1.0", ".click", "info",
1373- "test.manifest")) as f:
1374- json.dump(
1375- {"hooks": {"app": {"yelp": "foo.txt", "unity": "foo.scope"}}},
1376- f)
1377- with mkfile(os.path.join(
1378- package_dir, "1.1", ".click", "info",
1379- "test.manifest")) as f:
1380- json.dump({}, f)
1381+ self._make_installed_click("test", "1.0", make_current=False, json_data={
1382+ "hooks": {"app": {"yelp": "foo.txt", "unity": "foo.scope"}}})
1383+ self._make_installed_click("test", "1.1", json_data={})
1384 Click.package_install_hooks(
1385 self.db, "test", "1.0", "1.1", user_name=None)
1386 self.assertFalse(os.path.lexists(unity_path))
1387@@ -995,15 +973,10 @@
1388 print("Hook-Name: b", file=f)
1389 os.mkdir(os.path.join(self.temp_dir, "a"))
1390 os.mkdir(os.path.join(self.temp_dir, "b"))
1391- package_dir = os.path.join(self.temp_dir, "test")
1392- with mkfile(os.path.join(
1393- package_dir, "1.0", ".click", "info",
1394- "test.manifest")) as f:
1395- json.dump({"hooks": {}}, f)
1396- with mkfile(os.path.join(
1397- package_dir, "1.1", ".click", "info",
1398- "test.manifest")) as f:
1399- json.dump({"hooks": {"app": {"a": "foo.a", "b": "foo.b"}}}, f)
1400+ self._make_installed_click("test", "1.0", make_current=False,
1401+ json_data={"hooks": {}})
1402+ self._make_installed_click("test", "1.1", json_data={
1403+ "hooks": {"app": {"a": "foo.a", "b": "foo.b"}}})
1404 Click.package_install_hooks(
1405 self.db, "test", "1.0", "1.1", user_name=None)
1406 self.assertTrue(os.path.lexists(
1407@@ -1113,3 +1086,95 @@
1408 self.assertFalse(os.path.lexists(unity_path))
1409 self.assertFalse(os.path.lexists(yelp_docs_path))
1410 self.assertFalse(os.path.lexists(yelp_other_path))
1411+
1412+
1413+class TestPackageHooksValidateFramework(TestClickHookBase):
1414+
1415+ def _setup_test_env(self, preloads):
1416+ preloads["click_get_user_home"].return_value = "/home/test-user"
1417+ self._setup_hooks_dir(
1418+ preloads, os.path.join(self.temp_dir, "hooks"))
1419+ self._make_hook_file(dedent("""\
1420+ User-Level: yes
1421+ Pattern: %s/${id}.test
1422+ """) % self.temp_dir)
1423+ self.hook_symlink_path = os.path.join(
1424+ self.temp_dir, "test-1_test1-app_1.0.test")
1425+
1426+ def test_links_are_kept_on_validate_framework(self):
1427+ with self.run_in_subprocess(
1428+ "click_get_hooks_dir", "click_get_user_home",
1429+ "click_get_frameworks_dir",
1430+ ) as (enter, preloads):
1431+ enter()
1432+ self._setup_frameworks(
1433+ preloads, frameworks=["ubuntu-sdk-13.10"])
1434+ self._setup_test_env(preloads)
1435+ self._make_installed_click(json_data={
1436+ "framework": "ubuntu-sdk-13.10",
1437+ "hooks": {
1438+ "test1-app": {"test": "target-1"}
1439+ },
1440+ })
1441+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1442+ # run the hooks
1443+ Click.run_user_hooks(self.db, user_name=self.TEST_USER)
1444+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1445+
1446+ def test_links_are_kept_multiple_frameworks(self):
1447+ with self.run_in_subprocess(
1448+ "click_get_hooks_dir", "click_get_user_home",
1449+ "click_get_frameworks_dir",
1450+ ) as (enter, preloads):
1451+ enter()
1452+ self._setup_frameworks(
1453+ preloads, frameworks=["ubuntu-sdk-14.04", "ubuntu-sdk-13.10"])
1454+ self._setup_test_env(preloads)
1455+ self._make_installed_click(json_data={
1456+ "framework": "ubuntu-sdk-13.10",
1457+ "hooks": {
1458+ "test1-app": {"test": "target-1"}
1459+ },
1460+ })
1461+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1462+ # run the hooks
1463+ Click.run_user_hooks(self.db, user_name=self.TEST_USER)
1464+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1465+
1466+ def test_links_are_removed_on_missing_framework(self):
1467+ with self.run_in_subprocess(
1468+ "click_get_hooks_dir", "click_get_user_home",
1469+ "click_get_frameworks_dir",
1470+ ) as (enter, preloads):
1471+ enter()
1472+ self._setup_frameworks(preloads, frameworks=["missing"])
1473+ self._setup_test_env(preloads)
1474+ self._make_installed_click(json_data={
1475+ "framework": "ubuntu-sdk-13.10",
1476+ "hooks": {
1477+ "test1-app": {"test": "target-1"}
1478+ },
1479+ })
1480+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1481+ # run the hooks
1482+ Click.run_user_hooks(self.db, user_name=self.TEST_USER)
1483+ self.assertFalse(os.path.lexists(self.hook_symlink_path))
1484+
1485+ def test_links_are_removed_on_missing_multiple_framework(self):
1486+ with self.run_in_subprocess(
1487+ "click_get_hooks_dir", "click_get_user_home",
1488+ "click_get_frameworks_dir",
1489+ ) as (enter, preloads):
1490+ enter()
1491+ self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"])
1492+ self._setup_test_env(preloads)
1493+ self._make_installed_click(json_data={
1494+ "framework": "ubuntu-sdk-13.10, ubuntu-sdk-13.10-html",
1495+ "hooks": {
1496+ "test1-app": {"test": "target-1"}
1497+ },
1498+ })
1499+ self.assertTrue(os.path.lexists(self.hook_symlink_path))
1500+ # run the hooks
1501+ Click.run_user_hooks(self.db, user_name=self.TEST_USER)
1502+ self.assertFalse(os.path.lexists(self.hook_symlink_path))
1503
1504=== modified file 'click/tests/test_install.py'
1505--- click/tests/test_install.py 2014-05-05 13:23:35 +0000
1506+++ click/tests/test_install.py 2014-05-20 11:54:03 +0000
1507@@ -43,7 +43,13 @@
1508 ClickInstallerPermissionDenied,
1509 )
1510 from click.preinst import static_preinst
1511-from click.tests.helpers import TestCase, mkfile, mock, touch
1512+from click.tests.helpers import (
1513+ disable_logging,
1514+ mkfile,
1515+ mock,
1516+ TestCase,
1517+ touch,
1518+)
1519
1520
1521 @contextmanager
1522@@ -103,14 +109,6 @@
1523 self.temp_dir, control_dir, data_dir, package_path)
1524 return package_path
1525
1526- def _setup_frameworks(self, preloads, frameworks_dir=None, frameworks=[]):
1527- frameworks_dir = self._create_mock_framework_dir(frameworks_dir)
1528- shutil.rmtree(frameworks_dir, ignore_errors=True)
1529- for framework in frameworks:
1530- self._create_mock_framework_file(framework)
1531- preloads["click_get_frameworks_dir"].side_effect = (
1532- lambda: self.make_string(frameworks_dir))
1533-
1534 def test_audit_no_click_version(self):
1535 path = self.make_fake_package()
1536 self.assertRaisesRegex(
1537@@ -234,6 +232,7 @@
1538 'Framework "missing" not present on system.*',
1539 ClickInstaller(self.db).audit, path)
1540
1541+ @disable_logging
1542 def test_audit_missing_framework_force(self):
1543 with self.run_in_subprocess(
1544 "click_get_frameworks_dir") as (enter, preloads):
1545@@ -659,6 +658,7 @@
1546 self.assertTrue(
1547 os.path.exists(os.path.join(root, "test-package", "current")))
1548
1549+ @disable_logging
1550 def test_reinstall_preinstalled(self):
1551 # Attempting to reinstall a preinstalled version shouldn't actually
1552 # reinstall it in an overlay database (which would cause
1553
1554=== modified file 'debian/changelog'
1555--- debian/changelog 2014-05-14 06:28:34 +0000
1556+++ debian/changelog 2014-05-20 11:54:03 +0000
1557@@ -1,3 +1,33 @@
1558+click (0.4.23) UNRELEASED; urgency=medium
1559+
1560+ [ Michael Vogt ]
1561+ * Show human-readable error message when a click chroot subcommand fails
1562+ because of existing or non-existing chroots (LP: #1296820).
1563+ * Selectively disable logging on some tests to avoid message spam during
1564+ the test runs.
1565+ * When running hooks, remove hook symlinks if framework requirements are
1566+ not met (LP: #1271944).
1567+ * Cleanup the chroot if "click chroot create" fails (unless
1568+ --keep-broken-chroot is used)
1569+ * Fix sources.list generation when native_arch and target_arch are on the
1570+ same archive server (part of LP #1319153).
1571+ * Add "click framework list" command to list available frameworks
1572+ (LP: #1294659).
1573+
1574+ [ Pete Woods ]
1575+ * Add libunity-scopes-dev package to chroot (LP: #1320786).
1576+
1577+ [ Sergio Schvezov ]
1578+ * click chroot creation depends on dpkg-architecture, so recommend
1579+ dpkg-dev.
1580+
1581+ [ Colin Watson ]
1582+ * chroot: Handle the case where we can execute binaries for the target
1583+ architecture directly and thus don't need a cross-compiler
1584+ (LP: #1319153).
1585+
1586+ -- Colin Watson <cjwatson@ubuntu.com> Thu, 15 May 2014 17:10:58 +0100
1587+
1588 click (0.4.22) utopic; urgency=medium
1589
1590 [ Michael Vogt ]
1591
1592=== modified file 'debian/control'
1593--- debian/control 2014-03-13 13:56:57 +0000
1594+++ debian/control 2014-05-20 11:54:03 +0000
1595@@ -28,7 +28,7 @@
1596 Architecture: any
1597 Multi-Arch: foreign
1598 Depends: ${misc:Depends}, ${perl:Depends}, python3-click (= ${binary:Version})
1599-Recommends: debootstrap, schroot
1600+Recommends: debootstrap, schroot, dpkg-dev
1601 Description: build Click packages
1602 Click is a simplified packaging format that installs in a separate part of
1603 the file system, suitable for third-party applications.
1604
1605=== modified file 'doc/index.rst'
1606--- doc/index.rst 2014-05-08 12:49:11 +0000
1607+++ doc/index.rst 2014-05-20 11:54:03 +0000
1608@@ -38,8 +38,10 @@
1609 Then run::
1610
1611 $ ./autogen.sh
1612- $ ./configure --with-systemdsystemunitdir=/lib/systemd/system \
1613- --with-systemduserunitdir=/usr/lib/systemd/user
1614+ $ ./configure --prefix=/usr \
1615+ --sysconfdir=/etc \
1616+ --with-systemdsystemunitdir=/lib/systemd/system \
1617+ --with-systemduserunitdir=/usr/lib/systemd/user
1618 $ make
1619
1620 to build the project.
1621
1622=== modified file 'doc/manpage.rst'
1623--- doc/manpage.rst 2014-03-18 11:47:21 +0000
1624+++ doc/manpage.rst 2014-05-20 11:54:03 +0000
1625@@ -35,6 +35,7 @@
1626 click buildsource DIRECTORY
1627 click chroot
1628 click contents PATH
1629+ click framework list
1630 click hook install HOOK
1631 click hook remove HOOK
1632 click hook run-system
1633@@ -148,6 +149,11 @@
1634
1635 Display the contents of the Click package in PATH as a file listing.
1636
1637+click framework list
1638+--------------------
1639+
1640+Display a list of available frameworks as one framework per line.
1641+
1642 click hook install HOOK
1643 -----------------------
1644
1645
1646=== modified file 'lib/click/hooks.vala'
1647--- lib/click/hooks.vala 2014-04-08 09:31:40 +0000
1648+++ lib/click/hooks.vala 2014-05-20 11:54:03 +0000
1649@@ -56,9 +56,57 @@
1650 INCOMPLETE
1651 }
1652
1653+
1654+/* vala implementation of click.framework.validate_framework()
1655+ *
1656+ * Note that the required_frameworks string has the form
1657+ * framework1, framework2, ...
1658+ * See doc/file-format.rst for details.
1659+ */
1660+private bool
1661+validate_framework (string required_frameworks)
1662+{
1663+ // valid framework names, cf. debian policy ยง5.6.1
1664+ Regex valid_framework_re;
1665+ try {
1666+ valid_framework_re = new Regex ("^[a-z][a-z0-9.+-]+");
1667+ } catch (RegexError e) {
1668+ error ("Could not compile regex /^[a-z][a-z0-9.+-]+/: %s",
1669+ e.message);
1670+ }
1671+ var base_version = "";
1672+ foreach (var framework_name in required_frameworks.split (","))
1673+ {
1674+ framework_name = framework_name.strip ();
1675+ if (!valid_framework_re.match (framework_name))
1676+ return false;
1677+ Framework framework;
1678+ // now check the base-version
1679+ try {
1680+ framework = Framework.open (framework_name);
1681+ } catch (FrameworkError e) {
1682+ return false;
1683+ }
1684+ if (base_version == "")
1685+ base_version = framework.get_base_version ();
1686+ if (base_version != framework.get_base_version ())
1687+ return false;
1688+ }
1689+ return true;
1690+}
1691+
1692+private bool
1693+validate_framework_for_package (DB db, string package, string? version)
1694+{
1695+ var manifest = read_manifest (db, package, version);
1696+ if (!manifest.has_member ("framework"))
1697+ return true;
1698+ var required_frameworks = manifest.get_string_member ("framework");
1699+ return validate_framework (required_frameworks);
1700+}
1701+
1702 private Json.Object
1703-read_manifest_hooks (DB db, string package, string? version)
1704- throws DatabaseError
1705+read_manifest (DB db, string package, string? version)
1706 {
1707 if (version == null)
1708 return new Json.Object ();
1709@@ -69,15 +117,22 @@
1710 @"$package.manifest");
1711 parser.load_from_file (manifest_path);
1712 var manifest = parser.get_root ().get_object ();
1713- if (! manifest.has_member ("hooks"))
1714- return new Json.Object ();
1715- var hooks = manifest.get_object_member ("hooks");
1716- return hooks.ref ();
1717+ return manifest.ref ();
1718 } catch (Error e) {
1719 return new Json.Object ();
1720 }
1721 }
1722
1723+private Json.Object
1724+read_manifest_hooks (DB db, string package, string? version)
1725+{
1726+ var manifest = read_manifest (db, package, version);
1727+ if (! manifest.has_member ("hooks"))
1728+ return new Json.Object ();
1729+ var hooks = manifest.get_object_member ("hooks");
1730+ return hooks.ref ();
1731+}
1732+
1733 private class PreviousEntry : Object, Gee.Hashable<PreviousEntry> {
1734 public string path { get; construct; }
1735 public string package { get; construct; }
1736@@ -889,10 +944,16 @@
1737 var ret = new List<RelevantApp> ();
1738 var hook_name = get_hook_name ();
1739 foreach (var unpacked in get_all_packages (user_name)) {
1740- var manifest = read_manifest_hooks
1741+ // if the app is not using a valid framework (anymore)
1742+ // we don't consider it relevant (anymore)
1743+ if (!validate_framework_for_package
1744+ (db, unpacked.package, unpacked.version))
1745+ continue;
1746+
1747+ var manifest_hooks = read_manifest_hooks
1748 (db, unpacked.package, unpacked.version);
1749- foreach (var app_name in manifest.get_members ()) {
1750- var hooks = manifest.get_object_member
1751+ foreach (var app_name in manifest_hooks.get_members ()) {
1752+ var hooks = manifest_hooks.get_object_member
1753 (app_name);
1754 if (hooks.has_member (hook_name)) {
1755 var relative_path = hooks.get_string_member
1756@@ -960,6 +1021,7 @@
1757 unowned string package = app.package;
1758 unowned string version = app.version;
1759 unowned string app_name = app.app_name;
1760+
1761 seen.add (@"$(package)_$(app_name)_$(version)");
1762 if (is_user_level) {
1763 var user_db = new User.for_user
1764@@ -1016,7 +1078,7 @@
1765 * @new_version: The new version of the package.
1766 * @user_name: (allow-none): A user name, or null.
1767 *
1768- * Run hooks following removal of a Click package.
1769+ * Run hooks following install of a Click package.
1770 *
1771 * If @user_name is null, only run system-level hooks. If @user_name is not
1772 * null, only run user-level hooks for that user.

Subscribers

People subscribed via source and target branches

to all changes: