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
29 from giscanner.annotationparser import AnnotationParser
30 from giscanner.ast import Include
31 from giscanner.cachestore import CacheStore
32 from giscanner.dumper import compile_introspection_binary
33 from giscanner.gdumpparser import GDumpParser, IntrospectionBinary
34 from giscanner.minixpath import xpath_assert
35 from giscanner.sourcescanner import SourceScanner
36 from giscanner.shlibs import resolve_shlibs
37 from giscanner.transformer import Transformer
38 from giscanner.maintransformer import MainTransformer
39 from giscanner.introspectablepass import IntrospectablePass
40 from giscanner.girparser import GIRParser
41 from giscanner.girwriter import GIRWriter
42 from giscanner.utils import files_are_identical
44 def _get_option_parser():
45 parser = optparse.OptionParser('%prog [options] sources')
46 parser.add_option('', "--quiet",
47 action="store_true", dest="quiet",
49 help="If passed, do not print details of normal" \
51 parser.add_option("", "--format",
52 action="store", dest="format",
54 help="format to use, one of gidl, gir")
55 parser.add_option("-i", "--include",
56 action="append", dest="includes", default=[],
57 help="include types for other gidls")
58 parser.add_option('', "--generate-typelib-tests",
59 action="store", dest="test_codegen", default=None,
60 help="Generate test code for given namespace,output.h,output.c")
61 parser.add_option('', "--passthrough-gir",
62 action="store", dest="passthrough_gir", default=None,
63 help="Parse and re-output the specified GIR")
64 parser.add_option('', "--reparse-validate",
65 action="store_true", dest="reparse_validate_gir", default=False,
66 help="After generating the GIR, re-parse it to ensure validity")
67 parser.add_option("", "--add-include-path",
68 action="append", dest="include_paths", default=[],
69 help="include paths for other GIR files")
70 parser.add_option("", "--program",
71 action="store", dest="program", default=None,
72 help="program to execute")
73 parser.add_option("", "--program-arg",
74 action="append", dest="program_args", default=[],
75 help="extra arguments to program")
76 parser.add_option("", "--libtool",
77 action="store", dest="libtool_path", default=None,
78 help="full path to libtool")
79 parser.add_option("", "--no-libtool",
80 action="store_true", dest="nolibtool", default=False,
81 help="do not use libtool")
82 parser.add_option("-l", "--library",
83 action="append", dest="libraries", default=[],
84 help="libraries of this unit")
85 parser.add_option("-L", "--library-path",
86 action="append", dest="library_paths", default=[],
87 help="directories to search for libraries")
88 parser.add_option("-n", "--namespace",
89 action="store", dest="namespace_name",
90 help=("name of namespace for this unit, also "
91 "used to compute --identifier-prefix and --symbol-prefix"))
92 parser.add_option("", "--nsversion",
93 action="store", dest="namespace_version",
94 help="version of namespace for this unit")
95 parser.add_option("", "--identifier-prefix",
96 action="append", dest="identifier_prefixes", default=[],
97 help="""Remove this prefix from C identifiers (structure typedefs, etc.).
98 May be specified multiple times. This is also used as the default for --symbol-prefix if
99 the latter is not specified.""")
100 parser.add_option("", "--symbol-prefix",
101 action="append", dest="symbol_prefixes", default=[],
102 help="Remove this prefix from C symbols (function names)")
103 parser.add_option("", "--add-init-section",
104 action="append", dest="init_sections", default=[],
105 help="add extra initialization code in the introspection program")
106 parser.add_option("-o", "--output",
107 action="store", dest="output",
108 help="output to writeout, defaults to stdout")
109 parser.add_option("", "--pkg",
110 action="append", dest="packages", default=[],
111 help="pkg-config packages to get cflags from")
112 parser.add_option("", "--pkg-export",
113 action="append", dest="packages_export", default=[],
114 help="Associated pkg-config packages for this library")
115 parser.add_option('', "--warn-all",
116 action="store_true", dest="warn_all", default=False,
117 help="If true, enable all warnings for introspection")
118 parser.add_option('', "--warn-error",
119 action="store_true", dest="warn_fatal",
120 help="Turn warnings into fatal errors")
121 parser.add_option("-v", "--verbose",
122 action="store_true", dest="verbose",
124 parser.add_option("", "--typelib-xml",
125 action="store_true", dest="typelib_xml",
126 help="Just convert GIR to typelib XML")
127 parser.add_option("", "--xpath-assertions",
128 action="store", dest="xpath_assertions",
129 help="Use given file to create assertions on GIR content")
130 parser.add_option("", "--c-include",
131 action="append", dest="c_includes", default=[],
132 help="headers which should be included in C programs")
134 group = optparse.OptionGroup(parser, "Preprocessor options")
135 group.add_option("-I", help="Pre-processor include file",
136 action="append", dest="cpp_includes",
138 group.add_option("-D", help="Pre-processor define",
139 action="append", dest="cpp_defines",
141 group.add_option("-U", help="Pre-processor undefine",
142 action="append", dest="cpp_undefines",
144 group.add_option("-p", dest="", help="Ignored")
145 parser.add_option_group(group)
151 raise SystemExit('ERROR: %s' % (msg, ))
153 def passthrough_gir(path, f):
157 writer = GIRWriter(parser.get_namespace(),
158 parser.get_shared_libraries(),
159 parser.get_includes(),
160 parser.get_pkgconfig_packages(),
161 parser.get_c_includes())
162 f.write(writer.get_xml())
164 def test_codegen(optstring):
165 (namespace, out_h_filename, out_c_filename) = optstring.split(',')
166 if namespace == 'Everything':
167 from .testcodegen import EverythingCodeGenerator
168 gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
171 raise ValueError("Invaild namespace %r" % (namespace, ))
174 def validate(assertions, path):
175 from xml.etree.cElementTree import parse
176 doc = parse(open(path))
179 assertions_list = f.readlines()
180 for assertion in assertions_list:
181 assertion = assertion.strip()
182 xpath_assert(root, assertion)
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):
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]
200 # the error output should have already appeared on our stderr,
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)
212 args = ['pkg-config', '--libs-only-L']
213 args.extend(packages)
214 output = subprocess.Popen(args,
215 stdout=subprocess.PIPE).communicate()[0]
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)
222 def scanner_main(args):
223 parser = _get_option_parser()
224 (options, args) = parser.parse_args(args)
226 if options.passthrough_gir:
227 passthrough_gir(options.passthrough_gir, sys.stdout)
228 if options.test_codegen:
229 return test_codegen(options.test_codegen)
232 _error('Need at least one filename')
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
251 # We don't support real C++ parsing yet, but we should be able
252 # to understand C API implemented in C++ files.
253 if (arg.endswith('.c') or arg.endswith('.cpp') or
254 arg.endswith('.cc') or arg.endswith('.cxx') or
255 arg.endswith('.h') or arg.endswith('.hpp') or
256 arg.endswith('.hxx')):
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))
263 cachestore = CacheStore()
264 transformer = Transformer(cachestore,
265 options.namespace_name,
266 options.namespace_version,
267 options.identifier_prefixes,
268 options.symbol_prefixes)
270 transformer.enable_warnings(True)
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, ))
277 include_obj = Include.from_string(include)
279 sys.stderr.write("Malformed include %r\n" % (include, ))
281 transformer.register_include(include_obj)
283 packages = set(options.packages)
284 packages.update(transformer.get_pkgconfig_packages())
285 process_packages(parser, options, packages)
287 # Run the preprocessor, tokenize and construct simple
288 # objects representing the raw C symbols
290 ss.set_cpp_options(options.cpp_includes,
292 options.cpp_undefines)
293 ss.parse_files(filenames)
294 ss.parse_macros(filenames)
296 # Transform the C symbols into AST nodes
297 transformer.set_source_ast(ss)
299 # Transform the C AST nodes into higher level
301 gdump_parser = GDumpParser(transformer)
303 # Do enough parsing that we have the get_type() functions to reference
304 # when creating the introspection binary
305 gdump_parser.init_parse()
308 args=[options.program]
309 args.extend(options.program_args)
310 binary = IntrospectionBinary(args)
312 binary = compile_introspection_binary(options,
313 gdump_parser.get_get_type_functions())
315 shlibs = resolve_shlibs(options, binary, libraries)
317 gdump_parser.set_introspection_binary(binary)
320 ap = AnnotationParser(ss)
323 main = MainTransformer(transformer, blocks)
326 final = IntrospectablePass(transformer)
329 if options.warn_fatal and transformer.did_warn():
330 transformer.log_warning("warnings configured as fatal", fatal=True)
331 # Redundant sys.exit here, just in case
335 if options.packages_export:
336 exported_packages = options.packages_export
338 exported_packages = options.packages
339 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
340 exported_packages, options.c_includes)
341 data = writer.get_xml()
343 tempdir = os.path.dirname(options.output) or os.getcwd()
344 main_f = tempfile.NamedTemporaryFile(suffix='.gir', dir=tempdir, delete=False)
347 if options.reparse_validate_gir:
348 temp_f = tempfile.NamedTemporaryFile(suffix='.gir', dir=tempdir, delete=False)
349 passthrough_gir(main_f.name, temp_f)
351 if not files_are_identical(main_f.name, temp_f.name):
353 "Failed to re-parse .gir file; scanned=%r passthrough=%r" % (main_f.name, temp_f.name))
354 os.unlink(temp_f.name)
355 os.rename(main_f.name, options.output)
357 sys.stdout.write(data)