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
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
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
32 __author__
= 'ajwong@chromium.org (Albert J. Wong)'
42 class Error(Exception):
46 class BadSignatureError(Error
):
50 class SubprocessError(Error
):
51 def __init__(self
, message
, error_code
):
53 self
.message
= message
54 self
.error_code
= error_code
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
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);
99 # Template for generating a variadic stub function definition with return
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
109 # last_named_arg: Name of the last named argument before the variadic
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) {
115 va_start(args___, %(last_named_arg)s);
116 %(return_type)s ret___ = %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
121 # Template for generating a variadic stub function definition without
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
130 # last_named_arg: Name of the last named argument before the variadic
132 VOID_VARIADIC_STUB_FUNCTION_DEFINITION
= (
133 """extern void %(name)s(%(params)s) __attribute__((weak));
134 void %(export)s %(name)s(%(params)s) {
136 va_start(args___, %(last_named_arg)s);
137 %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
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
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.
175 #include <stdlib.h> // For NULL.
176 #include <dlfcn.h> // For dysym, dlopen.
182 # The start and end templates for the enum definitions used by the Umbrella
184 UMBRELLA_ENUM_START
= """// Enum and typedef for umbrella initializer.
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
= """
202 # Templates for the start and end of a namespace. Takes one parameter, the
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.
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.
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"
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.
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();
280 stub_handles->clear();
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);
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();
303 void* handle = dlopen(dso_path->c_str(), RTLD_LAZY);
304 if (handle != NULL) {
305 module_opened = true;
306 opened_libraries[cur_module] = handle;
308 VLOG(1) << "dlopen(" << dso_path->c_str() << ") failed, "
309 << "dlerror() says:\\n" << dlerror();
313 if (!module_opened) {
314 CloseLibraries(&opened_libraries);
320 # Template to generate code to check if each module initializer correctly
321 # completed, and cleanup on failures. This template takes the following
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) {
330 CloseLibraries(&opened_libraries);
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);
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.
366 infile_path: String holding the path to the input file.
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 "."
376 new_basename
= os
.path
.splitext(basename
)[0]
377 if basename
== new_basename
:
380 basename
= new_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
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
405 infile: File object holding a text file of function signatures.
408 A list of dictionaries, where each dictionary represents one function
412 BadSignatureError: A line could not be parsed as a signature.
417 if line
and line
[0] != '#' and line
[0:2] != '//':
418 m
= SIGNATURE_REGEX
.match(line
)
420 raise BadSignatureError('Unparsable line: %s' % line
)
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(',')]})
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.
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,
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
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
,
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.
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,
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'.
476 SubprocessError: If invoking the windows "lib" tool fails, this is raised
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')
485 WriteWindowsDefFile(module_name
, signatures
, outfile
)
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')
498 raise SubprocessError(
499 'Failed creating %s for %s' % (lib_file_path
, def_file_path
),
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.
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,
533 self
.signatures
= signatures
534 self
.module_name
= module_name
535 self
.export_macro
= export_macro
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.
546 identifier: The string with the module name to turn to C-style.
549 A string that can be used as part of a C identifier.
551 return string
.capwords(re
.sub(INVALID_C_IDENT_CHARS
, '', identifier
))
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.
562 module_name: The name of the module to generate an enum name for.
565 A string with the name of the enum value representing this module.
567 return 'kModule%s' % PosixStubWriter
.CStyleIdentifier(module_name
)
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.
577 module_name: The name of the module to generate the function name for.
580 A string with the name of the initialization check function.
582 return 'Is%sInitialized' % PosixStubWriter
.CStyleIdentifier(module_name
)
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.
592 module_name: The name of the module to generate the function name for.
595 A string with the name of the initialization function.
597 return 'Initialize%s' % PosixStubWriter
.CStyleIdentifier(module_name
)
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.
607 module_name: The name of the module to generate the function name for.
610 A string with the name of the uninitialization function.
612 return 'Uninitialize%s' % PosixStubWriter
.CStyleIdentifier(module_name
)
615 def StubFunctionPointer(cls
, signature
):
616 """Generates a function pointer declaration for the given signature.
619 signature: A signature hash, as produced by ParseSignatures,
620 representating the function signature.
623 A string with the declaration of the function pointer for the signature.
625 return 'static %s (*%s_ptr)(%s) = NULL;' % (signature
['return_type'],
627 ', '.join(signature
['params']))
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
638 signature: A signature hash, as produced by ParseSignatures,
639 representating the function signature.
642 A string with the stub function definition.
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
651 arg_list
= ', '.join(arguments
)
652 if arg_list
== 'void':
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', '')}
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', '')}
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', '')}
683 def WriteImplementationPreamble(cls
, header_path
, outfile
):
684 """Write the necessary includes for the implementation file.
687 header_path: The path to the header file.
688 outfile: The file handle to populate.
690 outfile
.write(IMPLEMENTATION_PREAMBLE
% header_path
)
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.
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
)
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
)))
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
)
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.
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':
774 def WriteImplementationContents(self
, namespace
, outfile
):
775 """Given a file handle, write out the stub definitions for this module.
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".
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
))
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) {
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
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
% {
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
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.
881 A OptionParser object.
883 parser
= optparse
.OptionParser(usage
='usage: %prog [options] input')
884 parser
.add_option('-o',
888 help='Output location.')
889 parser
.add_option('-i',
890 '--intermediate_dir',
891 dest
='intermediate_dir',
893 help=('Location of intermediate files. Ignored for %s type'
894 % FILE_TYPE_WIN_DEF
))
895 parser
.add_option('-t',
899 help=('Type of file. Valid types are "%s" or "%s" 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',
905 dest
='stubfile_name',
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',
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 '
920 (FILE_TYPE_WIN_X86
, FILE_TYPE_WIN_X64
)))
921 parser
.add_option('-e',
922 '--extra_stub_header',
923 dest
='extra_stub_header',
925 help=('File to insert after the system includes in the '
926 'generated stub implemenation file. Ignored for '
928 (FILE_TYPE_WIN_X86
, FILE_TYPE_WIN_X64
)))
929 parser
.add_option('-m',
933 help=('Name of output DLL or LIB for DEF creation using '
934 '%s type.' % FILE_TYPE_WIN_DEF
))
935 parser
.add_option('-x',
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.'))
947 """Parses the options and terminates program if they are not sane.
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()
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
,
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
)
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."""
988 if not os
.path
.isdir(dir):
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
1000 options: An OptionParser.OptionValues object with the parsed options.
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
,
1018 """For each signature file, create a windows lib.
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
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')
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
,
1042 def CreateWindowsDefForSigFiles(sig_files
, out_dir
, module_name
):
1043 """For all signature files, create a single windows def file.
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.
1051 for input_path
in sig_files
:
1052 infile
= open(input_path
, 'r')
1054 signatures
+= ParseSignatures(infile
)
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')
1063 WriteWindowsDefFile(module_name
, signatures
, outfile
)
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.
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
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')
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
,
1101 if extra_stub_header
is not None:
1102 extra_header_file
= open(extra_stub_header
, 'r')
1104 impl_file
.write('\n')
1105 for line
in extra_header_file
:
1106 impl_file
.write(line
)
1107 impl_file
.write('\n')
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')
1117 signatures
= ParseSignatures(infile
)
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
,
1129 # Then create the associated header file.
1130 header_file
= open(header_path
, 'w')
1132 PosixStubWriter
.WriteHeaderContents(module_names
, namespace
,
1133 header_guard
, header_file
)
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__':