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