Add ICU message format support
[chromium-blink-merge.git] / tools / generate_library_loader / generate_library_loader.py
blob5e9da2c8ded60e0ad94ff869151592ac50104dd5
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.
6 """
7 Creates a library loader (a header and implementation file),
8 which is a wrapper for dlopen or direct linking with given library.
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).
14 For more info refer to http://crbug.com/162733 .
15 """
18 import optparse
19 import os.path
20 import re
21 import sys
24 HEADER_TEMPLATE = """// This is generated file. Do not modify directly.
25 // Path to the code generator: %(generator_path)s .
27 #ifndef %(unique_prefix)s
28 #define %(unique_prefix)s
30 %(wrapped_header_include)s
32 #include <string>
34 class %(class_name)s {
35 public:
36 %(class_name)s();
37 ~%(class_name)s();
39 bool Load(const std::string& library_name)
40 __attribute__((warn_unused_result));
42 bool loaded() const { return loaded_; }
44 %(member_decls)s
46 private:
47 void CleanUp(bool unload);
49 #if defined(%(unique_prefix)s_DLOPEN)
50 void* library_;
51 #endif
53 bool loaded_;
55 // Disallow copy constructor and assignment operator.
56 %(class_name)s(const %(class_name)s&);
57 void operator=(const %(class_name)s&);
60 #endif // %(unique_prefix)s
61 """
64 HEADER_MEMBER_TEMPLATE = """ decltype(&::%(function_name)s) %(function_name)s;
65 """
68 IMPL_TEMPLATE = """// This is generated file. Do not modify directly.
69 // Path to the code generator: %(generator_path)s .
71 #include "%(generated_header_name)s"
73 #include <dlfcn.h>
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
84 %(class_name)s::%(class_name)s() : loaded_(false) {
87 %(class_name)s::~%(class_name)s() {
88 CleanUp(loaded_);
91 bool %(class_name)s::Load(const std::string& library_name) {
92 if (loaded_)
93 return false;
95 #if defined(%(unique_prefix)s_DLOPEN)
96 library_ = dlopen(library_name.c_str(), RTLD_LAZY);
97 if (!library_)
98 return false;
99 #endif
101 %(member_init)s
103 loaded_ = true;
104 return true;
107 void %(class_name)s::CleanUp(bool unload) {
108 #if defined(%(unique_prefix)s_DLOPEN)
109 if (unload) {
110 dlclose(library_);
111 library_ = NULL;
113 #endif
114 loaded_ = false;
115 %(member_cleanup)s
119 IMPL_MEMBER_INIT_TEMPLATE = """
120 #if defined(%(unique_prefix)s_DLOPEN)
121 %(function_name)s =
122 reinterpret_cast<decltype(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;
134 IMPL_MEMBER_CLEANUP_TEMPLATE = """ %(function_name)s = NULL;
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')
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)
148 options, args = parser.parse_args()
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')
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)
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())
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
182 member_init.append(IMPL_MEMBER_INIT_TEMPLATE % {
183 'function_name': fn,
184 'unique_prefix': unique_prefix
186 member_cleanup.append(IMPL_MEMBER_CLEANUP_TEMPLATE % {
187 'function_name': fn,
188 'unique_prefix': unique_prefix
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
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
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.')
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)
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),
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),
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()
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()
246 return 0
248 if __name__ == '__main__':
249 sys.exit(main())