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("", "--accept-unprefixed",
104 action="store_true", dest="accept_unprefixed", default=False,
105 help="""If specified, accept symbols and identifiers that do not
106 match the namespace prefix.""")
107 parser.add_option("", "--add-init-section",
108 action="append", dest="init_sections", default=[],
109 help="add extra initialization code in the introspection program")
110 parser.add_option("-o", "--output",
111 action="store", dest="output",
112 help="output to writeout, defaults to stdout")
113 parser.add_option("", "--pkg",
114 action="append", dest="packages", default=[],
115 help="pkg-config packages to get cflags from")
116 parser.add_option("", "--pkg-export",
117 action="append", dest="packages_export", default=[],
118 help="Associated pkg-config packages for this library")
119 parser.add_option('', "--warn-all",
120 action="store_true", dest="warn_all", default=False,
121 help="If true, enable all warnings for introspection")
122 parser.add_option('', "--warn-error",
123 action="store_true", dest="warn_fatal",
124 help="Turn warnings into fatal errors")
125 parser.add_option("-v", "--verbose",
126 action="store_true", dest="verbose",
128 parser.add_option("", "--typelib-xml",
129 action="store_true", dest="typelib_xml",
130 help="Just convert GIR to typelib XML")
131 parser.add_option("", "--xpath-assertions",
132 action="store", dest="xpath_assertions",
133 help="Use given file to create assertions on GIR content")
134 parser.add_option("", "--c-include",
135 action="append", dest="c_includes", default=[],
136 help="headers which should be included in C programs")
138 group = optparse.OptionGroup(parser, "Preprocessor options")
139 group.add_option("-I", help="Pre-processor include file",
140 action="append", dest="cpp_includes",
142 group.add_option("-D", help="Pre-processor define",
143 action="append", dest="cpp_defines",
145 group.add_option("-U", help="Pre-processor undefine",
146 action="append", dest="cpp_undefines",
148 group.add_option("-p", dest="", help="Ignored")
149 parser.add_option_group(group)
155 raise SystemExit('ERROR: %s' % (msg, ))
157 def passthrough_gir(path, f):
161 writer = GIRWriter(parser.get_namespace(),
162 parser.get_shared_libraries(),
163 parser.get_includes(),
164 parser.get_pkgconfig_packages(),
165 parser.get_c_includes())
166 f.write(writer.get_xml())
168 def test_codegen(optstring):
169 (namespace, out_h_filename, out_c_filename) = optstring.split(',')
170 if namespace == 'Everything':
171 from .testcodegen import EverythingCodeGenerator
172 gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
175 raise ValueError("Invaild namespace %r" % (namespace, ))
178 def validate(assertions, path):
179 from xml.etree.cElementTree import parse
180 doc = parse(open(path))
183 assertions_list = f.readlines()
184 for assertion in assertions_list:
185 assertion = assertion.strip()
186 xpath_assert(root, assertion)
190 def process_options(output, allowed_flags):
191 for option in output.split():
192 for flag in allowed_flags:
193 if not option.startswith(flag):
198 def process_packages(parser, options, packages):
199 args = ['pkg-config', '--cflags']
200 args.extend(packages)
201 output = subprocess.Popen(args,
202 stdout=subprocess.PIPE).communicate()[0]
204 # the error output should have already appeared on our stderr,
207 # Some pkg-config files on Windows have options we don't understand,
208 # so we explicitly filter to only the ones we need.
209 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
210 filtered_output = list(process_options(output, options_whitelist))
211 pkg_options, unused = parser.parse_args(filtered_output)
212 options.cpp_includes.extend(pkg_options.cpp_includes)
213 options.cpp_defines.extend(pkg_options.cpp_defines)
214 options.cpp_undefines.extend(pkg_options.cpp_undefines)
216 args = ['pkg-config', '--libs-only-L']
217 args.extend(packages)
218 output = subprocess.Popen(args,
219 stdout=subprocess.PIPE).communicate()[0]
222 filtered_output = list(process_options(output, options_whitelist))
223 pkg_options, unused = parser.parse_args(filtered_output)
224 options.library_paths.extend(pkg_options.library_paths)
226 def scanner_main(args):
227 parser = _get_option_parser()
228 (options, args) = parser.parse_args(args)
230 if options.passthrough_gir:
231 passthrough_gir(options.passthrough_gir, sys.stdout)
232 if options.test_codegen:
233 return test_codegen(options.test_codegen)
236 _error('Need at least one filename')
238 if options.xpath_assertions:
239 return validate(options.xpath_assertions, args[1])
241 if not options.namespace_name:
242 _error('Namespace name missing')
244 if options.format == 'gir':
245 from giscanner.girwriter import GIRWriter as Writer
247 _error("Unknown format: %s" % (options.format, ))
249 if not (options.libraries or options.program):
250 _error("Must specify --program or --library")
251 libraries = options.libraries
255 # We don't support real C++ parsing yet, but we should be able
256 # to understand C API implemented in C++ files.
257 if (arg.endswith('.c') or arg.endswith('.cpp') or
258 arg.endswith('.cc') or arg.endswith('.cxx') or
259 arg.endswith('.h') or arg.endswith('.hpp') or
260 arg.endswith('.hxx')):
261 if not os.path.exists(arg):
262 _error('%s: no such a file or directory' % (arg, ))
263 # Make absolute, because we do comparisons inside scannerparser.c
264 # against the absolute path that cpp will give us
265 filenames.append(os.path.abspath(arg))
267 # We do this dance because the empty list has different semantics from
268 # None; if the user didn't specify the options, we want to use None so
269 # the Namespace constructor picks the defaults.
270 if options.identifier_prefixes:
271 identifier_prefixes = options.identifier_prefixes
273 identifier_prefixes = None
274 if options.symbol_prefixes:
275 symbol_prefixes = options.symbol_prefixes
277 symbol_prefixes = None
279 cachestore = CacheStore()
280 transformer = Transformer(cachestore,
281 options.namespace_name,
282 options.namespace_version,
283 identifier_prefixes=identifier_prefixes,
284 symbol_prefixes=symbol_prefixes,
285 accept_unprefixed=options.accept_unprefixed)
287 transformer.enable_warnings(True)
288 transformer.set_include_paths(options.include_paths)
289 shown_include_warning = False
290 for include in options.includes:
291 if os.sep in include:
292 raise ValueError("Invalid include path %r" % (include, ))
294 include_obj = Include.from_string(include)
296 sys.stderr.write("Malformed include %r\n" % (include, ))
298 transformer.register_include(include_obj)
300 packages = set(options.packages)
301 packages.update(transformer.get_pkgconfig_packages())
302 process_packages(parser, options, packages)
304 # Run the preprocessor, tokenize and construct simple
305 # objects representing the raw C symbols
307 ss.set_cpp_options(options.cpp_includes,
309 options.cpp_undefines)
310 ss.parse_files(filenames)
311 ss.parse_macros(filenames)
313 # Transform the C symbols into AST nodes
314 transformer.set_source_ast(ss)
316 # Transform the C AST nodes into higher level
318 gdump_parser = GDumpParser(transformer)
320 # Do enough parsing that we have the get_type() functions to reference
321 # when creating the introspection binary
322 gdump_parser.init_parse()
325 args=[options.program]
326 args.extend(options.program_args)
327 binary = IntrospectionBinary(args)
329 binary = compile_introspection_binary(options,
330 gdump_parser.get_get_type_functions())
332 shlibs = resolve_shlibs(options, binary, libraries)
334 gdump_parser.set_introspection_binary(binary)
337 ap = AnnotationParser(ss)
340 main = MainTransformer(transformer, blocks)
343 final = IntrospectablePass(transformer)
346 if options.warn_fatal and transformer.did_warn():
347 transformer.log_warning("warnings configured as fatal", fatal=True)
348 # Redundant sys.exit here, just in case
352 if options.packages_export:
353 exported_packages = options.packages_export
355 exported_packages = options.packages
356 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
357 exported_packages, options.c_includes)
358 data = writer.get_xml()
360 tempdir = os.path.dirname(options.output) or os.getcwd()
361 main_f = tempfile.NamedTemporaryFile(suffix='.gir', dir=tempdir, delete=False)
364 if options.reparse_validate_gir:
365 temp_f = tempfile.NamedTemporaryFile(suffix='.gir', dir=tempdir, delete=False)
366 passthrough_gir(main_f.name, temp_f)
368 if not files_are_identical(main_f.name, temp_f.name):
370 "Failed to re-parse .gir file; scanned=%r passthrough=%r" % (main_f.name, temp_f.name))
371 os.unlink(temp_f.name)
372 os.rename(main_f.name, options.output)
374 sys.stdout.write(data)