Home | History | Annotate | Download | only in generate_library_loader
      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.
      5 
      6 """
      7 Creates a library loader (a header and implementation file),
      8 which is a wrapper for dlopen or direct linking with given library.
      9 
     10 The loader makes it possible to have the same client code for both cases,
     11 and also makes it easier to write code using dlopen (and also provides
     12 a standard way to do so, and limits the ugliness just to generated files).
     13 
     14 For more info refer to http://crbug.com/162733 .
     15 """
     16 
     17 
     18 import optparse
     19 import os.path
     20 import re
     21 import sys
     22 
     23 
     24 HEADER_TEMPLATE = """// This is generated file. Do not modify directly.
     25 // Path to the code generator: %(generator_path)s .
     26 
     27 #ifndef %(unique_prefix)s
     28 #define %(unique_prefix)s
     29 
     30 %(wrapped_header_include)s
     31 
     32 #include <string>
     33 
     34 class %(class_name)s {
     35  public:
     36   %(class_name)s();
     37   ~%(class_name)s();
     38 
     39   bool Load(const std::string& library_name)
     40       __attribute__((warn_unused_result));
     41 
     42   bool loaded() const { return loaded_; }
     43 
     44 %(member_decls)s
     45 
     46  private:
     47   void CleanUp(bool unload);
     48 
     49 #if defined(%(unique_prefix)s_DLOPEN)
     50   void* library_;
     51 #endif
     52 
     53   bool loaded_;
     54 
     55   // Disallow copy constructor and assignment operator.
     56   %(class_name)s(const %(class_name)s&);
     57   void operator=(const %(class_name)s&);
     58 };
     59 
     60 #endif  // %(unique_prefix)s
     61 """
     62 
     63 
     64 HEADER_MEMBER_TEMPLATE = """  typeof(&::%(function_name)s) %(function_name)s;
     65 """
     66 
     67 
     68 IMPL_TEMPLATE = """// This is generated file. Do not modify directly.
     69 // Path to the code generator: %(generator_path)s .
     70 
     71 #include "%(generated_header_name)s"
     72 
     73 #include <dlfcn.h>
     74 
     75 // Put these sanity checks here so that they fire at most once
     76 // (to avoid cluttering the build output).
     77 #if !defined(%(unique_prefix)s_DLOPEN) && !defined(%(unique_prefix)s_DT_NEEDED)
     78 #error neither %(unique_prefix)s_DLOPEN nor %(unique_prefix)s_DT_NEEDED defined
     79 #endif
     80 #if defined(%(unique_prefix)s_DLOPEN) && defined(%(unique_prefix)s_DT_NEEDED)
     81 #error both %(unique_prefix)s_DLOPEN and %(unique_prefix)s_DT_NEEDED defined
     82 #endif
     83 
     84 %(class_name)s::%(class_name)s() : loaded_(false) {
     85 }
     86 
     87 %(class_name)s::~%(class_name)s() {
     88   CleanUp(loaded_);
     89 }
     90 
     91 bool %(class_name)s::Load(const std::string& library_name) {
     92   if (loaded_)
     93     return false;
     94 
     95 #if defined(%(unique_prefix)s_DLOPEN)
     96   library_ = dlopen(library_name.c_str(), RTLD_LAZY);
     97   if (!library_)
     98     return false;
     99 #endif
    100 
    101 %(member_init)s
    102 
    103   loaded_ = true;
    104   return true;
    105 }
    106 
    107 void %(class_name)s::CleanUp(bool unload) {
    108 #if defined(%(unique_prefix)s_DLOPEN)
    109   if (unload) {
    110     dlclose(library_);
    111     library_ = NULL;
    112   }
    113 #endif
    114   loaded_ = false;
    115 %(member_cleanup)s
    116 }
    117 """
    118 
    119 IMPL_MEMBER_INIT_TEMPLATE = """
    120 #if defined(%(unique_prefix)s_DLOPEN)
    121   %(function_name)s =
    122       reinterpret_cast<typeof(this->%(function_name)s)>(
    123           dlsym(library_, "%(function_name)s"));
    124 #endif
    125 #if defined(%(unique_prefix)s_DT_NEEDED)
    126   %(function_name)s = &::%(function_name)s;
    127 #endif
    128   if (!%(function_name)s) {
    129     CleanUp(true);
    130     return false;
    131   }
    132 """
    133 
    134 IMPL_MEMBER_CLEANUP_TEMPLATE = """  %(function_name)s = NULL;
    135 """
    136 
    137 def main():
    138   parser = optparse.OptionParser()
    139   parser.add_option('--name')
    140   parser.add_option('--output-cc')
    141   parser.add_option('--output-h')
    142   parser.add_option('--header')
    143 
    144   parser.add_option('--bundled-header')
    145   parser.add_option('--use-extern-c', action='store_true', default=False)
    146   parser.add_option('--link-directly', type=int, default=0)
    147 
    148   options, args = parser.parse_args()
    149 
    150   if not options.name:
    151     parser.error('Missing --name parameter')
    152   if not options.output_cc:
    153     parser.error('Missing --output-cc parameter')
    154   if not options.output_h:
    155     parser.error('Missing --output-h parameter')
    156   if not options.header:
    157     parser.error('Missing --header paramater')
    158   if not args:
    159     parser.error('No function names specified')
    160 
    161   # Make sure we are always dealing with paths relative to source tree root
    162   # to avoid issues caused by different relative path roots.
    163   source_tree_root = os.path.abspath(
    164     os.path.join(os.path.dirname(__file__), '..', '..'))
    165   options.output_cc = os.path.relpath(options.output_cc, source_tree_root)
    166   options.output_h = os.path.relpath(options.output_h, source_tree_root)
    167 
    168   # Create a unique prefix, e.g. for header guards.
    169   # Stick a known string at the beginning to ensure this doesn't begin
    170   # with an underscore, which is reserved for the C++ implementation.
    171   unique_prefix = ('LIBRARY_LOADER_' +
    172                    re.sub(r'[\W]', '_', options.output_h).upper())
    173 
    174   member_decls = []
    175   member_init = []
    176   member_cleanup = []
    177   for fn in args:
    178     member_decls.append(HEADER_MEMBER_TEMPLATE % {
    179       'function_name': fn,
    180       'unique_prefix': unique_prefix
    181     })
    182     member_init.append(IMPL_MEMBER_INIT_TEMPLATE % {
    183       'function_name': fn,
    184       'unique_prefix': unique_prefix
    185     })
    186     member_cleanup.append(IMPL_MEMBER_CLEANUP_TEMPLATE % {
    187       'function_name': fn,
    188       'unique_prefix': unique_prefix
    189     })
    190 
    191   header = options.header
    192   if options.link_directly == 0 and options.bundled_header:
    193     header = options.bundled_header
    194   wrapped_header_include = '#include %s\n' % header
    195 
    196   # Some libraries (e.g. libpci) have headers that cannot be included
    197   # without extern "C", otherwise they cause the link to fail.
    198   # TODO(phajdan.jr): This is a workaround for broken headers. Remove it.
    199   if options.use_extern_c:
    200     wrapped_header_include = 'extern "C" {\n%s\n}\n' % wrapped_header_include
    201 
    202   # It seems cleaner just to have a single #define here and #ifdefs in bunch
    203   # of places, rather than having a different set of templates, duplicating
    204   # or complicating more code.
    205   if options.link_directly == 0:
    206     wrapped_header_include += '#define %s_DLOPEN\n' % unique_prefix
    207   elif options.link_directly == 1:
    208     wrapped_header_include += '#define %s_DT_NEEDED\n' % unique_prefix
    209   else:
    210     parser.error('Invalid value for --link-directly. Should be 0 or 1.')
    211 
    212   # Make it easier for people to find the code generator just in case.
    213   # Doing it this way is more maintainable, because it's going to work
    214   # even if file gets moved without updating the contents.
    215   generator_path = os.path.relpath(__file__, source_tree_root)
    216 
    217   header_contents = HEADER_TEMPLATE % {
    218     'generator_path': generator_path,
    219     'unique_prefix': unique_prefix,
    220     'wrapped_header_include': wrapped_header_include,
    221     'class_name': options.name,
    222     'member_decls': ''.join(member_decls),
    223   }
    224 
    225   impl_contents = IMPL_TEMPLATE % {
    226     'generator_path': generator_path,
    227     'unique_prefix': unique_prefix,
    228     'generated_header_name': options.output_h,
    229     'class_name': options.name,
    230     'member_init': ''.join(member_init),
    231     'member_cleanup': ''.join(member_cleanup),
    232   }
    233 
    234   header_file = open(os.path.join(source_tree_root, options.output_h), 'w')
    235   try:
    236     header_file.write(header_contents)
    237   finally:
    238     header_file.close()
    239 
    240   impl_file = open(os.path.join(source_tree_root, options.output_cc), 'w')
    241   try:
    242     impl_file.write(impl_contents)
    243   finally:
    244     impl_file.close()
    245 
    246   return 0
    247 
    248 if __name__ == '__main__':
    249   sys.exit(main())
    250