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