Bug 574501 - Install giscanner Python module to private directory
[platform/upstream/gobject-introspection.git] / giscanner / scannermain.py
1 #!/usr/bin/env python
2 # -*- Mode: Python -*-
3 # GObject-Introspection - a framework for introspecting GObject libraries
4 # Copyright (C) 2008  Johan Dahlin
5 # Copyright (C) 2009 Red Hat, Inc.
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 # 02110-1301, USA.
21 #
22
23 import subprocess
24 import optparse
25 import os
26 import sys
27
28 from giscanner.annotationparser import AnnotationParser, InvalidAnnotationError
29 from giscanner.ast import Include
30 from giscanner.cachestore import CacheStore
31 from giscanner.dumper import compile_introspection_binary
32 from giscanner.glibtransformer import GLibTransformer, IntrospectionBinary
33 from giscanner.minixpath import myxpath, xpath_assert
34 from giscanner.sourcescanner import SourceScanner
35 from giscanner.transformer import Transformer
36
37 def _get_option_parser():
38     parser = optparse.OptionParser('%prog [options] sources')
39     parser.add_option("", "--format",
40                       action="store", dest="format",
41                       default="gir",
42                       help="format to use, one of gidl, gir")
43     parser.add_option("-i", "--include",
44                       action="append", dest="includes", default=[],
45                       help="include types for other gidls")
46     parser.add_option("", "--add-include-path",
47                       action="append", dest="include_paths", default=[],
48                       help="include paths for other GIR files")
49     parser.add_option("", "--program",
50                       action="store", dest="program", default=None,
51                       help="program to execute")
52     parser.add_option("", "--program-arg",
53                       action="append", dest="program_args", default=[],
54                       help="extra arguments to program")
55     parser.add_option("", "--libtool",
56                       action="store", dest="libtool_path", default=None,
57                       help="full path to libtool")
58     parser.add_option("", "--no-libtool",
59                       action="store_true", dest="nolibtool", default=False,
60                       help="use libtool")
61     parser.add_option("-l", "--library",
62                       action="append", dest="libraries", default=[],
63                       help="libraries of this unit")
64     parser.add_option("-L", "--library-path",
65                       action="append", dest="library_paths", default=[],
66                       help="directories to search for libraries")
67     parser.add_option("-n", "--namespace",
68                       action="store", dest="namespace_name",
69                       help=("name of namespace for this unit, also "
70                             "used as --strip-prefix default"))
71     parser.add_option("", "--nsversion",
72                       action="store", dest="namespace_version",
73                       help="version of namespace for this unit")
74     parser.add_option("", "--strip-prefix",
75                       action="store", dest="strip_prefix", default=None,
76                       help="remove this prefix from objects and functions")
77     parser.add_option("-o", "--output",
78                       action="store", dest="output",
79                       help="output to writeout, defaults to stdout")
80     parser.add_option("", "--pkg",
81                       action="append", dest="packages", default=[],
82                       help="pkg-config packages to get cflags from")
83     parser.add_option("-v", "--verbose",
84                       action="store_true", dest="verbose",
85                       help="be verbose")
86     parser.add_option("", "--noclosure",
87                       action="store_true", dest="noclosure",
88                       help="do not delete unknown types")
89     parser.add_option("", "--typelib-xml",
90                       action="store_true", dest="typelib_xml",
91                       help="Just convert GIR to typelib XML")
92     parser.add_option("", "--inject",
93                       action="store_true", dest="inject",
94                       help="Inject additional components into GIR XML")
95     parser.add_option("", "--xpath-assertions",
96                       action="store", dest="xpath_assertions",
97             help="Use given file to create assertions on GIR content")
98     parser.add_option("", "--c-include",
99                       action="append", dest="c_includes", default=[],
100                       help="headers which should be included in C programs")
101
102     group = optparse.OptionGroup(parser, "Preprocessor options")
103     group.add_option("-I", help="Pre-processor include file",
104                      action="append", dest="cpp_includes",
105                      default=[])
106     group.add_option("-D", help="Pre-processor define",
107                      action="append", dest="cpp_defines",
108                      default=[])
109     group.add_option("-U", help="Pre-processor undefine",
110                      action="append", dest="cpp_undefines",
111                      default=[])
112     group.add_option("-p", dest="", help="Ignored")
113     parser.add_option_group(group)
114
115     return parser
116
117
118 def _error(msg):
119     raise SystemExit('ERROR: %s' % (msg, ))
120
121 def typelib_xml_strip(path):
122     from giscanner.girparser import GIRParser
123     from giscanner.girwriter import GIRWriter
124     from giscanner.girparser import C_NS
125     from xml.etree.cElementTree import parse
126
127     c_ns_key = '{%s}' % (C_NS, )
128
129     tree = parse(path)
130     root = tree.getroot()
131     for node in root.getiterator():
132         for attrib in list(node.attrib):
133             if attrib.startswith(c_ns_key):
134                 del node.attrib[attrib]
135     parser = GIRParser()
136     parser.parse_tree(tree)
137
138     writer = GIRWriter(parser.get_namespace(),
139                        parser.get_shared_libraries(),
140                        parser.get_includes())
141     sys.stdout.write(writer.get_xml())
142     return 0
143
144 def inject(path, additions, outpath):
145     from giscanner.girparser import GIRParser
146     from giscanner.girwriter import GIRWriter
147     from xml.etree.cElementTree import parse
148
149     tree = parse(path)
150     root = tree.getroot()
151     injectDoc = parse(open(additions))
152     for node in injectDoc.getroot():
153         injectPath = node.attrib['path']
154         target = myxpath(root, injectPath)
155         if not target:
156             raise ValueError("Couldn't find path %r" % (injectPath, ))
157         for child in node:
158             target.append(child)
159
160     parser = GIRParser()
161     parser.parse_tree(tree)
162     writer = GIRWriter(parser.get_namespace(),
163                        parser.get_shared_libraries(),
164                        parser.get_includes())
165     outf = open(outpath, 'w')
166     outf.write(writer.get_xml())
167     outf.close()
168     return 0
169
170 def validate(assertions, path):
171     from xml.etree.cElementTree import parse
172     doc = parse(open(path))
173     root = doc.getroot()
174     f = open(assertions)
175     assertions_list = f.readlines()
176     for assertion in assertions_list:
177         assertion = assertion.strip()
178         xpath_assert(root, assertion)
179     f.close()
180     return 0
181
182 def process_options(output, allowed_flags):
183     for option in output.split():
184         for flag in allowed_flags:
185             if not option.startswith(flag):
186                 continue
187             yield option
188             break
189
190 def process_packages(parser, options, packages):
191     args = ['pkg-config', '--cflags']
192     args.extend(packages)
193     output = subprocess.Popen(args,
194                               stdout=subprocess.PIPE).communicate()[0]
195     if output is None:
196         # the error output should have already appeared on our stderr,
197         # so we just exit
198         sys.exit(1)
199     # Some pkg-config files on Windows have options we don't understand,
200     # so we explicitly filter to only the ones we need.
201     options_whitelist = ['-I', '-D', '-U', '-l', '-L']
202     filtered_output = list(process_options(output, options_whitelist))
203     pkg_options, unused = parser.parse_args(filtered_output)
204     options.cpp_includes.extend(pkg_options.cpp_includes)
205     options.cpp_defines.extend(pkg_options.cpp_defines)
206     options.cpp_undefines.extend(pkg_options.cpp_undefines)
207
208     args = ['pkg-config', '--libs-only-L']
209     args.extend(packages)
210     output = subprocess.Popen(args,
211                               stdout=subprocess.PIPE).communicate()[0]
212     if output is None:
213         sys.exit(1)
214     filtered_output = list(process_options(output, options_whitelist))
215     pkg_options, unused = parser.parse_args(filtered_output)
216     options.library_paths.extend(pkg_options.library_paths)
217
218 def scanner_main(args):
219     parser = _get_option_parser()
220     (options, args) = parser.parse_args(args)
221
222     if len(args) <= 1:
223         _error('Need at least one filename')
224
225     if options.typelib_xml:
226         return typelib_xml_strip(args[1])
227
228     if options.inject:
229         if len(args) != 4:
230             _error('Need three filenames; e.g. g-ir-scanner '
231                    '--inject Source.gir Additions.xml SourceOut.gir')
232         return inject(*args[1:4])
233
234     if options.xpath_assertions:
235         return validate(options.xpath_assertions, args[1])
236
237     if not options.namespace_name:
238         _error('Namespace name missing')
239
240     if options.format == 'gir':
241         from giscanner.girwriter import GIRWriter as Writer
242     else:
243         _error("Unknown format: %s" % (options.format, ))
244
245     if not (options.libraries or options.program):
246         _error("Must specify --program or --library")
247     libraries = options.libraries
248
249     # FIXME: using LPATH is definitely not portable enough. Using Python's
250     # find_library for finding our shared libraries is not a portable enough
251     # anyway as it behaves differently depending on the OS
252     lpath = os.environ.get('LPATH')
253     library_path = ':'.join(options.library_paths)
254
255     ld_library_path = os.environ.get('LD_LIBRARY_PATH')
256     if ld_library_path:
257         library_path = ':'.join([ld_library_path, library_path])
258
259     if lpath:
260         os.environ['LPATH'] = ':'.join([lpath, library_path])
261     else:
262         os.environ['LPATH'] = library_path
263     filenames = []
264     for arg in args:
265         if (arg.endswith('.c') or
266             arg.endswith('.h')):
267             if not os.path.exists(arg):
268                 _error('%s: no such a file or directory' % (arg, ))
269             # Make absolute, because we do comparisons inside scannerparser.c
270             # against the absolute path that cpp will give us
271             filenames.append(os.path.abspath(arg))
272
273     cachestore = CacheStore()
274     transformer = Transformer(cachestore,
275                               options.namespace_name,
276                               options.namespace_version)
277     if options.strip_prefix:
278         transformer.set_strip_prefix(options.strip_prefix)
279     else:
280         transformer.set_strip_prefix(options.namespace_name)
281     transformer.set_include_paths(options.include_paths)
282     shown_include_warning = False
283     for include in options.includes:
284         if os.sep in include:
285             raise ValueError("Invalid include path %r" % (include, ))
286         include_obj = Include.from_string(include)
287         transformer.register_include(include_obj)
288
289     packages = set(options.packages)
290     packages.update(transformer.get_pkgconfig_packages())
291     process_packages(parser, options, packages)
292
293     # Run the preprocessor, tokenize and construct simple
294     # objects representing the raw C symbols
295     ss = SourceScanner()
296     ss.set_cpp_options(options.cpp_includes,
297                        options.cpp_defines,
298                        options.cpp_undefines)
299     ss.parse_files(filenames)
300     ss.parse_macros(filenames)
301
302     # Transform the C symbols into AST nodes
303     transformer.set_source_ast(ss)
304
305     # Transform the C AST nodes into higher level
306     # GLib/GObject nodes
307     glibtransformer = GLibTransformer(transformer,
308                                       noclosure=options.noclosure)
309
310     # Do enough parsing that we have the get_type() functions to reference
311     # when creating the introspection binary
312     glibtransformer.init_parse()
313
314     if options.program:
315         args=[options.program]
316         args.extend(options.program_args)
317         binary = IntrospectionBinary(args)
318     else:
319         binary = compile_introspection_binary(options,
320                             glibtransformer.get_get_type_functions())
321
322     glibtransformer.set_introspection_binary(binary)
323
324     namespace = glibtransformer.parse()
325
326     ap = AnnotationParser(namespace, ss, transformer)
327     try:
328         ap.parse()
329     except InvalidAnnotationError, e:
330         raise SystemExit("ERROR in annotation: %s" % (str(e), ))
331
332     # Write out AST
333     writer = Writer(namespace, libraries, transformer.get_includes(),
334                     options.packages, options.c_includes)
335     data = writer.get_xml()
336     if options.output:
337         fd = open(options.output, "w")
338         fd.write(data)
339     else:
340         print data
341
342     return 0