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.transformer import Transformer
37 def _get_option_parser():
38 parser = optparse.OptionParser('%prog [options] sources')
39 parser.add_option("", "--format",
40 action="store", dest="format",
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,
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",
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")
102 group = optparse.OptionGroup(parser, "Preprocessor options")
103 group.add_option("-I", help="Pre-processor include file",
104 action="append", dest="cpp_includes",
106 group.add_option("-D", help="Pre-processor define",
107 action="append", dest="cpp_defines",
109 group.add_option("-U", help="Pre-processor undefine",
110 action="append", dest="cpp_undefines",
112 group.add_option("-p", dest="", help="Ignored")
113 parser.add_option_group(group)
119 raise SystemExit('ERROR: %s' % (msg, ))
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
127 c_ns_key = '{%s}' % (C_NS, )
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]
136 parser.parse_tree(tree)
138 writer = GIRWriter(parser.get_namespace(),
139 parser.get_shared_libraries(),
140 parser.get_includes())
141 sys.stdout.write(writer.get_xml())
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
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)
156 raise ValueError("Couldn't find path %r" % (injectPath, ))
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())
170 def validate(assertions, path):
171 from xml.etree.cElementTree import parse
172 doc = parse(open(path))
175 assertions_list = f.readlines()
176 for assertion in assertions_list:
177 assertion = assertion.strip()
178 xpath_assert(root, assertion)
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):
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]
196 # the error output should have already appeared on our stderr,
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)
208 args = ['pkg-config', '--libs-only-L']
209 args.extend(packages)
210 output = subprocess.Popen(args,
211 stdout=subprocess.PIPE).communicate()[0]
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)
218 def scanner_main(args):
219 parser = _get_option_parser()
220 (options, args) = parser.parse_args(args)
223 _error('Need at least one filename')
225 if options.typelib_xml:
226 return typelib_xml_strip(args[1])
230 _error('Need three filenames; e.g. g-ir-scanner '
231 '--inject Source.gir Additions.xml SourceOut.gir')
232 return inject(*args[1:4])
234 if options.xpath_assertions:
235 return validate(options.xpath_assertions, args[1])
237 if not options.namespace_name:
238 _error('Namespace name missing')
240 if options.format == 'gir':
241 from giscanner.girwriter import GIRWriter as Writer
243 _error("Unknown format: %s" % (options.format, ))
245 if not (options.libraries or options.program):
246 _error("Must specify --program or --library")
247 libraries = options.libraries
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)
255 ld_library_path = os.environ.get('LD_LIBRARY_PATH')
257 library_path = ':'.join([ld_library_path, library_path])
260 os.environ['LPATH'] = ':'.join([lpath, library_path])
262 os.environ['LPATH'] = library_path
265 if (arg.endswith('.c') or
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))
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)
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)
289 packages = set(options.packages)
290 packages.update(transformer.get_pkgconfig_packages())
291 process_packages(parser, options, packages)
293 # Run the preprocessor, tokenize and construct simple
294 # objects representing the raw C symbols
296 ss.set_cpp_options(options.cpp_includes,
298 options.cpp_undefines)
299 ss.parse_files(filenames)
300 ss.parse_macros(filenames)
302 # Transform the C symbols into AST nodes
303 transformer.set_source_ast(ss)
305 # Transform the C AST nodes into higher level
307 glibtransformer = GLibTransformer(transformer,
308 noclosure=options.noclosure)
310 # Do enough parsing that we have the get_type() functions to reference
311 # when creating the introspection binary
312 glibtransformer.init_parse()
315 args=[options.program]
316 args.extend(options.program_args)
317 binary = IntrospectionBinary(args)
319 binary = compile_introspection_binary(options,
320 glibtransformer.get_get_type_functions())
322 glibtransformer.set_introspection_binary(binary)
324 namespace = glibtransformer.parse()
326 ap = AnnotationParser(namespace, ss, transformer)
329 except InvalidAnnotationError, e:
330 raise SystemExit("ERROR in annotation: %s" % (str(e), ))
333 writer = Writer(namespace, libraries, transformer.get_includes(),
334 options.packages, options.c_includes)
335 data = writer.get_xml()
337 fd = open(options.output, "w")