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