3 # GObject-Introspection - a framework for introspecting GObject libraries
4 # Copyright (C) 2008 Johan Dahlin
5 # Copyright (C) 2009 Red Hat, Inc.
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.
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.
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
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
38 def _get_option_parser():
39 parser = optparse.OptionParser('%prog [options] sources')
40 parser.add_option("", "--format",
41 action="store", dest="format",
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("", "--add-init-section",
79 action="append", dest="init_sections", default=[],
80 help="add extra initialization code in the introspection program")
81 parser.add_option("-o", "--output",
82 action="store", dest="output",
83 help="output to writeout, defaults to stdout")
84 parser.add_option("", "--pkg",
85 action="append", dest="packages", default=[],
86 help="pkg-config packages to get cflags from")
87 parser.add_option("", "--pkg-export",
88 action="append", dest="packages_export", default=[],
89 help="Associated pkg-config packages for this library")
90 parser.add_option('', "--Wall",
91 action="store_true", dest="warn_all", default=False,
92 help="If true, enable all warnings for introspection")
93 parser.add_option('', "--Werror",
94 action="store_true", dest="warn_fatal",
95 help="Turn warnings into fatal errors")
96 parser.add_option("-v", "--verbose",
97 action="store_true", dest="verbose",
99 parser.add_option("", "--noclosure",
100 action="store_true", dest="noclosure",
101 help="do not delete unknown types")
102 parser.add_option("", "--typelib-xml",
103 action="store_true", dest="typelib_xml",
104 help="Just convert GIR to typelib XML")
105 parser.add_option("", "--inject",
106 action="store_true", dest="inject",
107 help="Inject additional components into GIR XML")
108 parser.add_option("", "--xpath-assertions",
109 action="store", dest="xpath_assertions",
110 help="Use given file to create assertions on GIR content")
111 parser.add_option("", "--c-include",
112 action="append", dest="c_includes", default=[],
113 help="headers which should be included in C programs")
115 group = optparse.OptionGroup(parser, "Preprocessor options")
116 group.add_option("-I", help="Pre-processor include file",
117 action="append", dest="cpp_includes",
119 group.add_option("-D", help="Pre-processor define",
120 action="append", dest="cpp_defines",
122 group.add_option("-U", help="Pre-processor undefine",
123 action="append", dest="cpp_undefines",
125 group.add_option("-p", dest="", help="Ignored")
126 parser.add_option_group(group)
132 raise SystemExit('ERROR: %s' % (msg, ))
134 def typelib_xml_strip(path):
135 from giscanner.girparser import GIRParser
136 from giscanner.girwriter import GIRWriter
137 from giscanner.girparser import C_NS
138 from xml.etree.cElementTree import parse
140 c_ns_key = '{%s}' % (C_NS, )
143 root = tree.getroot()
144 for node in root.getiterator():
145 for attrib in list(node.attrib):
146 if attrib.startswith(c_ns_key):
147 del node.attrib[attrib]
149 parser.parse_tree(tree)
151 writer = GIRWriter(parser.get_namespace(),
152 parser.get_shared_libraries(),
153 parser.get_includes())
154 sys.stdout.write(writer.get_xml())
157 def inject(path, additions, outpath):
158 from giscanner.girparser import GIRParser
159 from giscanner.girwriter import GIRWriter
160 from xml.etree.cElementTree import parse
163 root = tree.getroot()
164 injectDoc = parse(open(additions))
165 for node in injectDoc.getroot():
166 injectPath = node.attrib['path']
167 target = myxpath(root, injectPath)
169 raise ValueError("Couldn't find path %r" % (injectPath, ))
174 parser.parse_tree(tree)
175 writer = GIRWriter(parser.get_namespace(),
176 parser.get_shared_libraries(),
177 parser.get_includes())
178 outf = open(outpath, 'w')
179 outf.write(writer.get_xml())
183 def validate(assertions, path):
184 from xml.etree.cElementTree import parse
185 doc = parse(open(path))
188 assertions_list = f.readlines()
189 for assertion in assertions_list:
190 assertion = assertion.strip()
191 xpath_assert(root, assertion)
195 def process_options(output, allowed_flags):
196 for option in output.split():
197 for flag in allowed_flags:
198 if not option.startswith(flag):
203 def process_packages(parser, options, packages):
204 args = ['pkg-config', '--cflags']
205 args.extend(packages)
206 output = subprocess.Popen(args,
207 stdout=subprocess.PIPE).communicate()[0]
209 # the error output should have already appeared on our stderr,
212 # Some pkg-config files on Windows have options we don't understand,
213 # so we explicitly filter to only the ones we need.
214 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
215 filtered_output = list(process_options(output, options_whitelist))
216 pkg_options, unused = parser.parse_args(filtered_output)
217 options.cpp_includes.extend(pkg_options.cpp_includes)
218 options.cpp_defines.extend(pkg_options.cpp_defines)
219 options.cpp_undefines.extend(pkg_options.cpp_undefines)
221 args = ['pkg-config', '--libs-only-L']
222 args.extend(packages)
223 output = subprocess.Popen(args,
224 stdout=subprocess.PIPE).communicate()[0]
227 filtered_output = list(process_options(output, options_whitelist))
228 pkg_options, unused = parser.parse_args(filtered_output)
229 options.library_paths.extend(pkg_options.library_paths)
231 def scanner_main(args):
232 parser = _get_option_parser()
233 (options, args) = parser.parse_args(args)
236 _error('Need at least one filename')
238 if options.typelib_xml:
239 return typelib_xml_strip(args[1])
243 _error('Need three filenames; e.g. g-ir-scanner '
244 '--inject Source.gir Additions.xml SourceOut.gir')
245 return inject(*args[1:4])
247 if options.xpath_assertions:
248 return validate(options.xpath_assertions, args[1])
250 if not options.namespace_name:
251 _error('Namespace name missing')
253 if options.format == 'gir':
254 from giscanner.girwriter import GIRWriter as Writer
256 _error("Unknown format: %s" % (options.format, ))
258 if not (options.libraries or options.program):
259 _error("Must specify --program or --library")
260 libraries = options.libraries
264 if (arg.endswith('.c') or
266 if not os.path.exists(arg):
267 _error('%s: no such a file or directory' % (arg, ))
268 # Make absolute, because we do comparisons inside scannerparser.c
269 # against the absolute path that cpp will give us
270 filenames.append(os.path.abspath(arg))
272 cachestore = CacheStore()
273 transformer = Transformer(cachestore,
274 options.namespace_name,
275 options.namespace_version)
276 if options.strip_prefix:
277 transformer.set_strip_prefix(options.strip_prefix)
279 transformer.set_strip_prefix(options.namespace_name)
281 transformer.enable_warnings(True)
282 transformer.set_include_paths(options.include_paths)
283 shown_include_warning = False
284 for include in options.includes:
285 if os.sep in include:
286 raise ValueError("Invalid include path %r" % (include, ))
288 include_obj = Include.from_string(include)
290 sys.stderr.write("Malformed include %r\n" % (include, ))
292 transformer.register_include(include_obj)
294 packages = set(options.packages)
295 packages.update(transformer.get_pkgconfig_packages())
296 process_packages(parser, options, packages)
298 # Run the preprocessor, tokenize and construct simple
299 # objects representing the raw C symbols
301 ss.set_cpp_options(options.cpp_includes,
303 options.cpp_undefines)
304 ss.parse_files(filenames)
305 ss.parse_macros(filenames)
307 # Transform the C symbols into AST nodes
308 transformer.set_source_ast(ss)
310 # Transform the C AST nodes into higher level
312 glibtransformer = GLibTransformer(transformer,
313 noclosure=options.noclosure)
315 # Do enough parsing that we have the get_type() functions to reference
316 # when creating the introspection binary
317 glibtransformer.init_parse()
320 args=[options.program]
321 args.extend(options.program_args)
322 binary = IntrospectionBinary(args)
324 binary = compile_introspection_binary(options,
325 glibtransformer.get_get_type_functions())
327 shlibs = resolve_shlibs(options, binary, libraries)
329 glibtransformer.set_introspection_binary(binary)
331 namespace = glibtransformer.parse()
333 ap = AnnotationParser(namespace, ss, transformer)
336 except InvalidAnnotationError, e:
337 raise SystemExit("ERROR in annotation: %s" % (str(e), ))
339 glibtransformer.final_analyze()
341 if options.warn_fatal and transformer.did_warn():
345 if options.packages_export:
346 exported_packages = options.packages_export
348 exported_packages = options.packages
349 writer = Writer(namespace, shlibs, transformer.get_includes(),
350 exported_packages, options.c_includes,
351 transformer.get_strip_prefix())
352 data = writer.get_xml()
354 fd = open(options.output, "w")