Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / tools / generate_stubs / generate_stubs.py
blob4c0fcac40cdaca862ec89a13812cb498ce057d55
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Creates windows and posix stub files for a given set of signatures.
8 For libraries that need to be loaded outside of the standard executable startup
9 path mechanism, stub files need to be generated for the wanted functions. In
10 windows, this is done via "def" files and the delay load mechanism. On a posix
11 system, a set of stub functions need to be generated that dispatch to functions
12 found via dlsym.
14 This script takes a set of files, where each file is a list of C-style
15 signatures (one signature per line). The output is either a windows def file,
16 or a header + implementation file of stubs suitable for use in a posix system.
18 This script also handles varidiac functions, e.g.
19 void printf(const char* s, ...);
21 TODO(hclam): Fix the situation for varidiac functions.
22 Stub for the above function will be generated and inside the stub function it
23 is translated to:
24 void printf(const char* s, ...) {
25 printf_ptr(s, (void*)arg1);
28 Only one argument from the varidiac arguments is used and it will be used as
29 type void*.
30 """
32 __author__ = 'ajwong@chromium.org (Albert J. Wong)'
34 import optparse
35 import os
36 import re
37 import string
38 import subprocess
39 import sys
42 class Error(Exception):
43 pass
46 class BadSignatureError(Error):
47 pass
50 class SubprocessError(Error):
51 def __init__(self, message, error_code):
52 Error.__init__(self)
53 self.message = message
54 self.error_code = error_code
56 def __str__(self):
57 return 'Failed with code %s: %s' % (self.message, repr(self.error_code))
60 # Regular expression used to parse function signatures in the input files.
61 # The regex is built around identifying the "identifier" for the function name.
62 # We consider the identifier to be the string that follows these constraints:
64 # 1) Starts with [_a-ZA-Z] (C++ spec 2.10).
65 # 2) Continues with [_a-ZA-Z0-9] (C++ spec 2.10).
66 # 3) Preceeds an opening parenthesis by 0 or more whitespace chars.
68 # From that, all preceeding characters are considered the return value.
69 # Trailing characters should have a substring matching the form (.*). That
70 # is considered the arguments.
71 SIGNATURE_REGEX = re.compile('(?P<return_type>.+?)'
72 '(?P<name>[_a-zA-Z][_a-zA-Z0-9]+)\s*'
73 '\((?P<params>.*?)\)')
75 # Used for generating C++ identifiers.
76 INVALID_C_IDENT_CHARS = re.compile('[^_a-zA-Z0-9]')
78 # Constants defning the supported file types options.
79 FILE_TYPE_WIN_X86 = 'windows_lib'
80 FILE_TYPE_WIN_X64 = 'windows_lib_x64'
81 FILE_TYPE_POSIX_STUB = 'posix_stubs'
82 FILE_TYPE_WIN_DEF = 'windows_def'
84 # Template for generating a stub function definition. Includes a forward
85 # declaration marking the symbol as weak. This template takes the following
86 # named parameters.
87 # return_type: The return type.
88 # export: The macro used to alter the stub's visibility.
89 # name: The name of the function.
90 # params: The parameters to the function.
91 # return_prefix: 'return ' if this function is not void. '' otherwise.
92 # arg_list: The arguments used to call the stub function.
93 STUB_FUNCTION_DEFINITION = (
94 """extern %(return_type)s %(name)s(%(params)s) __attribute__((weak));
95 %(return_type)s %(export)s %(name)s(%(params)s) {
96 %(return_prefix)s%(name)s_ptr(%(arg_list)s);
97 }""")
99 # Template for generating a variadic stub function definition with return
100 # value.
101 # Includes a forward declaration marking the symbol as weak.
102 # This template takes the following named parameters.
103 # return_type: The return type.
104 # export: The macro used to alter the stub's visibility.
105 # name: The name of the function.
106 # params: The parameters to the function.
107 # arg_list: The arguments used to call the stub function without the
108 # variadic argument.
109 # last_named_arg: Name of the last named argument before the variadic
110 # argument.
111 VARIADIC_STUB_FUNCTION_DEFINITION = (
112 """extern %(return_type)s %(name)s(%(params)s) __attribute__((weak));
113 %(return_type)s %(export)s %(name)s(%(params)s) {
114 va_list args___;
115 va_start(args___, %(last_named_arg)s);
116 %(return_type)s ret___ = %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
117 va_end(args___);
118 return ret___;
119 }""")
121 # Template for generating a variadic stub function definition without
122 # return value.
123 # Includes a forward declaration marking the symbol as weak.
124 # This template takes the following named parameters.
125 # name: The name of the function.
126 # params: The parameters to the function.
127 # export: The macro used to alter the stub's visibility.
128 # arg_list: The arguments used to call the stub function without the
129 # variadic argument.
130 # last_named_arg: Name of the last named argument before the variadic
131 # argument.
132 VOID_VARIADIC_STUB_FUNCTION_DEFINITION = (
133 """extern void %(name)s(%(params)s) __attribute__((weak));
134 void %(export)s %(name)s(%(params)s) {
135 va_list args___;
136 va_start(args___, %(last_named_arg)s);
137 %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
138 va_end(args___);
139 }""")
141 # Template for the preamble for the stub header file with the header guards,
142 # standard set of includes, and namespace opener. This template takes the
143 # following named parameters:
144 # guard_name: The macro to use as the header guard.
145 # namespace: The namespace for the stub functions.
146 STUB_HEADER_PREAMBLE = """// This is generated file. Do not modify directly.
148 #ifndef %(guard_name)s
149 #define %(guard_name)s
151 #include <map>
152 #include <string>
153 #include <vector>
155 #include "base/logging.h"
157 namespace %(namespace)s {
160 # Template for the end of the stub header. This closes the namespace and the
161 # header guards. This template takes the following named parameters:
162 # guard_name: The macro to use as the header guard.
163 # namespace: The namespace for the stub functions.
164 STUB_HEADER_CLOSER = """} // namespace %(namespace)s
166 #endif // %(guard_name)s
169 # The standard includes needed for the stub implementation file. Takes one
170 # string substition with the path to the associated stub header file.
171 IMPLEMENTATION_PREAMBLE = """// This is generated file. Do not modify directly.
173 #include "%s"
175 #include <stdlib.h> // For NULL.
176 #include <dlfcn.h> // For dysym, dlopen.
178 #include <map>
179 #include <vector>
182 # The start and end templates for the enum definitions used by the Umbrella
183 # initializer.
184 UMBRELLA_ENUM_START = """// Enum and typedef for umbrella initializer.
185 enum StubModules {
187 UMBRELLA_ENUM_END = """ kNumStubModules
192 # Start and end of the extern "C" section for the implementation contents.
193 IMPLEMENTATION_CONTENTS_C_START = """extern "C" {
196 IMPLEMENTATION_CONTENTS_C_END = """
197 } // extern "C"
202 # Templates for the start and end of a namespace. Takes one parameter, the
203 # namespace name.
204 NAMESPACE_START = """namespace %s {
207 NAMESPACE_END = """} // namespace %s
211 # Comment to include before the section declaring all the function pointers
212 # used by the stub functions.
213 FUNCTION_POINTER_SECTION_COMMENT = (
214 """// Static pointers that will hold the location of the real function
215 // implementations after the module has been loaded.
216 """)
218 # Template for the module initialization check function. This template
219 # takes two parameteres: the function name, and the conditional used to
220 # verify the module's initialization.
221 MODULE_INITIALIZATION_CHECK_FUNCTION = (
222 """// Returns true if all stubs have been properly initialized.
223 bool %s() {
224 if (%s) {
225 return true;
226 } else {
227 return false;
231 """)
233 # Template for the line that initialize the stub pointer. This template takes
234 # the following named parameters:
235 # name: The name of the function.
236 # return_type: The return type.
237 # params: The parameters to the function.
238 STUB_POINTER_INITIALIZER = """ %(name)s_ptr =
239 reinterpret_cast<%(return_type)s (*)(%(parameters)s)>(
240 dlsym(module, "%(name)s"));
241 VLOG_IF(1, !%(name)s_ptr) << "Couldn't load %(name)s, dlerror() says:\\n"
242 << dlerror();
245 # Template for module initializer function start and end. This template takes
246 # one parameter which is the initializer function name.
247 MODULE_INITIALIZE_START = """// Initializes the module stubs.
248 void %s(void* module) {
250 MODULE_INITIALIZE_END = """}
254 # Template for module uninitializer function start and end. This template
255 # takes one parameter which is the initializer function name.
256 MODULE_UNINITIALIZE_START = (
257 """// Uninitialize the module stubs. Reset pointers to NULL.
258 void %s() {
259 """)
260 MODULE_UNINITIALIZE_END = """}
265 # Open namespace and add typedef for internal data structures used by the
266 # umbrella initializer.
267 UMBRELLA_INITIALIZER_START = """namespace %s {
268 typedef std::map<StubModules, void*> StubHandleMap;
271 # Function close DSOs on error and clean up dangling references.
272 UMBRELLA_INITIALIZER_CLEANUP_FUNCTION = (
273 """static void CloseLibraries(StubHandleMap* stub_handles) {
274 for (StubHandleMap::const_iterator it = stub_handles->begin();
275 it != stub_handles->end();
276 ++it) {
277 dlclose(it->second);
280 stub_handles->clear();
282 """)
284 # Function to initialize each DSO for the given paths.
285 UMBRELLA_INITIALIZER_INITIALIZE_FUNCTION_START = (
286 """bool InitializeStubs(const StubPathMap& path_map) {
287 StubHandleMap opened_libraries;
288 for (int i = 0; i < kNumStubModules; ++i) {
289 StubModules cur_module = static_cast<StubModules>(i);
290 // If a module is missing, we fail.
291 StubPathMap::const_iterator it = path_map.find(cur_module);
292 if (it == path_map.end()) {
293 CloseLibraries(&opened_libraries);
294 return false;
297 // Otherwise, attempt to dlopen the library.
298 const std::vector<std::string>& paths = it->second;
299 bool module_opened = false;
300 for (std::vector<std::string>::const_iterator dso_path = paths.begin();
301 !module_opened && dso_path != paths.end();
302 ++dso_path) {
303 void* handle = dlopen(dso_path->c_str(), RTLD_LAZY);
304 if (handle != NULL) {
305 module_opened = true;
306 opened_libraries[cur_module] = handle;
307 } else {
308 VLOG(1) << "dlopen(" << dso_path->c_str() << ") failed, "
309 << "dlerror() says:\\n" << dlerror();
313 if (!module_opened) {
314 CloseLibraries(&opened_libraries);
315 return false;
318 """)
320 # Template to generate code to check if each module initializer correctly
321 # completed, and cleanup on failures. This template takes the following
322 # named parameters.
323 # conditional: The conditional expression for successful initialization.
324 # uninitializers: The statements needed to uninitialize the modules.
325 UMBRELLA_INITIALIZER_CHECK_AND_CLEANUP = (
326 """ // Check that each module is initialized correctly.
327 // Close all previously opened libraries on failure.
328 if (%(conditional)s) {
329 %(uninitializers)s;
330 CloseLibraries(&opened_libraries);
331 return false;
334 return true;
336 """)
338 # Template for Initialize, Unininitialize, and IsInitialized functions for each
339 # module. This template takes the following named parameters:
340 # initialize: Name of the Initialize function.
341 # uninitialize: Name of the Uninitialize function.
342 # is_initialized: Name of the IsInitialized function.
343 MODULE_FUNCTION_PROTOTYPES = """bool %(is_initialized)s();
344 void %(initialize)s(void* module);
345 void %(uninitialize)s();
349 # Template for umbrella initializer declaration and associated datatypes.
350 UMBRELLA_INITIALIZER_PROTOTYPE = (
351 """typedef std::map<StubModules, std::vector<std::string> > StubPathMap;
353 // Umbrella initializer for all the modules in this stub file.
354 bool InitializeStubs(const StubPathMap& path_map);
355 """)
358 def ExtractModuleName(infile_path):
359 """Infers the module name from the input file path.
361 The input filename is supposed to be in the form "ModuleName.sigs".
362 This function splits the filename from the extention on that basename of
363 the path and returns that as the module name.
365 Args:
366 infile_path: String holding the path to the input file.
368 Returns:
369 The module name as a string.
371 basename = os.path.basename(infile_path)
373 # This loop continously removes suffixes of the filename separated by a "."
374 # character.
375 while 1:
376 new_basename = os.path.splitext(basename)[0]
377 if basename == new_basename:
378 break
379 else:
380 basename = new_basename
381 return basename
384 def ParseSignatures(infile):
385 """Parses function signatures in the input file.
387 This function parses a file of signatures into a list of dictionaries that
388 represent the function signatures in the input file. Each dictionary has
389 the following keys:
390 return_type: A string with the return type.
391 name: A string with the name of the function.
392 params: A list of each function parameter declaration (type + name)
394 The format of the input file is one C-style function signature per line, no
395 trailing semicolon. Empty lines are allowed. An empty line is a line that
396 consists purely of whitespace. Lines that begin with a # or // are considered
397 comment lines and are ignored.
399 We assume that "int foo(void)" is the same as "int foo()", which is not
400 true in C where "int foo()" is equivalent to "int foo(...)". Our generated
401 code is C++, and we do not handle varargs, so this is a case that can be
402 ignored for now.
404 Args:
405 infile: File object holding a text file of function signatures.
407 Returns:
408 A list of dictionaries, where each dictionary represents one function
409 signature.
411 Raises:
412 BadSignatureError: A line could not be parsed as a signature.
414 signatures = []
415 for line in infile:
416 line = line.strip()
417 if line and line[0] != '#' and line[0:2] != '//':
418 m = SIGNATURE_REGEX.match(line)
419 if m is None:
420 raise BadSignatureError('Unparsable line: %s' % line)
421 signatures.append(
422 {'return_type': m.group('return_type').strip(),
423 'name': m.group('name').strip(),
424 'params': [arg.strip() for arg in m.group('params').split(',')]})
425 return signatures
428 def WriteWindowsDefFile(module_name, signatures, outfile):
429 """Writes a windows def file to the given output file object.
431 The def file format is basically a list of function names. Generation is
432 simple. After outputting the LIBRARY and EXPORTS lines, print out each
433 function name, one to a line, preceeded by 2 spaces.
435 Args:
436 module_name: The name of the module we are writing a stub for.
437 signatures: The list of signature hashes, as produced by ParseSignatures,
438 to create stubs for.
439 outfile: File handle to populate with definitions.
441 outfile.write('LIBRARY %s\n' % module_name)
442 outfile.write('EXPORTS\n')
444 for sig in signatures:
445 outfile.write(' %s\n' % sig['name'])
448 def QuietRun(args, filter=None, write_to=sys.stdout):
449 """Invoke |args| as command via subprocess.Popen, filtering lines starting
450 with |filter|."""
451 popen = subprocess.Popen(args, stdout=subprocess.PIPE)
452 out, _ = popen.communicate()
453 for line in out.splitlines():
454 if not filter or not line.startswith(filter):
455 write_to.write(line + '\n')
456 return popen.returncode
459 def CreateWindowsLib(module_name, signatures, intermediate_dir, outdir_path,
460 machine):
461 """Creates a windows library file.
463 Calling this function will create a lib file in the outdir_path that exports
464 the signatures passed into the object. A temporary def file will be created
465 in the intermediate_dir.
467 Args:
468 module_name: The name of the module we are writing a stub for.
469 signatures: The list of signature hashes, as produced by ParseSignatures,
470 to create stubs for.
471 intermediate_dir: The directory where the generated .def files should go.
472 outdir_path: The directory where generated .lib files should go.
473 machine: String holding the machine type, 'X86' or 'X64'.
475 Raises:
476 SubprocessError: If invoking the windows "lib" tool fails, this is raised
477 with the error code.
479 def_file_path = os.path.join(intermediate_dir,
480 module_name + '.def')
481 lib_file_path = os.path.join(outdir_path,
482 module_name + '.lib')
483 outfile = open(def_file_path, 'w')
484 try:
485 WriteWindowsDefFile(module_name, signatures, outfile)
486 finally:
487 outfile.close()
489 # Invoke the "lib" program on Windows to create stub .lib files for the
490 # generated definitions. These .lib files can then be used during
491 # delayloading of the dynamic libraries.
492 ret = QuietRun(['lib', '/nologo',
493 '/machine:' + machine,
494 '/def:' + def_file_path,
495 '/out:' + lib_file_path],
496 filter=' Creating library')
497 if ret != 0:
498 raise SubprocessError(
499 'Failed creating %s for %s' % (lib_file_path, def_file_path),
500 ret)
503 class PosixStubWriter(object):
504 """Creates a file of stub functions for a library that is opened via dlopen.
506 Windows provides a function in their compiler known as delay loading, which
507 effectively generates a set of stub functions for a dynamic library that
508 delays loading of the dynamic library/resolution of the symbols until one of
509 the needed functions are accessed.
511 In posix, RTLD_LAZY does something similar with DSOs. This is the default
512 link mode for DSOs. However, even though the symbol is not resolved until
513 first usage, the DSO must be present at load time of the main binary.
515 To simulate the windows delay load procedure, we need to create a set of
516 stub functions that allow for correct linkage of the main binary, but
517 dispatch to the dynamically resolved symbol when the module is initialized.
519 This class takes a list of function signatures, and generates a set of stub
520 functions plus initialization code for them.
523 def __init__(self, module_name, export_macro, signatures):
524 """Initializes PosixStubWriter for this set of signatures and module_name.
526 Args:
527 module_name: The name of the module we are writing a stub for.
528 export_macro: A preprocessor macro used to annotate stub symbols with
529 an EXPORT marking, to control visibility.
530 signatures: The list of signature hashes, as produced by ParseSignatures,
531 to create stubs for.
533 self.signatures = signatures
534 self.module_name = module_name
535 self.export_macro = export_macro
537 @classmethod
538 def CStyleIdentifier(cls, identifier):
539 """Generates a C style identifier.
541 The module_name has all invalid identifier characters removed (anything
542 that's not [_a-zA-Z0-9]) and is run through string.capwords to try
543 and approximate camel case.
545 Args:
546 identifier: The string with the module name to turn to C-style.
548 Returns:
549 A string that can be used as part of a C identifier.
551 return string.capwords(re.sub(INVALID_C_IDENT_CHARS, '', identifier))
553 @classmethod
554 def EnumName(cls, module_name):
555 """Gets the enum name for the module.
557 Takes the module name and creates a suitable enum name. The module_name
558 is munged to be a valid C identifier then prefixed with the string
559 "kModule" to generate a Google style enum name.
561 Args:
562 module_name: The name of the module to generate an enum name for.
564 Returns:
565 A string with the name of the enum value representing this module.
567 return 'kModule%s' % PosixStubWriter.CStyleIdentifier(module_name)
569 @classmethod
570 def IsInitializedName(cls, module_name):
571 """Gets the name of function that checks initialization of this module.
573 The name is in the format IsModuleInitialized. Where "Module" is replaced
574 with the module name, munged to be a valid C identifier.
576 Args:
577 module_name: The name of the module to generate the function name for.
579 Returns:
580 A string with the name of the initialization check function.
582 return 'Is%sInitialized' % PosixStubWriter.CStyleIdentifier(module_name)
584 @classmethod
585 def InitializeModuleName(cls, module_name):
586 """Gets the name of the function that initializes this module.
588 The name is in the format InitializeModule. Where "Module" is replaced
589 with the module name, munged to be a valid C identifier.
591 Args:
592 module_name: The name of the module to generate the function name for.
594 Returns:
595 A string with the name of the initialization function.
597 return 'Initialize%s' % PosixStubWriter.CStyleIdentifier(module_name)
599 @classmethod
600 def UninitializeModuleName(cls, module_name):
601 """Gets the name of the function that uninitializes this module.
603 The name is in the format UninitializeModule. Where "Module" is replaced
604 with the module name, munged to be a valid C identifier.
606 Args:
607 module_name: The name of the module to generate the function name for.
609 Returns:
610 A string with the name of the uninitialization function.
612 return 'Uninitialize%s' % PosixStubWriter.CStyleIdentifier(module_name)
614 @classmethod
615 def StubFunctionPointer(cls, signature):
616 """Generates a function pointer declaration for the given signature.
618 Args:
619 signature: A signature hash, as produced by ParseSignatures,
620 representating the function signature.
622 Returns:
623 A string with the declaration of the function pointer for the signature.
625 return 'static %s (*%s_ptr)(%s) = NULL;' % (signature['return_type'],
626 signature['name'],
627 ', '.join(signature['params']))
629 @classmethod
630 def StubFunction(cls, signature):
631 """Generates a stub function definition for the given signature.
633 The function definitions are created with __attribute__((weak)) so that
634 they may be overridden by a real static link or mock versions to be used
635 when testing.
637 Args:
638 signature: A signature hash, as produced by ParseSignatures,
639 representating the function signature.
641 Returns:
642 A string with the stub function definition.
644 return_prefix = ''
645 if signature['return_type'] != 'void':
646 return_prefix = 'return '
648 # Generate the argument list.
649 arguments = [re.split('[\*& ]', arg)[-1].strip() for arg in
650 signature['params']]
651 arg_list = ', '.join(arguments)
652 if arg_list == 'void':
653 arg_list = ''
655 if arg_list != '' and len(arguments) > 1 and arguments[-1] == '...':
656 # If the last argment is ... then this is a variadic function.
657 if return_prefix != '':
658 return VARIADIC_STUB_FUNCTION_DEFINITION % {
659 'return_type': signature['return_type'],
660 'name': signature['name'],
661 'params': ', '.join(signature['params']),
662 'arg_list': ', '.join(arguments[0:-1]),
663 'last_named_arg': arguments[-2],
664 'export': signature.get('export', '')}
665 else:
666 return VOID_VARIADIC_STUB_FUNCTION_DEFINITION % {
667 'name': signature['name'],
668 'params': ', '.join(signature['params']),
669 'arg_list': ', '.join(arguments[0:-1]),
670 'last_named_arg': arguments[-2],
671 'export': signature.get('export', '')}
672 else:
673 # This is a regular function.
674 return STUB_FUNCTION_DEFINITION % {
675 'return_type': signature['return_type'],
676 'name': signature['name'],
677 'params': ', '.join(signature['params']),
678 'return_prefix': return_prefix,
679 'arg_list': arg_list,
680 'export': signature.get('export', '')}
682 @classmethod
683 def WriteImplementationPreamble(cls, header_path, outfile):
684 """Write the necessary includes for the implementation file.
686 Args:
687 header_path: The path to the header file.
688 outfile: The file handle to populate.
690 outfile.write(IMPLEMENTATION_PREAMBLE % header_path)
692 @classmethod
693 def WriteUmbrellaInitializer(cls, module_names, namespace, outfile):
694 """Writes a single function that will open + initialize each module.
696 This intializer will take in an stl map of that lists the correct
697 dlopen target for each module. The map type is
698 std::map<enum StubModules, vector<std::string>> which matches one module
699 to a list of paths to try in dlopen.
701 This function is an all-or-nothing function. If any module fails to load,
702 all other modules are dlclosed, and the function returns. Though it is
703 not enforced, this function should only be called once.
705 Args:
706 module_names: A list with the names of the modules in this stub file.
707 namespace: The namespace these functions should be in.
708 outfile: The file handle to populate with pointer definitions.
710 outfile.write(UMBRELLA_INITIALIZER_START % namespace)
711 outfile.write(UMBRELLA_INITIALIZER_CLEANUP_FUNCTION)
713 # Create the initializaiton function that calls all module initializers,
714 # checks if they succeeded, and backs out module loads on an error.
715 outfile.write(UMBRELLA_INITIALIZER_INITIALIZE_FUNCTION_START)
716 outfile.write(
717 '\n // Initialize each module if we have not already failed.\n')
718 for module in module_names:
719 outfile.write(' %s(opened_libraries[%s]);\n' %
720 (PosixStubWriter.InitializeModuleName(module),
721 PosixStubWriter.EnumName(module)))
722 outfile.write('\n')
724 # Output code to check the initialization status, clean up on error.
725 initializer_checks = ['!%s()' % PosixStubWriter.IsInitializedName(name)
726 for name in module_names]
727 uninitializers = ['%s()' % PosixStubWriter.UninitializeModuleName(name)
728 for name in module_names]
729 outfile.write(UMBRELLA_INITIALIZER_CHECK_AND_CLEANUP % {
730 'conditional': ' ||\n '.join(initializer_checks),
731 'uninitializers': ';\n '.join(uninitializers)})
732 outfile.write('\n} // namespace %s\n' % namespace)
734 @classmethod
735 def WriteHeaderContents(cls, module_names, namespace, header_guard, outfile):
736 """Writes a header file for the stub file generated for module_names.
738 The header file exposes the following:
739 1) An enum, StubModules, listing with an entry for each enum.
740 2) A typedef for a StubPathMap allowing for specification of paths to
741 search for each module.
742 3) The IsInitialized/Initialize/Uninitialize functions for each module.
743 4) An umbrella initialize function for all modules.
745 Args:
746 module_names: A list with the names of each module in this stub file.
747 namespace: The namespace these functions should be in.
748 header_guard: The macro to use as our header guard.
749 outfile: The output handle to populate.
751 outfile.write(STUB_HEADER_PREAMBLE %
752 {'guard_name': header_guard, 'namespace': namespace})
754 # Generate the Initializer protoypes for each module.
755 outfile.write('// Individual module initializer functions.\n')
756 for name in module_names:
757 outfile.write(MODULE_FUNCTION_PROTOTYPES % {
758 'is_initialized': PosixStubWriter.IsInitializedName(name),
759 'initialize': PosixStubWriter.InitializeModuleName(name),
760 'uninitialize': PosixStubWriter.UninitializeModuleName(name)})
762 # Generate the enum for umbrella initializer.
763 outfile.write(UMBRELLA_ENUM_START)
764 outfile.write(' %s = 0,\n' % PosixStubWriter.EnumName(module_names[0]))
765 for name in module_names[1:]:
766 outfile.write(' %s,\n' % PosixStubWriter.EnumName(name))
767 outfile.write(UMBRELLA_ENUM_END)
769 outfile.write(UMBRELLA_INITIALIZER_PROTOTYPE)
770 outfile.write(STUB_HEADER_CLOSER % {
771 'namespace': namespace, 'guard_name':
772 header_guard})
774 def WriteImplementationContents(self, namespace, outfile):
775 """Given a file handle, write out the stub definitions for this module.
777 Args:
778 namespace: The namespace these functions should be in.
779 outfile: The file handle to populate.
781 outfile.write(IMPLEMENTATION_CONTENTS_C_START)
782 self.WriteFunctionPointers(outfile)
783 self.WriteStubFunctions(outfile)
784 outfile.write(IMPLEMENTATION_CONTENTS_C_END)
786 outfile.write(NAMESPACE_START % namespace)
787 self.WriteModuleInitializeFunctions(outfile)
788 outfile.write(NAMESPACE_END % namespace)
790 def WriteFunctionPointers(self, outfile):
791 """Write the function pointer declarations needed by the stubs.
793 We need function pointers to hold the actual location of the function
794 implementation returned by dlsym. This function outputs a pointer
795 definition for each signature in the module.
797 Pointers will be named with the following pattern "FuntionName_ptr".
799 Args:
800 outfile: The file handle to populate with pointer definitions.
802 outfile.write(FUNCTION_POINTER_SECTION_COMMENT)
804 for sig in self.signatures:
805 outfile.write('%s\n' % PosixStubWriter.StubFunctionPointer(sig))
806 outfile.write('\n')
808 def WriteStubFunctions(self, outfile):
809 """Write the function stubs to handle dispatching to real implementations.
811 Functions that have a return type other than void will look as follows:
813 ReturnType FunctionName(A a) {
814 return FunctionName_ptr(a);
817 Functions with a return type of void will look as follows:
819 void FunctionName(A a) {
820 FunctionName_ptr(a);
823 Args:
824 outfile: The file handle to populate.
826 outfile.write('// Stubs that dispatch to the real implementations.\n')
827 for sig in self.signatures:
828 sig['export'] = self.export_macro
829 outfile.write('%s\n' % PosixStubWriter.StubFunction(sig))
831 def WriteModuleInitializeFunctions(self, outfile):
832 """Write functions to initialize/query initlialization of the module.
834 This creates 2 functions IsModuleInitialized and InitializeModule where
835 "Module" is replaced with the module name, first letter capitalized.
837 The InitializeModule function takes a handle that is retrieved from dlopen
838 and attempts to assign each function pointer above via dlsym.
840 The IsModuleInitialized returns true if none of the required functions
841 pointers are NULL.
843 Args:
844 outfile: The file handle to populate.
846 ptr_names = ['%s_ptr' % sig['name'] for sig in self.signatures]
848 # Construct the conditional expression to check the initialization of
849 # all the function pointers above. It should generate a conjuntion
850 # with each pointer on its own line, indented by six spaces to match
851 # the indentation level of MODULE_INITIALIZATION_CHECK_FUNCTION.
852 initialization_conditional = ' &&\n '.join(ptr_names)
854 outfile.write(MODULE_INITIALIZATION_CHECK_FUNCTION % (
855 PosixStubWriter.IsInitializedName(self.module_name),
856 initialization_conditional))
858 # Create function that initializes the module.
859 outfile.write(MODULE_INITIALIZE_START %
860 PosixStubWriter.InitializeModuleName(self.module_name))
861 for sig in self.signatures:
862 outfile.write(STUB_POINTER_INITIALIZER % {
863 'name': sig['name'],
864 'return_type': sig['return_type'],
865 'parameters': ', '.join(sig['params'])})
866 outfile.write(MODULE_INITIALIZE_END)
868 # Create function that uninitializes the module (sets all pointers to
869 # NULL).
870 outfile.write(MODULE_UNINITIALIZE_START %
871 PosixStubWriter.UninitializeModuleName(self.module_name))
872 for sig in self.signatures:
873 outfile.write(' %s_ptr = NULL;\n' % sig['name'])
874 outfile.write(MODULE_UNINITIALIZE_END)
877 def CreateOptionParser():
878 """Creates an OptionParser for the configuration options of script.
880 Returns:
881 A OptionParser object.
883 parser = optparse.OptionParser(usage='usage: %prog [options] input')
884 parser.add_option('-o',
885 '--output',
886 dest='out_dir',
887 default=None,
888 help='Output location.')
889 parser.add_option('-i',
890 '--intermediate_dir',
891 dest='intermediate_dir',
892 default=None,
893 help=('Location of intermediate files. Ignored for %s type'
894 % FILE_TYPE_WIN_DEF))
895 parser.add_option('-t',
896 '--type',
897 dest='type',
898 default=None,
899 help=('Type of file. Valid types are "%s" or "%s" or "%s" '
900 'or "%s"' %
901 (FILE_TYPE_POSIX_STUB, FILE_TYPE_WIN_X86,
902 FILE_TYPE_WIN_X64, FILE_TYPE_WIN_DEF)))
903 parser.add_option('-s',
904 '--stubfile_name',
905 dest='stubfile_name',
906 default=None,
907 help=('Name of posix_stubs output file. Only valid with '
908 '%s type.' % FILE_TYPE_POSIX_STUB))
909 parser.add_option('-p',
910 '--path_from_source',
911 dest='path_from_source',
912 default=None,
913 help=('The relative path from the project root that the '
914 'generated file should consider itself part of (eg. '
915 'third_party/ffmpeg). This is used to generate the '
916 'header guard and namespace for our initializer '
917 'functions and does NOT affect the physical output '
918 'location of the file like -o does. Ignored for '
919 '%s and %s types.' %
920 (FILE_TYPE_WIN_X86, FILE_TYPE_WIN_X64)))
921 parser.add_option('-e',
922 '--extra_stub_header',
923 dest='extra_stub_header',
924 default=None,
925 help=('File to insert after the system includes in the '
926 'generated stub implemenation file. Ignored for '
927 '%s and %s types.' %
928 (FILE_TYPE_WIN_X86, FILE_TYPE_WIN_X64)))
929 parser.add_option('-m',
930 '--module_name',
931 dest='module_name',
932 default=None,
933 help=('Name of output DLL or LIB for DEF creation using '
934 '%s type.' % FILE_TYPE_WIN_DEF))
935 parser.add_option('-x',
936 '--export_macro',
937 dest='export_macro',
938 default='',
939 help=('A macro to place between the return type and '
940 'function name, e.g. MODULE_EXPORT, to control the '
941 'visbility of the stub functions.'))
943 return parser
946 def ParseOptions():
947 """Parses the options and terminates program if they are not sane.
949 Returns:
950 The pair (optparse.OptionValues, [string]), that is the output of
951 a successful call to parser.parse_args().
953 parser = CreateOptionParser()
954 options, args = parser.parse_args()
956 if not args:
957 parser.error('No inputs specified')
959 if options.out_dir is None:
960 parser.error('Output location not specified')
962 if (options.type not in
963 [FILE_TYPE_WIN_X86, FILE_TYPE_WIN_X64, FILE_TYPE_POSIX_STUB,
964 FILE_TYPE_WIN_DEF]):
965 parser.error('Invalid output file type: %s' % options.type)
967 if options.type == FILE_TYPE_POSIX_STUB:
968 if options.stubfile_name is None:
969 parser.error('Output file name needed for %s' % FILE_TYPE_POSIX_STUB)
970 if options.path_from_source is None:
971 parser.error('Path from source needed for %s' % FILE_TYPE_POSIX_STUB)
973 if options.type == FILE_TYPE_WIN_DEF:
974 if options.module_name is None:
975 parser.error('Module name needed for %s' % FILE_TYPE_WIN_DEF)
977 return options, args
980 def EnsureDirExists(dir):
981 """Creates a directory. Does not use the more obvious 'if not exists: create'
982 to avoid race with other invocations of the same code, which will error out
983 on makedirs if another invocation has succeeded in creating the directory
984 since the existence check."""
985 try:
986 os.makedirs(dir)
987 except:
988 if not os.path.isdir(dir):
989 raise
992 def CreateOutputDirectories(options):
993 """Creates the intermediate and final output directories.
995 Given the parsed options, create the intermediate and final output
996 directories if they do not exist. Returns the paths to both directories
997 as a pair.
999 Args:
1000 options: An OptionParser.OptionValues object with the parsed options.
1002 Returns:
1003 The pair (out_dir, intermediate_dir), both of which are strings.
1005 out_dir = os.path.normpath(options.out_dir)
1006 intermediate_dir = os.path.normpath(options.intermediate_dir)
1007 if intermediate_dir is None:
1008 intermediate_dir = out_dir
1010 EnsureDirExists(out_dir)
1011 EnsureDirExists(intermediate_dir)
1013 return out_dir, intermediate_dir
1016 def CreateWindowsLibForSigFiles(sig_files, out_dir, intermediate_dir, machine,
1017 export_macro):
1018 """For each signature file, create a windows lib.
1020 Args:
1021 sig_files: Array of strings with the paths to each signature file.
1022 out_dir: String holding path to directory where the generated libs go.
1023 intermediate_dir: String holding path to directory generated intermdiate
1024 artifacts.
1025 machine: String holding the machine type, 'X86' or 'X64'.
1026 export_macro: A preprocessor macro used to annotate stub symbols with
1027 an EXPORT marking, to control visibility.
1029 for input_path in sig_files:
1030 infile = open(input_path, 'r')
1031 try:
1032 signatures = ParseSignatures(infile)
1033 module_name = ExtractModuleName(os.path.basename(input_path))
1034 for sig in signatures:
1035 sig['export'] = export_macro
1036 CreateWindowsLib(module_name, signatures, intermediate_dir, out_dir,
1037 machine)
1038 finally:
1039 infile.close()
1042 def CreateWindowsDefForSigFiles(sig_files, out_dir, module_name):
1043 """For all signature files, create a single windows def file.
1045 Args:
1046 sig_files: Array of strings with the paths to each signature file.
1047 out_dir: String holding path to directory where the generated def goes.
1048 module_name: Name of the output DLL or LIB which will link in the def file.
1050 signatures = []
1051 for input_path in sig_files:
1052 infile = open(input_path, 'r')
1053 try:
1054 signatures += ParseSignatures(infile)
1055 finally:
1056 infile.close()
1058 def_file_path = os.path.join(
1059 out_dir, os.path.splitext(os.path.basename(module_name))[0] + '.def')
1060 outfile = open(def_file_path, 'w')
1062 try:
1063 WriteWindowsDefFile(module_name, signatures, outfile)
1064 finally:
1065 outfile.close()
1068 def CreatePosixStubsForSigFiles(sig_files, stub_name, out_dir,
1069 intermediate_dir, path_from_source,
1070 extra_stub_header, export_macro):
1071 """Create a posix stub library with a module for each signature file.
1073 Args:
1074 sig_files: Array of strings with the paths to each signature file.
1075 stub_name: String with the basename of the generated stub file.
1076 out_dir: String holding path to directory for the .h files.
1077 intermediate_dir: String holding path to directory for the .cc files.
1078 path_from_source: String with relative path of generated files from the
1079 project root.
1080 extra_stub_header: String with path to file of extra lines to insert
1081 into the generated header for the stub library.
1082 export_macro: A preprocessor macro used to annotate stub symbols with
1083 an EXPORT marking, to control visibility.
1085 header_base_name = stub_name + '.h'
1086 header_path = os.path.join(out_dir, header_base_name)
1087 impl_path = os.path.join(intermediate_dir, stub_name + '.cc')
1089 module_names = [ExtractModuleName(path) for path in sig_files]
1090 namespace = path_from_source.replace('/', '_').lower()
1091 header_guard = '%s_' % namespace.upper()
1092 header_include_path = os.path.join(path_from_source, header_base_name)
1094 # First create the implementation file.
1095 impl_file = open(impl_path, 'w')
1096 try:
1097 # Open the file, and create the preamble which consists of a file
1098 # header plus any necessary includes.
1099 PosixStubWriter.WriteImplementationPreamble(header_include_path,
1100 impl_file)
1101 if extra_stub_header is not None:
1102 extra_header_file = open(extra_stub_header, 'r')
1103 try:
1104 impl_file.write('\n')
1105 for line in extra_header_file:
1106 impl_file.write(line)
1107 impl_file.write('\n')
1108 finally:
1109 extra_header_file.close()
1111 # For each signature file, generate the stub population functions
1112 # for that file. Each file represents one module.
1113 for input_path in sig_files:
1114 name = ExtractModuleName(input_path)
1115 infile = open(input_path, 'r')
1116 try:
1117 signatures = ParseSignatures(infile)
1118 finally:
1119 infile.close()
1120 writer = PosixStubWriter(name, export_macro, signatures)
1121 writer.WriteImplementationContents(namespace, impl_file)
1123 # Lastly, output the umbrella function for the file.
1124 PosixStubWriter.WriteUmbrellaInitializer(module_names, namespace,
1125 impl_file)
1126 finally:
1127 impl_file.close()
1129 # Then create the associated header file.
1130 header_file = open(header_path, 'w')
1131 try:
1132 PosixStubWriter.WriteHeaderContents(module_names, namespace,
1133 header_guard, header_file)
1134 finally:
1135 header_file.close()
1138 def main():
1139 options, args = ParseOptions()
1140 out_dir, intermediate_dir = CreateOutputDirectories(options)
1142 if options.type == FILE_TYPE_WIN_X86:
1143 CreateWindowsLibForSigFiles(args, out_dir, intermediate_dir, 'X86',
1144 options.export_macro)
1145 elif options.type == FILE_TYPE_WIN_X64:
1146 CreateWindowsLibForSigFiles(args, out_dir, intermediate_dir, 'X64',
1147 options.export_macro)
1148 elif options.type == FILE_TYPE_POSIX_STUB:
1149 CreatePosixStubsForSigFiles(args, options.stubfile_name, out_dir,
1150 intermediate_dir, options.path_from_source,
1151 options.extra_stub_header, options.export_macro)
1152 elif options.type == FILE_TYPE_WIN_DEF:
1153 CreateWindowsDefForSigFiles(args, out_dir, options.module_name)
1156 if __name__ == '__main__':
1157 main()