Mike Sullivan
2011-08-08 1de4f073716d4b37e985601de9505db5caeb2a4e
commit | author | age
4158c0 1 #!/usr/bin/python2.6
NJ 2 #
3 # CDDL HEADER START
4 #
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
8 #
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
22 # Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
23 #
24 #
25 # userland-mangler - a file mangling utility
26 #
27 #  A simple program to mangle files to conform to Solaris WOS or Consoldation
28 #  requirements.
29 #
30
31 import os
32 import sys
33 import re
34
35 import pkg.fmri
36 import pkg.manifest
37 import pkg.actions
38 import pkg.elf as elf
39
40 attribute_table_header = """
41 .SH ATTRIBUTES
42 See
43 .BR attributes (5)
44 for descriptions of the following attributes:
45 .sp
46 .TS
47 box;
48 cbp-1 | cbp-1
49 l | l .
50 ATTRIBUTE TYPE    ATTRIBUTE VALUE """
51
52 attribute_table_availability = """
53 =
54 Availability    %s"""
55
56 attribute_table_stability = """
57 =
58 Stability    %s"""
59
60 attribute_table_footer = """
61 .TE 
62 .PP
63 """
2720d6 64 def attributes_section_text(availability, stability):
NJ 65     result = ''
66
4158c0 67     # is there anything to do?
2720d6 68     if availability is not None or stability is not None:
NJ 69         result = attribute_table_header
4158c0 70
2720d6 71         if availability is not None:
NJ 72             result += (attribute_table_availability % availability)
73         if stability is not None:
74             result += (attribute_table_stability % stability.capitalize())
75         result += attribute_table_footer
4158c0 76
2720d6 77     return result
4158c0 78
NJ 79 notes_header = """
80 .SH NOTES
81 """
82
83 notes_community = """
84 Further information about this software can be found on the open source community website at %s.
85 """
86 notes_source = """
87 This software was built from source available at http://opensolaris.org/.  The original community source was downloaded from  %s
88 """
89
2720d6 90 def notes_section_text(header_seen, community, source):
NJ 91     result = ''
92
4158c0 93     # is there anything to do?
2720d6 94     if community is not None or source is not None:
NJ 95         if header_seen == False:
96             result += notes_header
97         if source is not None:
98             result += (notes_source % source)
99         if community is not None:
100             result += (notes_community % community)
4158c0 101
2720d6 102     return result
4158c0 103
2720d6 104 so_re = re.compile('^\.so.+$', re.MULTILINE)
4158c0 105 section_re = re.compile('\.SH "?([^"]+).*$', re.IGNORECASE)
NJ 106 #
107 # mangler.man.stability = (mangler.man.stability)
108 # mangler.man.availability = (pkg.fmri)
1de4f0 109 # mangler.man.source-url = (pkg.source-url)
MS 110 # mangler.man.upstream-url = (pkg.upstream-url)
4158c0 111 #
2720d6 112 def mangle_manpage(manifest, action, text):
4158c0 113     # manpages must have a taxonomy defined
NJ 114     stability = action.attrs.pop('mangler.man.stability', None)
115     if stability is None:
116         sys.stderr.write("ERROR: manpage action missing mangler.man.stability: %s" % action)
117         sys.exit(1)
118
119     attributes_written = False
120     notes_seen = False
121
122     if 'pkg.fmri' in manifest.attributes:
123         fmri = pkg.fmri.PkgFmri(manifest.attributes['pkg.fmri'])
124         availability = fmri.pkg_name
125
dc18f2 126     community = None
1de4f0 127     if 'info.upstream-url' in manifest.attributes:
MS 128         community = manifest.attributes['info.upstream-url']
4158c0 129
dc18f2 130     source = None
1de4f0 131     if 'info.source-url' in manifest.attributes:
MS 132         source = manifest.attributes['info.source-url']
133     elif 'info.repository-url' in manifest.attributes:
134         source = manifest.attributes['info.repository-url']
4158c0 135
NJ 136     # skip reference only pages
2720d6 137     if so_re.match(text) is not None:
NJ 138         return text
4158c0 139
NJ 140     # tell man that we want tables (and eqn)
2720d6 141     result = "'\\\" te\n"
4158c0 142
NJ 143     # write the orginal data
2720d6 144     for line in text.split('\n'):
4158c0 145         match = section_re.match(line)
NJ 146         if match is not None:
147             section = match.group(1)
148             if section in ['SEE ALSO', 'NOTES']:
149                 if attributes_written == False:
2720d6 150                     result += attributes_section_text(
4158c0 151                                  availability,
NJ 152                                  stability)
153                     attributes_written = True
154                 if section == 'NOTES':
155                     notes_seen = True
2720d6 156         result += ("%s\n" % line)
4158c0 157
NJ 158     if attributes_written == False:
2720d6 159         result += attributes_section_text(availability, stability)
4158c0 160
2720d6 161     result += notes_section_text(notes_seen, community, source)
4158c0 162
2720d6 163     return result
4158c0 164
NJ 165
166 #
167 # mangler.elf.strip = (true|false)
168 #
169 def mangle_elf(manifest, action, src, dest):
170     pass
171
172 #
173 # mangler.script.file-magic =
174 #
2720d6 175 def mangle_script(manifest, action, text):
NJ 176     return text
177
178 #
179 # mangler.strip_cddl = false
180 #
181 def mangle_cddl(manifest, action, text):
182     strip_cddl = action.attrs.pop('mangler.strip_cddl', 'true')
183     if strip_cddl is 'false':
184         return text
185     cddl_re = re.compile('^[^\n]*CDDL HEADER START.+CDDL HEADER END[^\n]*$',
186                  re.MULTILINE|re.DOTALL)
187     return cddl_re.sub('', text)
4158c0 188
NJ 189 def mangle_path(manifest, action, src, dest):
2720d6 190     if elf.is_elf_object(src):
NJ 191         mangle_elf(manifest, action, src, dest)
192     else:
193         # a 'text' document (script, man page, config file, ...
194         ifp = open(src, 'r')
195         text = ifp.read()
196         ifp.close()
197
198         # remove the CDDL from files
199         result = mangle_cddl(manifest, action, text)
200
201         if 'facet.doc.man' in action.attrs:
202              result = mangle_manpage(manifest, action, result)
203         elif 'mode' in action.attrs and int(action.attrs['mode'], 8) & 0111 != 0:
204             result = mangle_script(manifest, action, result)
205
206         if text != result:
207             destdir = os.path.dirname(dest)
208             if not os.path.exists(destdir):
209                 os.makedirs(destdir)
210             with open(dest, 'w') as ofp:
211                 ofp.write(result)
4158c0 212
NJ 213 #
214 # mangler.bypass = (true|false)
215 #
216 def mangle_paths(manifest, search_paths, destination):
217     for action in manifest.gen_actions_by_type("file"):
218         bypass = action.attrs.pop('mangler.bypass', 'false').lower()
219         if bypass == 'true':
220             continue
221
222         path = None
223         if 'path' in action.attrs:
224             path = action.attrs['path']
225         if action.hash and action.hash != 'NOHASH':
226             path = action.hash
227         if not path:
228             continue
229
2720d6 230         if not os.path.exists(destination):
NJ 231             os.makedirs(destination)
232
4158c0 233         dest = os.path.join(destination, path)
NJ 234         for directory in search_paths:
235             if directory != destination:
236                 src = os.path.join(directory, path)
b0fde7 237                 if os.path.isfile(src):
4158c0 238                     mangle_path(manifest, action, src, dest)
NJ 239                     break
240
241 def load_manifest(manifest_file):
242     manifest = pkg.manifest.Manifest()
243     manifest.set_content(pathname=manifest_file)
244
245     return manifest
246
247 def usage():
248     print "Usage: %s [-m|--manifest (file)] [-d|--search-directory (dir)] [-D|--destination (dir)] " % (sys.argv[0].split('/')[-1])
249     sys.exit(1)
250
251 def main():
252     import getopt
253
254     # FLUSH STDOUT 
255     sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
256
257     search_paths = []
258     destination = None
259     manifests = []
260
261     try:
262         opts, args = getopt.getopt(sys.argv[1:], "D:d:m:",
263             ["destination=", "search-directory=", "manifest="])
264     except getopt.GetoptError, err:
265         print str(err)
266         usage()
267
268     for opt, arg in opts:
269         if opt in [ "-D", "--destination" ]:
270             destination = arg
271         elif opt in [ "-d", "--search-directory" ]:
272             search_paths.append(arg)
273         elif opt in [ "-m", "--manifest" ]:
274             try:
275                 manifest = load_manifest(arg)
276             except IOError, err:
277                 print "oops, %s: %s" % (arg, str(err))
278                 usage()
279             else:
280                 manifests.append(manifest)
281         else:
282             usage()
283
284     if destination == None:
285         usage()
286
287     for manifest in manifests:
288         mangle_paths(manifest, search_paths, destination)
289         print manifest
290
291     sys.exit(0)
292
293 if __name__ == "__main__":
294     main()