Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 import os
6 import struct
7 import subprocess
8 from mozpack.errors import errors
10 MACHO_SIGNATURES = [
11 0xfeedface, # mach-o 32-bits big endian
12 0xcefaedfe, # mach-o 32-bits little endian
13 0xfeedfacf, # mach-o 64-bits big endian
14 0xcffaedfe, # mach-o 64-bits little endian
15 ]
17 FAT_SIGNATURE = 0xcafebabe # mach-o FAT binary
19 ELF_SIGNATURE = 0x7f454c46 # Elf binary
21 UNKNOWN = 0
22 MACHO = 1
23 ELF = 2
25 def get_type(path):
26 '''
27 Check the signature of the give file and returns what kind of executable
28 matches.
29 '''
30 with open(path, 'rb') as f:
31 signature = f.read(4)
32 if len(signature) < 4:
33 return UNKNOWN
34 signature = struct.unpack('>L', signature)[0]
35 if signature == ELF_SIGNATURE:
36 return ELF
37 if signature in MACHO_SIGNATURES:
38 return MACHO
39 if signature != FAT_SIGNATURE:
40 return UNKNOWN
41 # We have to sanity check the second four bytes, because Java class
42 # files use the same magic number as Mach-O fat binaries.
43 # This logic is adapted from file(1), which says that Mach-O uses
44 # these bytes to count the number of architectures within, while
45 # Java uses it for a version number. Conveniently, there are only
46 # 18 labelled Mach-O architectures, and Java's first released
47 # class format used the version 43.0.
48 num = f.read(4)
49 if len(num) < 4:
50 return UNKNOWN
51 num = struct.unpack('>L', num)[0]
52 if num < 20:
53 return MACHO
54 return UNKNOWN
57 def is_executable(path):
58 '''
59 Return whether a given file path points to an executable or a library,
60 where an executable or library is identified by:
61 - the file extension on OS/2 and WINNT
62 - the file signature on OS/X and ELF systems (GNU/Linux, Android, BSD,
63 Solaris)
65 As this function is intended for use to choose between the ExecutableFile
66 and File classes in FileFinder, and choosing ExecutableFile only matters
67 on OS/2, OS/X, ELF and WINNT (in GCC build) systems, we don't bother
68 detecting other kind of executables.
69 '''
70 from buildconfig import substs
71 if not os.path.exists(path):
72 return False
74 if substs['OS_ARCH'] == 'WINNT':
75 return path.lower().endswith((substs['DLL_SUFFIX'],
76 substs['BIN_SUFFIX']))
78 return get_type(path) != UNKNOWN
81 def may_strip(path):
82 '''
83 Return whether strip() should be called
84 '''
85 from buildconfig import substs
86 return not substs['PKG_SKIP_STRIP']
89 def strip(path):
90 '''
91 Execute the STRIP command with STRIP_FLAGS on the given path.
92 '''
93 from buildconfig import substs
94 strip = substs['STRIP']
95 flags = substs['STRIP_FLAGS'].split() if 'STRIP_FLAGS' in substs else []
96 cmd = [strip] + flags + [path]
97 if subprocess.call(cmd) != 0:
98 errors.fatal('Error executing ' + ' '.join(cmd))
101 def may_elfhack(path):
102 '''
103 Return whether elfhack() should be called
104 '''
105 # elfhack only supports libraries. We should check the ELF header for
106 # the right flag, but checking the file extension works too.
107 from buildconfig import substs
108 return 'USE_ELF_HACK' in substs and substs['USE_ELF_HACK'] and \
109 path.endswith(substs['DLL_SUFFIX'])
112 def elfhack(path):
113 '''
114 Execute the elfhack command on the given path.
115 '''
116 from buildconfig import topobjdir
117 cmd = [os.path.join(topobjdir, 'build/unix/elfhack/elfhack'), path]
118 if 'ELF_HACK_FLAGS' in os.environ:
119 cmd[1:0] = os.environ['ELF_HACK_FLAGS'].split()
120 if subprocess.call(cmd) != 0:
121 errors.fatal('Error executing ' + ' '.join(cmd))