Merge lp:~james-w/pkgme-devportal/executable-executables into lp:pkgme-devportal

Proposed by James Westby
Status: Merged
Approved by: James Westby
Approved revision: 126
Merged at revision: 125
Proposed branch: lp:~james-w/pkgme-devportal/executable-executables
Merge into: lp:pkgme-devportal
Diff against target: 251 lines (+99/-26)
3 files modified
devportalbinary/binary.py (+34/-11)
devportalbinary/metadata.py (+11/-10)
devportalbinary/tests/test_binary.py (+54/-5)
To merge this branch: bzr merge lp:~james-w/pkgme-devportal/executable-executables
Reviewer Review Type Date Requested Status
Jonathan Lange Approve
Review via email: mp+124803@code.launchpad.net

Commit message

Make executables executable.

The executable may not be marked exectuable (e.g. if it was delivered
in a .zip), so we also consider any ELF executables as candidate exectuables.

That will make it more likely we find the right thing, but it won't be
enough to actually make that file exectuable. Therefore we override
dh_fixperms to chmod the file at build time so it will be installed correctly.

We also tell dh_fixperms to not adjust anything in /opt so that it doesn't
remove any needed exectuable bits.

Description of the change

Hi,

This tries to make executable handling more robust and lenient.

It should now work for things delivered as zip files, and will also
do a better job at making sure that any executables stay executable.

It also fixes another instance of the package name bug.

The branch is a bit messy, sorry. I couldn't think of any changes to
make the code more sensible though.

Thanks,

James

To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote :

Looks good. The only thing I'd consider changing is making the "special casing" a bit less special by making it a loop or something similar.

review: Approve
126. By James Westby

