Release 2.0.4
[ocl-icd] / icd_generator.rb
1 =begin
2 Copyright (c) 2012, Brice Videau <brice.videau@imag.fr>
3 Copyright (c) 2012, Vincent Danjean <Vincent.Danjean@ens-lyon.org>
4 All rights reserved.
5       
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8     
9 1. Redistributions of source code must retain the above copyright notice, this
10    list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright notice,
12    this list of conditions and the following disclaimer in the documentation
13    and/or other materials provided with the distribution.
14         
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 =end
26
27 require 'yaml'
28
29 module IcdGenerator
30   $api_entries = {}
31   $api_entries_array = []
32   $cl_objects = ["platform_id", "device_id", "context", "command_queue", "mem", "program", "kernel", "event", "sampler"]
33   $know_entries = { 1 => "clGetPlatformInfo", 0 => "clGetPlatformIDs" }
34   $use_name_in_test = { 1 => "clGetPlatformInfo", 0 => "clGetPlatformIDs" }
35   # do not call these functions when trying to discover the mapping
36   $forbidden_funcs = ["clGetExtensionFunctionAddress", "clGetPlatformIDs",
37     "clGetPlatformInfo", "clGetGLContextInfoKHR", "clUnloadCompiler",
38     "clSetCommandQueueProperty"]
39   $windows_funcs = ["clGetDeviceIDsFromD3D10KHR", "clCreateFromD3D10BufferKHR",
40     "clCreateFromD3D10Texture2DKHR", "clCreateFromD3D10Texture3DKHR",
41     "clEnqueueAcquireD3D10ObjectsKHR", "clEnqueueReleaseD3D10ObjectsKHR",
42     "clGetDeviceIDsFromD3D11KHR", "clCreateFromD3D11BufferKHR",
43     "clCreateFromD3D11Texture2DKHR", "clCreateFromD3D11Texture3DKHR",
44     "clEnqueueAcquireD3D11ObjectsKHR", "clEnqueueReleaseD3D11ObjectsKHR",
45     "clGetDeviceIDsFromDX9MediaAdapterKHR", "clCreateFromDX9MediaSurfaceKHR",
46     "clEnqueueAcquireDX9MediaSurfacesKHR", "clEnqueueReleaseDX9MediaSurfacesKHR"]
47   # do not create weak functions for these ones in the discovering program
48   $noweak_funcs = ["clGetExtensionFunctionAddress", "clGetPlatformIDs",
49     "clGetPlatformInfo", "clGetGLContextInfoKHR", "clUnloadCompiler",
50     "clCreateContext", "clCreateContextFromType", "clWaitForEvents"]
51   # functions written specifically in the loader
52   $specific_loader_funcs = ["clGetExtensionFunctionAddress","clGetPlatformIDs",
53                          "clGetGLContextInfoKHR", "clUnloadCompiler",
54     "clCreateContext", "clCreateContextFromType", "clWaitForEvents"]
55   $header_files = ["/usr/include/CL/cl.h", "/usr/include/CL/cl_gl.h",
56     "/usr/include/CL/cl_ext.h", "/usr/include/CL/cl_gl_ext.h"]
57   $windows_header_files = ["/usr/include/CL/cl_dx9_media_sharing.h", "/usr/include/CL/cl_d3d11.h", "/usr/include/CL/cl_d3d10.h"]
58   $versions_entries = []
59   $buff=20
60   $license = <<EOF
61 Copyright (c) 2012, Brice Videau <brice.videau@imag.fr>
62 Copyright (c) 2012, Vincent Danjean <Vincent.Danjean@ens-lyon.org>
63 All rights reserved.
64       
65 Redistribution and use in source and binary forms, with or without
66 modification, are permitted provided that the following conditions are met:
67     
68 1. Redistributions of source code must retain the above copyright notice, this
69    list of conditions and the following disclaimer.
70 2. Redistributions in binary form must reproduce the above copyright notice,
71    this list of conditions and the following disclaimer in the documentation
72    and/or other materials provided with the distribution.
73         
74 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
75 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
76 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
77 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
78 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
79 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
80 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
81 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
82 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
83 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
84
85 Do not edit this file. It is automatically generated.
86 EOF
87
88   ##########################################################
89   ##########################################################
90   # helper functions
91   def self.parse_headers
92     api_entries = []
93     $header_files.each{ |fname|
94       f = File::open(fname)
95       doc = f.read
96       api_entries += doc.scan(/CL_API_ENTRY.*?;/m)
97       f.close
98     }
99     api_entries.each{ |entry|
100 #      puts entry
101       begin 
102         entry_name = entry.match(/CL_API_CALL(.*?)\(/m)[1].strip
103       rescue
104         entry_name = entry.match(/(\S*?)\(/m)[1].strip
105       end
106       next if entry_name.match('\*')
107       next if entry_name.match("INTEL")
108       next if entry_name.match("APPLE")
109       $api_entries[entry_name] = entry.gsub("\r","")
110     }
111 #    $api_entries.each{ |key, value|
112 #      puts "#{key}: #{value}"
113 #    }
114   end
115
116   def self.load_database(yamlfile, with_windows=false)
117     doc = YAML::load_file(yamlfile)
118     $known_entries = {}
119     $api_entries ||= {}
120     $versions_entries = Hash::new { |hash,key| hash[key]=[] }
121     entry_name = ""
122     version = ""
123     doc.each { |key, value|
124       #puts (key.to_s+":: "+value)
125       begin
126         entry_name = value.match(/CL_API_CALL(.*?)\(/m)[1].strip
127       rescue
128         entry_name = value.match(/(\S*?)\(/m)[1].strip
129       end
130       next if (!with_windows) && $windows_funcs.include?(entry_name)
131       version = value.match(/SUFFIX__VERSION_(\d_\d)/m)[1]
132       $versions_entries[version].push(entry_name)
133       $known_entries[key] = entry_name
134       $api_entries[entry_name] = value
135     }
136     $api_entries_array = []
137     ($known_entries.length+$buff).times { |i|
138       #puts (i.to_s+": "+$known_entries[i])
139       if $known_entries[i] then
140         $api_entries_array.push( $api_entries[$known_entries[i]] )
141       else
142         $api_entries_array.push( "CL_API_ENTRY cl_int CL_API_CALL clUnknown#{i}(void);" )
143       end
144     }
145   end
146
147   def self.include_headers
148     headers =""
149     $header_files.each { |h|
150       if h.match('^/usr/include/') then
151         headers += "#include <#{h[13..-1]}>\n"
152       else
153         headers += "#include \"#{h}\"\n"
154       end
155     }
156     return headers
157   end
158
159   ##########################################################
160   ##########################################################
161   # generate mode
162   def self.generate_libdummy_icd_header
163     libdummy_icd_structures = "/**\n#{$license}\n*/\n"
164     libdummy_icd_structures +=  "#include <CL/opencl.h>\n"
165     libdummy_icd_structures += self.include_headers
166     libdummy_icd_structures += "\n\nstruct _cl_icd_dispatch;\n"
167     libdummy_icd_structures += "struct _cl_platform_id { struct _cl_icd_dispatch *dispatch; };\n\n"
168     libdummy_icd_structures += "struct _cl_icd_dispatch {\n"
169     ($api_entries.length+$buff).times { |i|
170       if( $known_entries[i] ) then
171         libdummy_icd_structures += "  void(*known#{i})(void);\n"
172       else
173         libdummy_icd_structures += "  void(*unknown#{i})(void);\n"
174       end
175     }
176     libdummy_icd_structures += "};\n\n"
177     libdummy_icd_structures += "#pragma GCC visibility push(hidden)\n\n"
178     libdummy_icd_structures += "struct _cl_icd_dispatch master_dispatch; \n\n"
179     $use_name_in_test.each { |k, f|
180       libdummy_icd_structures += "typeof(#{f}) INT#{f};\n"
181     }
182     libdummy_icd_structures += "#pragma GCC visibility pop\n\n"
183     return libdummy_icd_structures
184   end
185
186   def self.generate_libdummy_icd_source
187     libdummy_icd_source = "/**\n#{$license}\n*/\n\n"
188     libdummy_icd_source += "#include <stdio.h>\n\n"
189     libdummy_icd_source += "#include \"libdummy_icd_gen.h\"\n\n"
190     libdummy_icd_source += "#include \"libdummy_icd.h\"\n\n"
191     (0...$api_entries.length+$buff).each { |i|
192       libdummy_icd_source += "void dummyFunc#{i}(void){ printf(\"#{i}  : \"); fflush(NULL); }\n"
193     }
194     libdummy_icd_source += "\nstruct _cl_icd_dispatch master_dispatch = {\n"
195     comma=","
196     ($api_entries.length+$buff).times { |i|
197       comma="" if (i == $api_entries.length+$buff-1)
198       if( $use_name_in_test[i] ) then 
199         libdummy_icd_source += "  (void(*)(void))& INT#{$known_entries[i]}#{comma}\n"
200       else
201         libdummy_icd_source += "  (void(*)(void))& dummyFunc#{i}#{comma}\n"
202       end
203     }
204     libdummy_icd_source += "};\n"
205     return libdummy_icd_source
206   end
207   
208   def self.generate_run_dummy_icd_source
209     run_dummy_icd = "/**\n#{$license}\n*/\n"
210     run_dummy_icd += "#include <stdlib.h>\n"
211     run_dummy_icd += "#include <stdio.h>\n"
212     run_dummy_icd += "#pragma GCC diagnostic push\n"
213     run_dummy_icd += "#  pragma GCC diagnostic ignored \"-Wcpp\"\n"
214     run_dummy_icd += "#  define CL_USE_DEPRECATED_OPENCL_1_0_APIS\n"
215     run_dummy_icd += "#  define CL_USE_DEPRECATED_OPENCL_1_1_APIS\n"
216     run_dummy_icd += "#  include <CL/opencl.h>\n"
217     run_dummy_icd += self.include_headers
218     run_dummy_icd += "#pragma GCC diagnostic pop\n"
219     run_dummy_icd += "\n\n"
220     run_dummy_icd += "typedef CL_API_ENTRY cl_int (CL_API_CALL* oclFuncPtr_fn)(cl_platform_id platform);\n\n"
221     run_dummy_icd += "void call_all_OpenCL_functions(cl_platform_id chosen_platform) {\n"
222     run_dummy_icd += "  oclFuncPtr_fn oclFuncPtr;\n"
223     run_dummy_icd += "  cl_context_properties properties[] = { CL_CONTEXT_PLATFORM, (cl_context_properties)chosen_platform, 0 };\n"
224     $api_entries.each_key { |func_name|
225        next if $forbidden_funcs.include?(func_name)
226        if func_name == "clCreateContext" then
227          run_dummy_icd += "  #{func_name}(properties,1,(cl_device_id*)&chosen_platform,NULL,NULL,NULL);\n"
228        elsif func_name == "clGetGLContextInfoKHR" then
229          run_dummy_icd += "  #{func_name}(properties,CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR, 0, NULL, NULL);\n"
230        elsif func_name == "clCreateContextFromType" then
231          run_dummy_icd += "  #{func_name}(properties,CL_DEVICE_TYPE_CPU,NULL,NULL,NULL);\n"
232        elsif func_name == "clWaitForEvents" then
233          run_dummy_icd += "  #{func_name}(1,(cl_event*)&chosen_platform);\n"
234        elsif func_name == "clGetExtensionFunctionAddressForPlatform" then
235          run_dummy_icd += "  #{func_name}((cl_platform_id)chosen_platform, \"clIcdGetPlatformIDsKHR\");\n"
236        else
237          run_dummy_icd += "  oclFuncPtr = (oclFuncPtr_fn)" + func_name + ";\n"
238          run_dummy_icd += "  oclFuncPtr(chosen_platform);\n"
239        end
240        run_dummy_icd += "  printf(\"%s\\n\", \"#{func_name}\");"
241        run_dummy_icd += "  fflush(NULL);\n"
242     }
243     run_dummy_icd += "  return;\n}\n"
244     return run_dummy_icd
245   end
246
247   def self.generate_run_dummy_icd_weak_source
248     run_dummy_icd_weak = "/**\n#{$license}\n*/\n"
249     run_dummy_icd_weak += <<EOF
250 #define _GNU_SOURCE 1
251 #include <stdio.h>
252 #include <dlfcn.h>
253
254 #define F(f) \\
255 __attribute__((weak)) int f (void* arg, void* arg2) { \\
256   void (* p)(void*, void*)=NULL; \\
257   p=dlsym(RTLD_NEXT, #f); \\
258   if (p) { \\
259     (*p)(arg, arg2); \\
260   } else { \\
261     printf("-1 : "); \\
262   } \\
263   return 0; \\
264
265
266 EOF
267     $api_entries.each_key { |func_name|
268        next if $noweak_funcs.include?(func_name)
269        run_dummy_icd_weak += "F(#{func_name})\n"
270     }
271     return run_dummy_icd_weak
272   end
273
274   def self.generate_sources(from_headers=true, from_database=false, database=nil)
275     if from_headers then        
276       parse_headers
277     end
278     if from_database then
279       load_database(database)
280     end
281     File.open('libdummy_icd_gen.h','w') { |f|
282       f.puts generate_libdummy_icd_header
283     }
284     File.open('libdummy_icd_gen.c','w') { |f|
285       f.puts generate_libdummy_icd_source
286     }
287     File.open('run_dummy_icd_gen.c','w') { |f|
288       f.puts generate_run_dummy_icd_source
289     }
290     File.open('run_dummy_icd_weak.c','w') { |f|
291       f.puts generate_run_dummy_icd_weak_source
292     }
293   end
294
295   ##########################################################
296   ##########################################################
297   # database mode
298   def self.generate_ocl_icd_header
299     ocl_icd_header = "/**\n#{$license}\n*/\n\n"
300     ocl_icd_header += "#ifndef OCL_ICD_H\n"
301     ocl_icd_header += "#define OCL_ICD_H\n"
302     ocl_icd_header += "#pragma GCC diagnostic push\n"
303     ocl_icd_header += "#  pragma GCC diagnostic ignored \"-Wcpp\"\n"
304     ocl_icd_header += "#  define CL_USE_DEPRECATED_OPENCL_1_0_APIS\n"
305     ocl_icd_header += "#  define CL_USE_DEPRECATED_OPENCL_1_1_APIS\n"
306     ocl_icd_header += "#  include <CL/opencl.h>\n"
307     ocl_icd_header += self.include_headers
308     ocl_icd_header += "#pragma GCC diagnostic pop\n"
309     ocl_icd_header += <<EOF
310
311 #define OCL_ICD_API_VERSION     1
312 #define OCL_ICD_IDENTIFIED_FUNCTIONS    #{$known_entries.count}
313
314 struct _cl_icd_dispatch {
315 EOF
316     $api_entries_array.each { |entry|
317       ocl_icd_header += entry.gsub("\r","").
318         sub(/CL_API_CALL\n?(.*?)\(/m,'(CL_API_CALL*\1)('+"\n  ").
319         gsub(/\) (CL_API_SUFFIX__VERSION)/m,"\n) \\1").gsub(/\s*$/,'').
320         gsub(/^[\t ]+/,"    ").gsub(/^([^\t ])/, '  \1') + "\n\n"
321     }
322     ocl_icd_header += "};\n"
323     ocl_icd_header += "#endif\n\n"
324     return ocl_icd_header
325   end
326
327   def self.generate_ocl_icd_loader_header
328     ocl_icd_header = "/**\n#{$license}\n*/\n\n"
329     ocl_icd_header += "#include \"ocl_icd.h\"\n\n"
330     ocl_icd_header += <<EOF
331
332 struct func_desc {
333   const char* name;
334   void(*const addr)(void);
335 };
336
337 extern const struct func_desc function_description[];
338
339 EOF
340     ocl_icd_header += "extern struct _cl_icd_dispatch master_dispatch;\n"
341     $cl_objects.each { |o|
342       ocl_icd_header += "struct _cl_#{o} { struct _cl_icd_dispatch *dispatch; };\n"
343     }
344     return ocl_icd_header
345   end
346
347   def self.generate_ocl_icd_loader_map
348     ocl_icd_loader_map = "/**\n#{$license}\n*/\n\n"
349     prev_version=""
350     $versions_entries.keys.sort.each { |version|
351       ocl_icd_loader_map += "OPENCL_#{version.sub('_','.')} {\n";
352       ocl_icd_loader_map += "  global:\n";
353       $versions_entries[version].each { |symb|
354         ocl_icd_loader_map += "    #{symb};\n"
355       }
356       if (prev_version == "") then
357         ocl_icd_loader_map += "  local:\n";
358         ocl_icd_loader_map += "    *;\n";
359       end
360       ocl_icd_loader_map += "} #{prev_version};\n\n";
361       prev_version="OPENCL_#{version.sub('_','.')}";
362     }
363     return ocl_icd_loader_map
364   end
365  
366   def self.generate_ocl_icd_bindings_source
367     ocl_icd_bindings_source = "/**\n#{$license}\n*/\n"
368     ocl_icd_bindings_source += "#include \"ocl_icd.h\"\n"
369     ocl_icd_bindings_source += "struct _cl_icd_dispatch master_dispatch = {\n"
370     ($api_entries.length+$buff-1).times { |i|
371       if( $known_entries[i] ) then 
372         ocl_icd_bindings_source += "  #{$known_entries[i]},\n"
373       else
374         ocl_icd_bindings_source += "  (void *) NULL,\n"
375       end
376     }
377     if( $known_entries[$api_entries.length+$buff-1] ) then
378       ocl_icd_bindings_source += "  #{$known_entries[i]}\n"
379     else
380       ocl_icd_bindings_source += "  (void *) NULL\n"
381     end
382     ocl_icd_bindings_source += "};\n"
383     ocl_icd_bindings_source += <<EOF
384
385 CL_API_ENTRY cl_int CL_API_CALL clIcdGetPlatformIDsKHR(  
386              cl_uint num_entries, 
387              cl_platform_id *platforms,
388              cl_uint *num_platforms) {
389   if( platforms == NULL && num_platforms == NULL )
390     return CL_INVALID_VALUE;
391   if( num_entries == 0 && platforms != NULL )
392     return CL_INVALID_VALUE;
393 #error You have to fill the commented lines with corresponding variables from your library
394 //  if( your_number_of_platforms == 0)
395 //    return CL_PLATFORM_NOT_FOUND_KHR;
396 //  if( num_platforms != NULL )
397 //    *num_platforms = your_number_of_platforms;
398   if( platforms != NULL ) {
399     cl_uint i;
400 //    for( i=0; i<(your_number_of_platforms<num_entries?your_number_of_platforms:num_entries); i++)
401 //      platforms[i] = &your_platforms[i];
402   }
403   return CL_SUCCESS;
404 }
405
406 CL_API_ENTRY void * CL_API_CALL clGetExtensionFunctionAddress(
407              const char *   func_name) CL_API_SUFFIX__VERSION_1_0 {
408 #error You have to fill this function with your extensions of incorporate these lines in your version
409   if( func_name != NULL &&  strcmp("clIcdGetPlatformIDsKHR", func_name) == 0 )
410     return (void *)clIcdGetPlatformIDsKHR;
411   return NULL;
412 }
413 CL_API_ENTRY cl_int CL_API_CALL clGetPlatformInfo(
414              cl_platform_id   platform, 
415              cl_platform_info param_name,
416              size_t           param_value_size, 
417              void *           param_value,
418              size_t *         param_value_size_ret) CL_API_SUFFIX__VERSION_1_0 {
419 #error You ahve to fill this function with your information or assert that your version responds to CL_PLATFORM_ICD_SUFFIX_KHR
420 //  char cl_platform_profile[] = "FULL_PROFILE";
421 //  char cl_platform_version[] = "OpenCL 1.1";
422 //  char cl_platform_name[] = "DummyCL";
423 //  char cl_platform_vendor[] = "LIG";
424 //  char cl_platform_extensions[] = "cl_khr_icd";
425 //  char cl_platform_icd_suffix_khr[] = "DUMMY";
426   size_t size_string;
427   char * string_p;
428   if( platform != NULL ) {
429     int found = 0;
430     int i;
431     for(i=0; i<num_master_platforms; i++) {
432       if( platform == &master_platforms[i] )
433         found = 1;
434     }
435     if(!found)
436       return CL_INVALID_PLATFORM;
437   }
438   switch ( param_name ) {
439     case CL_PLATFORM_PROFILE:
440       string_p = cl_platform_profile;
441       size_string = sizeof(cl_platform_profile);
442       break;
443     case CL_PLATFORM_VERSION:
444       string_p = cl_platform_version;
445       size_string = sizeof(cl_platform_version);
446       break;
447     case CL_PLATFORM_NAME:
448       string_p = cl_platform_name;
449       size_string = sizeof(cl_platform_name);
450       break;
451     case CL_PLATFORM_VENDOR:
452       string_p = cl_platform_vendor;
453       size_string = sizeof(cl_platform_vendor);
454       break;
455     case CL_PLATFORM_EXTENSIONS:
456       string_p = cl_platform_extensions;
457       size_string = sizeof(cl_platform_extensions);
458       break;
459     case CL_PLATFORM_ICD_SUFFIX_KHR:
460       string_p = cl_platform_icd_suffix_khr;
461       size_string = sizeof(cl_platform_icd_suffix_khr);
462       break;
463     default:
464       return CL_INVALID_VALUE;
465       break;
466   }
467   if( param_value != NULL ) {
468     if( size_string > param_value_size )
469       return CL_INVALID_VALUE;
470     memcpy(param_value, string_p, size_string);
471   }
472   if( param_value_size_ret != NULL )
473     *param_value_size_ret = size_string;
474   return CL_SUCCESS;
475 }
476 EOF
477     return ocl_icd_bindings_source
478   end
479
480   def self.generate_ocl_icd_loader_gen_source
481     skip_funcs = $specific_loader_funcs
482     ocl_icd_loader_gen_source = "/**\n#{$license}\n*/\n"
483     ocl_icd_loader_gen_source += "#include \"ocl_icd_loader.h\"\n"
484     ocl_icd_loader_gen_source += "#define DEBUG_OCL_ICD_PROVIDE_DUMP_FIELD\n"
485     ocl_icd_loader_gen_source += "#include \"ocl_icd_debug.h\"\n"
486     ocl_icd_loader_gen_source += ""
487     $api_entries.each { |func_name, entry|
488       next if skip_funcs.include?(func_name)
489       clean_entry = entry.sub(/(.*\)).*/m,'\1').gsub("/*","").gsub("*/","").gsub("\r","") + "{\n"
490       parameters = clean_entry.match(/\(.*\)/m)[0][1..-2]
491       parameters.gsub!(/\[.*?\]/,"")
492       parameters.sub!(/\(.*?\*\s*(.*?)\)\s*\(.*?\)/,'\1')
493       ocl_icd_loader_gen_source += clean_entry.gsub(/\[.*?\]/,"")
494       first_parameter = parameters.match(/.*?\,/m)
495       if not first_parameter then
496         first_parameter =  parameters.match(/.*/m)[0]
497       else
498         first_parameter = first_parameter[0][0..-2]
499       end
500       fps = first_parameter.split
501       ocl_icd_loader_gen_source += "  debug_trace();\n"
502       ocl_icd_loader_gen_source += "  RETURN(((struct _#{fps[0]} *)#{fps[1]})->dispatch->#{func_name}("
503       ps = parameters.split(",")
504       ps = ps.collect { |p|
505         p = p.split
506         p = p[-1].gsub("*","")
507       }
508       ocl_icd_loader_gen_source += ps.join(", ")
509       ocl_icd_loader_gen_source += "));\n"
510       ocl_icd_loader_gen_source += "}\n\n"
511     }
512     ocl_icd_loader_gen_source += "#pragma GCC visibility push(hidden)\n\n"
513     skip_funcs = $specific_loader_funcs
514     $api_entries.each { |func_name, entry|
515       #next if func_name.match(/EXT$/)
516       #next if func_name.match(/KHR$/)
517       if (skip_funcs.include?(func_name)) then
518         ocl_icd_loader_gen_source += "extern typeof(#{func_name}) #{func_name}_hid;\n"
519       else
520         ocl_icd_loader_gen_source += "typeof(#{func_name}) #{func_name}_hid __attribute__ ((alias (\"#{func_name}\"), visibility(\"hidden\")));\n"
521       end
522     }
523     ocl_icd_loader_gen_source += "\n\nstruct func_desc const function_description[]= {\n"
524     $api_entries.each { |func_name, entry|
525       #next if func_name.match(/EXT$/)
526       #next if func_name.match(/KHR$/)
527       ocl_icd_loader_gen_source += "  {\"#{func_name}\", (void(* const)(void))&#{func_name}_hid },\n"
528     }
529     ocl_icd_loader_gen_source += <<EOF
530   {NULL, NULL}
531 };
532
533 #ifdef DEBUG_OCL_ICD
534 void dump_platform(clGEFA_t f, cl_platform_id pid) {
535   debug(D_ALWAYS, "platform @%p:  name=field_in_struct [clGetExtensionFunctionAddress(name)/clGetExtensionFunctionAddressForPlatform(name)]", pid);
536 EOF
537     $api_entries_array.each { |entry|
538       e = entry.gsub("\r"," ").gsub("\n"," ").gsub("\t"," ").
539         sub(/.*CL_API_CALL *([^ ()]*)[ ()].*$/m, '\1')
540       ocl_icd_loader_gen_source += "  dump_field(pid, f, #{e});\n"
541     }
542
543     ocl_icd_loader_gen_source += <<EOF
544 }
545 #endif
546
547 #pragma GCC visibility pop
548
549 EOF
550     return ocl_icd_loader_gen_source;
551   end
552   
553   def self.generate_from_database(yamlfile)
554     load_database(yamlfile)
555     File.open('ocl_icd.h','w') { |f|
556       f.puts generate_ocl_icd_header
557     }
558     File.open('ocl_icd_loader.h','w') { |f|
559       f.puts generate_ocl_icd_loader_header
560     }
561     File.open('ocl_icd_loader.map','w') { |f|
562       f.puts generate_ocl_icd_loader_map
563     }
564     File.open('ocl_icd_bindings.c','w') { |f|
565       f.puts generate_ocl_icd_bindings_source
566     }
567     File.open('ocl_icd_loader_gen.c','w') { |f|
568       f.puts generate_ocl_icd_loader_gen_source
569     }
570   end
571
572   ##########################################################
573   ##########################################################
574   # update-database mode
575   def self.savedb(yamlfile)
576     File::open(yamlfile,"w") { |f|
577       f.write($license.gsub(/^/,"# "))
578       f.write( <<EOF
579
580 # In Intel (OpenCL 1.1):
581 # * clSetCommandQueueProperty(13): nil (deprecated in 1.1)
582 # * clGetGLContextInfoKHR(74): function present with its symbol
583 # * 75-80: nil
584 # * 92: correspond to symbol clGetKernelArgInfo (first abandonned version?)
585 # * 93-: garbage
586 # In nvidia (OpenCL 1.1):
587 # * clGetGLContextInfoKHR(74): function present but no symbol
588 # * 75-80: nil
589 # * 89-: nil
590 # * only two OpenCL symbols: clGetPlatformInfo(1) and clGetExtensionFunctionAddress(65)
591 # In AMD (OpenCL 1.2):
592 # * clGetPlatformIDs(0): nil (symbol present)
593 # * clGetGLContextInfoKHR(74): function present but no symbol
594 # * 75-80: nil
595 # * 92: nil
596 # * 109-118: nil
597 # * 119-: garbage
598
599 EOF
600 )
601       # Not using YAML::dump as:
602       # * keys are not ordered
603       # * strings are badly formatted in new YAML ruby implementation (psych)
604       # * it is very easy to do it ourself
605       #f.write(YAML::dump(api_db))
606       f.write("--- ")
607       $known_entries.keys.sort.each { |k|
608         f.write("\n#{k}: |-\n  ")
609         f.write($api_entries[$known_entries[k]].gsub("\n","\n  "))
610       }
611       f.write("\n")
612     }
613   end
614
615   def self.updatedb_from_input(dbfile, inputfile)
616     parse_headers
617     load_database(dbfile, with_windows=true)
618     doc = YAML::load_file(inputfile)
619     doc.delete(-1)
620     doc.each_key {|i|
621       next if $known_entries[i]
622       $known_entries[i]=doc[i]
623     }
624     self.savedb(dbfile)
625   end
626
627 end
628
629 ############################################################
630 ############################################################
631 ############################################################
632
633 ### Main program
634
635 require 'optparse'
636
637 options = {}
638 OptionParser.new do |opts|
639   opts.banner = "Usage: cd_generator.rb [options] mode"
640
641   opts.on("-d", "--database FILE", String, "YAML file (default ocl_interface.yaml)") do |v|
642     options[:database] = v
643   end
644   opts.on("-i", "--input FILE", String,
645           "binding between OpenCL functions and entry number (required for update-database)") \
646   do |v|
647     options[:input] = v
648   end
649   opts.on("-s", "--[no-]system-headers", 
650           "Look for OpenCL functions in system header files") \
651   do |v|
652     options[:"system-headers"] = v
653   end
654   opts.on("-m", "--mode [MODE]", [:database, :generate, :"update-database"],
655           "Select mode (database, generate, update-database)") do |m|
656     options[:mode] = m
657   end
658 end.parse!
659
660 if !options[:database] then
661   options[:database] = "ocl_interface.yaml"
662 end
663
664 if !options[:mode] then
665   raise "--mode option required"
666 end
667 if options[:mode] == :generate then
668   if !options[:"system-headers"] then
669     IcdGenerator.generate_sources(from_headers=false, from_database=true, database=options[:database])
670   else
671     IcdGenerator.generate_sources(from_headers=true, from_database=false)
672   end
673 elsif options[:mode] == :"update-database" then
674   if !options[:input] then
675     raise "--input option required"
676   end
677   IcdGenerator.updatedb_from_input(options[:database], options[:input])
678 elsif options[:mode] == :database then
679   IcdGenerator.generate_from_database(options[:database])
680 else
681   raise "Mode must be one of generate, database or update-database not #{options[:mode]}" 
682 end
683