• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
34class %(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
64HEADER_MEMBER_TEMPLATE = """  typeof(&::%(function_name)s) %(function_name)s;
65"""
66
67
68IMPL_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
91bool %(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
107void %(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
119IMPL_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
134IMPL_MEMBER_CLEANUP_TEMPLATE = """  %(function_name)s = NULL;
135"""
136
137def 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
248if __name__ == '__main__':
249  sys.exit(main())
250