Use a loop for the elements requiring package name. Thanks jml.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'devportalbinary/binary.py'
--- devportalbinary/binary.py 2012-09-11 17:19:18 +0000
+++ devportalbinary/binary.py 2012-09-18 14:36:18 +0000
@@ -59,9 +59,14 @@
59# the overrides template for a dh_shlibdeps59# the overrides template for a dh_shlibdeps
60OVERRIDE_DH_SHLIBDEPS_TEMPLATE = """60OVERRIDE_DH_SHLIBDEPS_TEMPLATE = """
61override_dh_shlibdeps:61override_dh_shlibdeps:
62\tdh_shlibdeps -l%s62\tdh_shlibdeps -l{ld_search_path}
63"""63"""
6464
65OVERRIDE_DH_FIXPERMS_TEMPLATE = """
66override_dh_fixperms:
67\tchmod u+x debian/{package_name}/{executable_path}
68\tdh_fixperms -X/opt/
69"""
6570
66# the default extra debian/rules targets71# the default extra debian/rules targets
67DEFAULT_EXTRA_RULES_TARGETS = OVERRIDE_DH_STRIP_TEMPLATE72DEFAULT_EXTRA_RULES_TARGETS = OVERRIDE_DH_STRIP_TEMPLATE
@@ -160,16 +165,26 @@
160 return None165 return None
161166
162167
168def is_elf_exectuable(file_type):
169 """Whether `file_type` indicates an ELF executable."""
170 if file_type.startswith('ELF'):
171 if "executable" in file_type.split(",")[0]:
172 return True
173 return False
174
175
163def iter_executables(path):176def iter_executables(path):
164 """Iterate through all executable files under 'path'.177 """Iterate through all executable files under 'path'.
165178
166 Paths yielded will be relative to 'path'. Directories will *not* be179 Paths yielded will be relative to 'path'. Directories will *not* be
167 yielded. "Executable" is determined by filesystem permissions.180 yielded. A file is considered "executable" if it has the executable
181 bit set, or if it is an ELF executable.
168 """182 """
169 for root, dirs, files in os.walk(path):183 for root, dirs, files in os.walk(path):
170 for filename in files:184 for filename in files:
171 file_path = os.path.join(root, filename)185 file_path = os.path.join(root, filename)
172 if os.access(file_path, os.X_OK):186 if (os.access(file_path, os.X_OK)
187 or is_elf_exectuable(get_file_type(file_path))):
173 yield os.path.relpath(file_path, path)188 yield os.path.relpath(file_path, path)
174189
175190
@@ -189,7 +204,7 @@
189OBJDUMP = '/usr/bin/objdump'204OBJDUMP = '/usr/bin/objdump'
190# Map the objdump "architecture" value to the dpkg architecture,205# Map the objdump "architecture" value to the dpkg architecture,
191# objdump report i386 as "i386" but amd64 as "i386:x86-64" 206# objdump report i386 as "i386" but amd64 as "i386:x86-64"
192OBJDUMP_MAPPING = { 207OBJDUMP_MAPPING = {
193 "i386:x86-64" : "amd64",208 "i386:x86-64" : "amd64",
194}209}
195210
@@ -319,15 +334,21 @@
319def guess_extra_debian_rules_targets(334def guess_extra_debian_rules_targets(
320 package_name,335 package_name,
321 path,336 path,
322 embedded_libs_finder=guess_embedded_libs_search_paths):337 embedded_libs_finder=guess_embedded_libs_search_paths,
338 executable_path=None):
323 embedded_paths = embedded_libs_finder(path)339 embedded_paths = embedded_libs_finder(path)
324 extra_targets = DEFAULT_EXTRA_RULES_TARGETS340 extra_targets = DEFAULT_EXTRA_RULES_TARGETS
325 if embedded_paths:341 if embedded_paths:
326 base_dir = "$(CURDIR)/debian/%(package_name)s/opt/" \342 base_dir = "$(CURDIR)/debian/{package_name}/opt/" \
327 "%(package_name)s/" % { 'package_name' : package_name }343 "{package_name}/".format(package_name=package_name)
328 ld_search_path = build_ld_library_search_path(344 ld_search_path = build_ld_library_search_path(
329 base_dir, embedded_paths)345 base_dir, embedded_paths)
330 extra_targets += OVERRIDE_DH_SHLIBDEPS_TEMPLATE % ld_search_path346 extra_targets += OVERRIDE_DH_SHLIBDEPS_TEMPLATE.format(
347 ld_search_path=ld_search_path)
348 if executable_path:
349 extra_targets += OVERRIDE_DH_FIXPERMS_TEMPLATE.format(
350 executable_path=executable_path,
351 package_name=package_name)
331 return extra_targets352 return extra_targets
332353
333354
@@ -401,12 +422,14 @@
401This file was automatically generated.422This file was automatically generated.
402""" % (maintainer_suffix,)423""" % (maintainer_suffix,)
403424
404 def get_extra_targets(self):425 def get_extra_targets(self, package_name):
405 package_name = self.get_package_name()426 executable_path = self.get_executable(package_name)
406 return guess_extra_debian_rules_targets(package_name, self.path)427 return guess_extra_debian_rules_targets(package_name, self.path,
428 executable_path=executable_path)
407429
408 @classmethod430 @classmethod
409 def want_with_metadata(cls, path, metadata):431 def want_with_metadata(cls, path, metadata):
432 # XXX: should it also check that there is an executable?
410 if list(iter_binaries(path)):433 if list(iter_binaries(path)):
411 return {434 return {
412 'score': 10,435 'score': 10,
413436
=== modified file 'devportalbinary/metadata.py'
--- devportalbinary/metadata.py 2012-09-17 16:05:32 +0000
+++ devportalbinary/metadata.py 2012-09-18 14:36:18 +0000
@@ -353,7 +353,7 @@
353 def get_extra_control_binary_fields(self):353 def get_extra_control_binary_fields(self):
354 return None354 return None
355355
356 def get_extra_targets(self):356 def get_extra_targets(self, package_name):
357 """Return any extra debian/rules targets. """357 """Return any extra debian/rules targets. """
358 return ""358 return ""
359359
@@ -537,12 +537,16 @@
537 Distribution,537 Distribution,
538 ExplicitCopyright,538 ExplicitCopyright,
539 ExtraControlBinaryFields,539 ExtraControlBinaryFields,
540 ExtraTargets,
541 Maintainer,540 Maintainer,
542 Section,541 Section,
543 Version,542 Version,
544 Homepage,543 Homepage,
545 ]544 ]
545 PACKAGE_NAME_ELEMENTS = [
546 ExtraFiles,
547 ExtraFilesFromPaths,
548 ExtraTargets,
549 ]
546 info = {}550 info = {}
547 for element in COMPULSORY_ELEMENTS:551 for element in COMPULSORY_ELEMENTS:
548 info[element] = self._calculate_info_element(element)552 info[element] = self._calculate_info_element(element)
@@ -550,15 +554,12 @@
550 value = self._calculate_info_element(element)554 value = self._calculate_info_element(element)
551 if value:555 if value:
552 info[element] = value556 info[element] = value
553 # Special-case ExtraFiles and ExtraFilesFromPaths since they need557 # Special-case those elements that need the package name
554 # PackageName.
555 package_name = PackageName.clean(info[PackageName])558 package_name = PackageName.clean(info[PackageName])
556 info[ExtraFiles] = self._calculate_info_element(559 for element in PACKAGE_NAME_ELEMENTS:
557 ExtraFiles, package_name)560 value = self._calculate_info_element(element, package_name)
558 extra_files_from_paths = self._calculate_info_element(561 if value:
559 ExtraFilesFromPaths, package_name)562 info[element] = value
560 if extra_files_from_paths:
561 info[ExtraFilesFromPaths] = extra_files_from_paths
562 return info563 return info
563564
564 @classmethod565 @classmethod
565566
=== modified file 'devportalbinary/tests/test_binary.py'
--- devportalbinary/tests/test_binary.py 2012-09-11 17:19:18 +0000
+++ devportalbinary/tests/test_binary.py 2012-09-18 14:36:18 +0000
@@ -30,6 +30,7 @@
30 guess_embedded_libs_search_paths,30 guess_embedded_libs_search_paths,
31 guess_executable,31 guess_executable,
32 guess_extra_debian_rules_targets,32 guess_extra_debian_rules_targets,
33 is_elf_exectuable,
33 iter_binaries,34 iter_binaries,
34 iter_executables,35 iter_executables,
35 needed_libraries_from_objdump,36 needed_libraries_from_objdump,
@@ -69,6 +70,16 @@
69 executables = list(iter_executables(path))70 executables = list(iter_executables(path))
70 self.assertEqual(['some-file'], executables)71 self.assertEqual(['some-file'], executables)
7172
73 def test_finds_elf_executable(self):
74 # ELF executables are considered executables.
75 executable = get_test_data_file_path('hello', 'hello')
76 with open(executable, 'rb') as f:
77 content = f.read()
78 tree = FileTree({'some-file': {PERMISSIONS: 0644, CONTENT: content}})
79 path = self.useFixture(tree).path
80 executables = list(iter_executables(path))
81 self.assertEqual(['some-file'], executables)
82
72 def test_no_files_at_all(self):83 def test_no_files_at_all(self):
73 # iter_executables finds no executables if there are no files at all.84 # iter_executables finds no executables if there are no files at all.
74 path = self.useFixture(FileTree({})).path85 path = self.useFixture(FileTree({})).path
@@ -383,21 +394,36 @@
383 conf = load_configuration()394 conf = load_configuration()
384 self.assertEqual(backend.get_architecture(), "i386")395 self.assertEqual(backend.get_architecture(), "i386")
385396
397 def test_get_extra_targets_makes_executable_executable(self):
398 package_name = 'package'
399 executable_path = 'a/binary'
400 path = self.getUniqueString()
401 extra_targets = guess_extra_debian_rules_targets(package_name, path,
402 lambda l: [], executable_path=executable_path)
403 self.assertIn(
404 "override_dh_fixperms:"
405 "\n\tchmod u+x debian/{1}/{0}"
406 "\n\tdh_fixperms -X/opt/".format(executable_path,
407 package_name),
408 extra_targets)
409
386 def test_get_extra_targets_no_bundled_libs(self):410 def test_get_extra_targets_no_bundled_libs(self):
387 package_name = self.getUniqueString()411 package_name = self.getUniqueString()
388 path = self.getUniqueString()412 path = self.getUniqueString()
389 self.assertEqual(413 self.assertEqual(
390 guess_extra_debian_rules_targets(package_name, path, lambda l: []),414 DEFAULT_EXTRA_RULES_TARGETS,
391 DEFAULT_EXTRA_RULES_TARGETS)415 guess_extra_debian_rules_targets(package_name, path, lambda l: []))
392416
393 def test_get_extra_targets_bundled_libs(self):417 def test_get_extra_targets_bundled_libs(self):
394 package_name = "a-pkgname"418 package_name = "a-pkgname"
395 path = self.getUniqueString()419 path = self.getUniqueString()
396 ld_search_path = ["x86/lib"]420 ld_search_path = ["x86/lib"]
421 expected_ld_search_path = ("$(CURDIR)/debian/{package_name}"
422 "/opt/{package_name}/{search_path}").format(package_name=package_name,
423 search_path=ld_search_path[0])
397 self.assertEqual(424 self.assertEqual(
398 DEFAULT_EXTRA_RULES_TARGETS+OVERRIDE_DH_SHLIBDEPS_TEMPLATE % (425 DEFAULT_EXTRA_RULES_TARGETS+OVERRIDE_DH_SHLIBDEPS_TEMPLATE.format(
399 "$(CURDIR)/debian/%s/opt/%s/%s" % (426 ld_search_path=expected_ld_search_path),
400 package_name, package_name, ld_search_path[0])),
401 guess_extra_debian_rules_targets(427 guess_extra_debian_rules_targets(
402 package_name, path, lambda l: ld_search_path))428 package_name, path, lambda l: ld_search_path))
403429
@@ -422,3 +448,26 @@
422This file was automatically generated.448This file was automatically generated.
423""" % (submitter,)449""" % (submitter,)
424 self.assertEqual(expected, backend.get_explicit_copyright())450 self.assertEqual(expected, backend.get_explicit_copyright())
451
452
453class IsElfExecutableTests(TestCase):
454
455 def test_not_elf(self):
456 self.assertEqual(False, is_elf_exectuable('blah'))
457
458 def test_not_executable(self):
459 self.assertEqual(False,
460 is_elf_exectuable('ELF 32-bit LSB shared object'))
461
462 def test_executable(self):
463 self.assertEqual(True,
464 is_elf_exectuable('ELF 32-bit LSB executable'))
465
466 def test_executable_with_extra_junk(self):
467 self.assertEqual(True,
468 is_elf_exectuable('ELF 32-bit LSB executable, blah'))
469
470 def test_not_executable_with_extra_junk(self):
471 self.assertEqual(False,
472 is_elf_exectuable(
473 'ELF 32-bit LSB shared object, blah executable'))

Subscribers

People subscribed via source and target branches