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 # We do this dance because the empty list has different semantics from
264 # None; if the user didn't specify the options, we want to use None so
265 # the Namespace constructor picks the defaults.
266 if options.identifier_prefixes:
267 identifier_prefixes = options.identifier_prefixes
269 identifier_prefixes = None
270 if options.symbol_prefixes:
271 symbol_prefixes = options.symbol_prefixes
273 symbol_prefixes = None
275 cachestore = CacheStore()
276 transformer = Transformer(cachestore,
277 options.namespace_name,
278 options.namespace_version,
282 transformer.enable_warnings(True)
283 transformer.set_include_paths(options.include_paths)
284 shown_include_warning = False
285 for include in options.includes:
286 if os.sep in include:
287 raise ValueError("Invalid include path %r" % (include, ))
289 include_obj = Include.from_string(include)
291 sys.stderr.write("Malformed include %r\n" % (include, ))
293 transformer.register_include(include_obj)
295 packages = set(options.packages)
296 packages.update(transformer.get_pkgconfig_packages())
297 process_packages(parser, options, packages)
299 # Run the preprocessor, tokenize and construct simple
300 # objects representing the raw C symbols
302 ss.set_cpp_options(options.cpp_includes,
304 options.cpp_undefines)
305 ss.parse_files(filenames)
306 ss.parse_macros(filenames)
308 # Transform the C symbols into AST nodes
309 transformer.set_source_ast(ss)
311 # Transform the C AST nodes into higher level
313 gdump_parser = GDumpParser(transformer)
315 # Do enough parsing that we have the get_type() functions to reference
316 # when creating the introspection binary
317 gdump_parser.init_parse()
320 args=[options.program]
321 args.extend(options.program_args)
322 binary = IntrospectionBinary(args)
324 binary = compile_introspection_binary(options,
325 gdump_parser.get_get_type_functions())
327 shlibs = resolve_shlibs(options, binary, libraries)
329 gdump_parser.set_introspection_binary(binary)
332 ap = AnnotationParser(ss)
335 main = MainTransformer(transformer, blocks)
338 final = IntrospectablePass(transformer)
341 if options.warn_fatal and transformer.did_warn():
342 transformer.log_warning("warnings configured as fatal", fatal=True)
343 # Redundant sys.exit here, just in case
347 if options.packages_export:
348 exported_packages = options.packages_export
350 exported_packages = options.packages
351 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
352 exported_packages, options.c_includes)
353 data = writer.get_xml()
355 tempdir = os.path.dirname(options.output) or os.getcwd()
356 main_f = tempfile.NamedTemporaryFile(suffix='.gir', dir=tempdir, delete=False)
359 if options.reparse_validate_gir:
360 temp_f = tempfile.NamedTemporaryFile(suffix='.gir', dir=tempdir, delete=False)
361 passthrough_gir(main_f.name, temp_f)
363 if not files_are_identical(main_f.name, temp_f.name):
365 "Failed to re-parse .gir file; scanned=%r passthrough=%r" % (main_f.name, temp_f.name))
366 os.unlink(temp_f.name)
367 os.rename(main_f.name, options.output)
369 sys.stdout.write(data)