| #!/usr/bin/env python | 
 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | """ | 
 | Creates a library loader (a header and implementation file), | 
 | which is a wrapper for dlopen or direct linking with given library. | 
 |  | 
 | The loader makes it possible to have the same client code for both cases, | 
 | and also makes it easier to write code using dlopen (and also provides | 
 | a standard way to do so, and limits the ugliness just to generated files). | 
 |  | 
 | For more info refer to http://crbug.com/162733 . | 
 | """ | 
 |  | 
 |  | 
 | import optparse | 
 | import os.path | 
 | import re | 
 | import sys | 
 |  | 
 |  | 
 | HEADER_TEMPLATE = """// This is generated file. Do not modify directly. | 
 | // Path to the code generator: %(generator_path)s . | 
 |  | 
 | #ifndef %(unique_prefix)s | 
 | #define %(unique_prefix)s | 
 |  | 
 | %(wrapped_header_include)s | 
 |  | 
 | #include <string> | 
 |  | 
 | class %(class_name)s { | 
 |  public: | 
 |   %(class_name)s(); | 
 |   ~%(class_name)s(); | 
 |  | 
 |   bool Load(const std::string& library_name) | 
 |       __attribute__((warn_unused_result)); | 
 |  | 
 |   bool loaded() const { return loaded_; } | 
 |  | 
 | %(member_decls)s | 
 |  | 
 |  private: | 
 |   void CleanUp(bool unload); | 
 |  | 
 | #if defined(%(unique_prefix)s_DLOPEN) | 
 |   void* library_; | 
 | #endif | 
 |  | 
 |   bool loaded_; | 
 |  | 
 |   // Disallow copy constructor and assignment operator. | 
 |   %(class_name)s(const %(class_name)s&); | 
 |   void operator=(const %(class_name)s&); | 
 | }; | 
 |  | 
 | #endif  // %(unique_prefix)s | 
 | """ | 
 |  | 
 |  | 
 | HEADER_MEMBER_TEMPLATE = """  decltype(&::%(function_name)s) %(function_name)s; | 
 | """ | 
 |  | 
 |  | 
 | IMPL_TEMPLATE = """// This is generated file. Do not modify directly. | 
 | // Path to the code generator: %(generator_path)s . | 
 |  | 
 | #include "%(generated_header_name)s" | 
 |  | 
 | #include <dlfcn.h> | 
 |  | 
 | // Put these sanity checks here so that they fire at most once | 
 | // (to avoid cluttering the build output). | 
 | #if !defined(%(unique_prefix)s_DLOPEN) && !defined(%(unique_prefix)s_DT_NEEDED) | 
 | #error neither %(unique_prefix)s_DLOPEN nor %(unique_prefix)s_DT_NEEDED defined | 
 | #endif | 
 | #if defined(%(unique_prefix)s_DLOPEN) && defined(%(unique_prefix)s_DT_NEEDED) | 
 | #error both %(unique_prefix)s_DLOPEN and %(unique_prefix)s_DT_NEEDED defined | 
 | #endif | 
 |  | 
 | %(class_name)s::%(class_name)s() : loaded_(false) { | 
 | } | 
 |  | 
 | %(class_name)s::~%(class_name)s() { | 
 |   CleanUp(loaded_); | 
 | } | 
 |  | 
 | bool %(class_name)s::Load(const std::string& library_name) { | 
 |   if (loaded_) | 
 |     return false; | 
 |  | 
 | #if defined(%(unique_prefix)s_DLOPEN) | 
 |   library_ = dlopen(library_name.c_str(), RTLD_LAZY); | 
 |   if (!library_) | 
 |     return false; | 
 | #endif | 
 |  | 
 | %(member_init)s | 
 |  | 
 |   loaded_ = true; | 
 |   return true; | 
 | } | 
 |  | 
 | void %(class_name)s::CleanUp(bool unload) { | 
 | #if defined(%(unique_prefix)s_DLOPEN) | 
 |   if (unload) { | 
 |     dlclose(library_); | 
 |     library_ = NULL; | 
 |   } | 
 | #endif | 
 |   loaded_ = false; | 
 | %(member_cleanup)s | 
 | } | 
 | """ | 
 |  | 
 | IMPL_MEMBER_INIT_TEMPLATE = """ | 
 | #if defined(%(unique_prefix)s_DLOPEN) | 
 |   %(function_name)s = | 
 |       reinterpret_cast<decltype(this->%(function_name)s)>( | 
 |           dlsym(library_, "%(function_name)s")); | 
 | #endif | 
 | #if defined(%(unique_prefix)s_DT_NEEDED) | 
 |   %(function_name)s = &::%(function_name)s; | 
 | #endif | 
 |   if (!%(function_name)s) { | 
 |     CleanUp(true); | 
 |     return false; | 
 |   } | 
 | """ | 
 |  | 
 | IMPL_MEMBER_CLEANUP_TEMPLATE = """  %(function_name)s = NULL; | 
 | """ | 
 |  | 
 | def main(): | 
 |   parser = optparse.OptionParser() | 
 |   parser.add_option('--name') | 
 |   parser.add_option('--output-cc') | 
 |   parser.add_option('--output-h') | 
 |   parser.add_option('--header') | 
 |  | 
 |   parser.add_option('--bundled-header') | 
 |   parser.add_option('--use-extern-c', action='store_true', default=False) | 
 |   parser.add_option('--link-directly', type=int, default=0) | 
 |  | 
 |   options, args = parser.parse_args() | 
 |  | 
 |   if not options.name: | 
 |     parser.error('Missing --name parameter') | 
 |   if not options.output_cc: | 
 |     parser.error('Missing --output-cc parameter') | 
 |   if not options.output_h: | 
 |     parser.error('Missing --output-h parameter') | 
 |   if not options.header: | 
 |     parser.error('Missing --header paramater') | 
 |   if not args: | 
 |     parser.error('No function names specified') | 
 |  | 
 |   # Make sure we are always dealing with paths relative to source tree root | 
 |   # to avoid issues caused by different relative path roots. | 
 |   source_tree_root = os.path.abspath( | 
 |     os.path.join(os.path.dirname(__file__), '..', '..')) | 
 |   options.output_cc = os.path.relpath(options.output_cc, source_tree_root) | 
 |   options.output_h = os.path.relpath(options.output_h, source_tree_root) | 
 |  | 
 |   # Create a unique prefix, e.g. for header guards. | 
 |   # Stick a known string at the beginning to ensure this doesn't begin | 
 |   # with an underscore, which is reserved for the C++ implementation. | 
 |   unique_prefix = ('LIBRARY_LOADER_' + | 
 |                    re.sub(r'[\W]', '_', options.output_h).upper()) | 
 |  | 
 |   member_decls = [] | 
 |   member_init = [] | 
 |   member_cleanup = [] | 
 |   for fn in args: | 
 |     member_decls.append(HEADER_MEMBER_TEMPLATE % { | 
 |       'function_name': fn, | 
 |       'unique_prefix': unique_prefix | 
 |     }) | 
 |     member_init.append(IMPL_MEMBER_INIT_TEMPLATE % { | 
 |       'function_name': fn, | 
 |       'unique_prefix': unique_prefix | 
 |     }) | 
 |     member_cleanup.append(IMPL_MEMBER_CLEANUP_TEMPLATE % { | 
 |       'function_name': fn, | 
 |       'unique_prefix': unique_prefix | 
 |     }) | 
 |  | 
 |   header = options.header | 
 |   if options.link_directly == 0 and options.bundled_header: | 
 |     header = options.bundled_header | 
 |   wrapped_header_include = '#include %s\n' % header | 
 |  | 
 |   # Some libraries (e.g. libpci) have headers that cannot be included | 
 |   # without extern "C", otherwise they cause the link to fail. | 
 |   # TODO(phajdan.jr): This is a workaround for broken headers. Remove it. | 
 |   if options.use_extern_c: | 
 |     wrapped_header_include = 'extern "C" {\n%s\n}\n' % wrapped_header_include | 
 |  | 
 |   # It seems cleaner just to have a single #define here and #ifdefs in bunch | 
 |   # of places, rather than having a different set of templates, duplicating | 
 |   # or complicating more code. | 
 |   if options.link_directly == 0: | 
 |     wrapped_header_include += '#define %s_DLOPEN\n' % unique_prefix | 
 |   elif options.link_directly == 1: | 
 |     wrapped_header_include += '#define %s_DT_NEEDED\n' % unique_prefix | 
 |   else: | 
 |     parser.error('Invalid value for --link-directly. Should be 0 or 1.') | 
 |  | 
 |   # Make it easier for people to find the code generator just in case. | 
 |   # Doing it this way is more maintainable, because it's going to work | 
 |   # even if file gets moved without updating the contents. | 
 |   generator_path = os.path.relpath(__file__, source_tree_root) | 
 |  | 
 |   header_contents = HEADER_TEMPLATE % { | 
 |     'generator_path': generator_path, | 
 |     'unique_prefix': unique_prefix, | 
 |     'wrapped_header_include': wrapped_header_include, | 
 |     'class_name': options.name, | 
 |     'member_decls': ''.join(member_decls), | 
 |   } | 
 |  | 
 |   impl_contents = IMPL_TEMPLATE % { | 
 |     'generator_path': generator_path, | 
 |     'unique_prefix': unique_prefix, | 
 |     'generated_header_name': options.output_h, | 
 |     'class_name': options.name, | 
 |     'member_init': ''.join(member_init), | 
 |     'member_cleanup': ''.join(member_cleanup), | 
 |   } | 
 |  | 
 |   header_file = open(os.path.join(source_tree_root, options.output_h), 'w') | 
 |   try: | 
 |     header_file.write(header_contents) | 
 |   finally: | 
 |     header_file.close() | 
 |  | 
 |   impl_file = open(os.path.join(source_tree_root, options.output_cc), 'w') | 
 |   try: | 
 |     impl_file.write(impl_contents) | 
 |   finally: | 
 |     impl_file.close() | 
 |  | 
 |   return 0 | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(main()) |