1 ;; You should nominially invoke this file via ./generators/make-opengl-bindings.rb
2 ;; as that performs the necessary setup.
4 (declaim (optimize (speed 0) (space 0) (debug 3)))
7 (defparameter *reports
* '(:type-map nil
12 :function-category-counts nil
))
14 (defun make-version-syms (&rest versions
)
15 (loop for version in versions
16 collecting
(intern (concatenate 'string
"VERSION_" version
))))
18 (defparameter *core-opengl-versions
*
19 (make-version-syms "1_0" "1_1"))
21 (defparameter *opengl-versions
*
22 (make-version-syms "1_0" "1_1" "1_2" "1_3" "1_4" "1_5"
24 "3_0" "3_1" "3_2" "3_3"
26 "List of versioned extensions for dependency generation.
27 Must be in the correct order.")
29 (defparameter *source-filename
* (or #.
*compile-file-truename
*
30 (load-time-value *load-truename
*)))
32 (defparameter *extension-names
* nil
)
34 (defparameter *base
* (merge-pathnames #P
"../" *source-filename
*))
35 (defparameter *spec
* nil
)
36 (defparameter *type-map
* nil
)
38 (defparameter *enum-specs
* nil
)
39 (defparameter *function-specs
* nil
)
40 (defparameter *function-specs-by-name
* nil
)
42 (defparameter *exports
* (list '#:check-linked-program-arb
'#:with-push-attrib
'#:with-new-list
43 '#:check-linked-program
'#:fallback-synchronizing-program
44 '#:check-compiled-shader
'#:*fallback-synchronizing-program-arb
*
45 '#:*fallback-synchronizing-program
* '#:with-begin-query
46 '#:shader-source-from-stream-arb
'#:with-setup-projection
47 '#:with-bind-buffer
'#:with-use-program-arb
'#:with-push-client-attrib
48 '#:fallback-synchronizing-program-arb
'#:shader-source-from-stream
49 '#:with-use-program
'#:synchronizing-program-arb
'#:with-begin
50 '#:with-push-matrix
'#:make-program
'#:make-program-arb
'#:make-shader
51 '#:synchronizing-shader-arb
'#:with-projection-matrix
52 '#:clear-synchronizing-shaders
'#:make-shader-arb
53 '#:check-compiled-shader-arb
'#:with-map-buffer-arb
54 '#:with-bind-buffer-arb
'#:with-push-name
'#:with-map-buffer
55 '#:synchronizing-shader
'#:synchronizing-program
))
57 (defparameter *function-categories
* nil
)
59 (defparameter *predefined-enumerants
* (make-hash-table))
64 (defun plist-keys (plist)
65 "Return all of the keys of a plist"
66 (loop for key in plist by
#'cddr collect key
))
68 (defun plist-values (plist)
69 "Return all of the values of a plist"
70 (loop for key in
(cdr plist
) by
#'cddr collect key
))
72 (defun constantize (symbol)
73 "Converts a symbol into a nice constant-style symbol,
74 changing non-alphanumeric characters to - and surrounding it
76 (intern (format nil
"+~a+"
77 (map 'string
#'(lambda (c) (if (alphanumericp c
) c
#\-
))
78 (string-upcase (string symbol
))))))
80 (defun deconstant (symbol)
81 "Sometimes argument names of OpenGLâ„¢ functions have silly names like
82 't', this is a generalised way to rename them to something more sensible."
83 (if (not (constantp symbol
))
85 (deconstant (intern (concatenate 'string
"_" (symbol-name symbol
))))))
90 (defmacro c-name-of
(func-spec) `(first ,func-spec
))
91 (defmacro lisp-name-of
(func-spec) `(second ,func-spec
))
92 (defmacro freturn-of
(func-spec) `(getf (cddr ,func-spec
) :return
))
93 (defmacro args-of
(func-spec) `(getf (cddr ,func-spec
) :args
))
94 (defun category-of (func-spec) (intern (getf (cddr func-spec
) :category
)))
95 (defmacro alias-of
(func-spec) `(getf (cddr ,func-spec
) :alias
))
96 (defmacro core-of
(func-spec) `(getf (cddr ,func-spec
) :core
))
100 (defparameter *strippable-type-endings
*
101 (list "NV" "ARB" "SGIX" "EXT" "ATI" "IBM" "3DFX" "SGIS"
102 "SUNX" "HP" "GREMEDY" "APPLE" "MESA" "SUN" "INTEL"
105 (defun string-ends-with (string ending
)
106 "Returns t if string ends with ending."
107 (and (>= (length string
) (length ending
))
108 (string= string ending
:start1
(- (length string
) (length ending
)))))
110 (defun string-strip-ending (string ending
)
111 "Returns string (with ending removed, if it was there)."
112 (if (string-ends-with string ending
)
113 (subseq string
0 (- (length string
) (length ending
)))
116 (defun string-strip-endings (string endings
)
117 "Removes any of multiple endings from string, if it has any of them."
119 (string-strip-endings (string-strip-ending string
(car endings
)) (cdr endings
))
120 (string-strip-ending string
(first endings
))))
122 (defun type-map-type-to-gl-type (type-map-type)
123 "Strips the extension suffix off a type and returns an appropriate type symbol
124 suitable for cl-glfw-types or CFFI."
125 (let ((s (string-strip-endings (symbol-name type-map-type
) *strippable-type-endings
*)))
126 (cond ((equal s
"*") :void
)
127 ((equal s
"const GLubyte *") 'string
)
128 ((find #\
* (format nil
"~a" s
)) 'pointer
)
129 ((equal (subseq s
0 2) "GL") (intern (string-upcase (subseq s
2))))
130 ((equal s
"_GLfuncptr") 'pointer
)
133 (defun set-type-maps ()
134 "Fix mappings of specification type names onto valid cl-glfw-types/CFFI symbols."
137 (loop for src-type in
(getf *spec
* :type-map
) by
#'cddr
138 for dst-type in
(cdr (getf *spec
* :type-map
)) by
#'cddr
139 nconc
(list src-type
(type-map-type-to-gl-type dst-type
)))))
143 ;;; {{{ FIX ENUM SPECS
144 (defun set-enum-specs ()
145 "Extract the enum specs from *spec* and resolve all the values"
148 (labels ((resolve-enum (enum-name enum-value
&optional used-groups
)
150 ;; the only end-value type (there are no strings or anything)
151 ((numberp enum-value
) enum-value
)
152 ;; nil value means we have to look everywhere for a value
157 (loop for enum-group-name in
(getf *spec
* :enum-spec
) by
#'cddr
158 for enum-group in
(cdr (getf *spec
* :enum-spec
)) by
#'cddr
159 do
(unless (find enum-group-name used-groups
)
160 (let ((resolved-value (getf enum-group enum-name
)))
162 (push enum-group-name used-groups
)
163 (return-from find-value resolved-value
)))))
164 (return-from resolve-enum
:unable-to-resolve
))
166 ;; it's a name of another symbol, re-resolve with that name
167 ((symbolp enum-value
) (resolve-enum enum-value nil
))
168 ;; a use list means we look in another group for it
169 ((and (listp enum-value
)
170 (eql (first enum-value
) :use
))
173 (getf (getf (getf *spec
* :enum-spec
) (second enum-value
))
176 (t (error "I don't know what to do with the enum definition ~s -> ~s" enum-name enum-value
)))))
177 (loop for enum-group-name in
(getf *spec
* :enum-spec
) by
#'cddr
178 for enum-group in
(cdr (getf *spec
* :enum-spec
)) by
#'cddr
179 unless
(eql enum-group-name
:extensions
)
182 (list enum-group-name
183 (loop for enum-name in enum-group by
#'cddr
184 for enum-value in
(cdr enum-group
) by
#'cddr
187 (resolve-enum enum-name enum-value
(list enum-group-name
)))))))))
190 ;;; {{{ SET FUNC SPECS
191 (defun set-func-specs ()
192 (setf *function-specs-by-name
* (make-hash-table :test
#'equal
))
193 (setf *function-specs
*
194 (loop for func-spec in
(getf *spec
* :functions
)
197 (let* ((category (string-strip-ending (first (getf (rest func-spec
) :category
)) "_DEPRECATED"))
198 (fixed-func-spec (list (first (first func-spec
))
199 (second (first func-spec
))
200 :return
(first (getf (rest func-spec
) :return
))
201 :args
(loop while
(getf (rest func-spec
) :param
) collect
202 (prog1 (getf (rest func-spec
) :param
)
203 (remf (rest func-spec
) :param
)))
205 :deprecated
(first (getf (rest func-spec
) :deprecated
))
206 :version
(first (getf (rest func-spec
) :version
))
207 :alias
(first (getf (rest func-spec
) :alias
)))))
208 (when (find (intern category
) *opengl-versions
* :test
#'eql
)
209 (setf (core-of fixed-func-spec
) t
))
210 (setf (gethash (c-name-of fixed-func-spec
) *function-specs-by-name
*)
213 (dolist (func-spec *function-specs
*)
214 (let ((alias (alias-of func-spec
)))
216 (let ((aliased-spec (gethash alias
*function-specs-by-name
*)))
218 (setf (alias-of func-spec
) aliased-spec
)
220 (warn "Could not find alias ~S for function ~S~%"
221 alias
(c-name-of func-spec
))
222 (remf (cddr func-spec
) :alias
))))
223 (remf (cddr func-spec
) :alias
)))))
228 (setf *spec
* (with-open-file (in (merge-pathnames #P
"src/gl.spec.lisp" *base
*)) (read in
)))
231 (when (getf *reports
* :type-map
)
233 (sort (loop for name in
*type-map
* by
#'cddr
234 for value in
(cdr *type-map
*) by
#'cddr
235 collect
(cons name value
))
237 (string-lessp (string (cdr a
)) (string (cdr b
)))))
238 do
(format t
"~& ~s:~40t~s~%" (car n-v
) (cdr n-v
))))
242 (remf *enum-specs
* :extensions
)
244 ;; print out initial statistics
245 (format t
"~a functions~%" (length *function-specs
*))
246 (format t
"~a type-maps~%" (/ (length *type-map
*) 2))
247 (format t
"~a enum-specs~%" (length *enum-specs
*))
250 (when (getf *reports
* :property-counts
)
251 ;; count up the properties of functions, what's useful for parsing?
252 (let ((property-counts ()))
253 (dolist (function-spec *function-specs
*)
254 (dolist (property (plist-keys (rest function-spec
)))
255 (incf (getf property-counts property
0))))
256 (let ((*print-pretty
* t
))
257 (format t
"Property counts: ~a~%" property-counts
))))
259 ;; categorize functions
260 (dolist (function-spec *function-specs
*)
262 (getf *function-categories
* (category-of function-spec
))))
264 ;;Work out which categories are actually extensions we want
265 (dolist (category-sym (nconc (plist-keys *function-categories
*)
266 (plist-keys *enum-specs
*)))
267 (let* ((category-string (string category-sym
))
268 (underscore-pos (position #\_ category-string
)))
269 (when (and (integerp underscore-pos
)
270 (plusp underscore-pos
)
271 (every (lambda (char)
272 (or (upper-case-p char
)
273 (digit-char-p char
)))
274 (subseq category-string
0 underscore-pos
))
275 (not (find category-sym
*extension-names
*)))
276 (push category-sym
*extension-names
*))))
278 (when (getf *reports
* :function-category-counts
)
279 (format t
"Category function counts:~%")
280 (loop for cat-name in
*function-categories
* by
#'cddr
281 for cat-contents in
(cdr *function-categories
*) by
#'cddr
282 do
(format t
" ~S: ~S~%" cat-name
(length cat-contents
)))))
286 (defun gl-extension-function-definition (func-spec)
287 (push (lisp-name-of func-spec
) *exports
*)
288 `(defglextfun ,@func-spec
))
290 (defun gl-function-definition (func-spec)
291 (push (lisp-name-of func-spec
) *exports
*)
292 `(defglfun ,@func-spec
))
298 ;; this is the real template opengl defpackage
299 (defun make-opengl-defpackage (exports)
300 "Returns the defpackage for opengl with the exports list given."
301 `(defpackage #:cl-glfw-opengl
302 (:use
#:cffi
#:cl
#:cl-glfw-types
#:cl-glfw-scaffolding
)
303 (:nicknames
#:gl
#:opengl
)
304 (:shadowing-import-from
#:cl-glfw-types
#:boolean
#:byte
#:float
#:char
#:string
#:pointer
)
306 #:enum
#:boolean
#:bitfield
#:byte
#:short
#:int
#:sizei
#:ubyte
#:ushort
#:uint
307 #:float
#:clampf
#:double
#:clampd
#:void
#:uint64
#:int64
312 ,@(mapcar #'make-symbol
(mapcar #'string-upcase
(mapcar #'string exports
))))))
315 (defmacro with-output-file
((out name
) &body forms
)
316 (declare (type symbol out
))
317 `(with-open-file (,out
(merge-pathnames ,name
*base
*) :direction
:output
:if-exists
:supersede
)
318 (when (getf *reports
* :files-output
)
319 (format t
"Generating ~s~%" (truename ,out
)))
320 (format ,out
";;;; This file was automatically generated by ~a~%" *source-filename
*)
330 (defun output-core ()
331 ;; write the main bindings file...
332 (with-output-file (out #P
"lib/opengl-core.lisp")
334 (print `(in-package #:cl-glfw-opengl
) out
)
336 ;; dump all enumerations not in an extension
337 (loop for enum-group-name in
*enum-specs
* by
#'cddr
338 for enum-group in
(cdr *enum-specs
*) by
#'cddr
339 unless
(find enum-group-name
*extension-names
*)
341 (let ((enums-to-define
342 (loop for enum-name in enum-group by
#'cddr
343 for enum-value in
(cdr enum-group
) by
#'cddr
345 (let ((existing (gethash enum-name
*predefined-enumerants
*)))
348 (setf (gethash enum-name
*predefined-enumerants
*) enum-value
)
349 (list (cons enum-name enum-value
)))
350 ((eql existing enum-value
)
353 (warn "Won't redefine enum ~A as ~A, because it is already ~A"
354 enum-name enum-value existing
)
356 ;; when this group is not empty and there is a name that isn't already defined
357 (when enums-to-define
358 (format out
"~&~%;;;; {{{ ~A~%" (string enum-group-name
))
359 (loop for
(enum-name . enum-value
) in enums-to-define do
360 (let ((constant-name (constantize enum-name
)))
361 (push constant-name
*exports
*)
362 (print `(defconstant ,constant-name
,enum-value
) out
)))
363 (format out
"~&~%;;;; }}}~%"))
364 (remf *enum-specs
* enum-group-name
)))))
366 (defclass literal-string
()
367 ((string :type string
:initarg
:string
)))
369 (defmethod print-object ((string literal-string
) stream
)
370 (write-string (slot-value string
'string
) stream
))
372 (defun printable-string (str)
373 (make-instance 'literal-string
:string str
))
375 (defun output-category (name category-names
)
376 "write out the extension named by category name"
378 (let ((enum-specs (copy-tree *enum-specs
*))
379 (function-categories (copy-tree *function-categories
*)))
381 ;; collect up the elements of the extension, the enums and functions
383 (loop for category-name in category-names nconcing
384 (loop while
(getf enum-specs category-name
) nconcing
385 (prog1 (loop for enum-name in
(getf enum-specs category-name
) by
#'cddr
386 for enum-value in
(cdr (getf enum-specs category-name
)) by
#'cddr
387 unless
(gethash enum-name
*predefined-enumerants
*)
389 (let ((constant-name (constantize enum-name
)))
390 (push constant-name
*exports
*)
391 `(defconstant ,constant-name
,enum-value
)))
392 (remf enum-specs category-name
)))))
394 (loop for category-name in category-names nconcing
395 (loop while
(getf function-categories category-name
) nconcing
397 (let ((func-specs (getf function-categories category-name
)))
398 (loop for func-spec in func-specs
399 unless
(let ((deprecated-at (getf func-spec
:deprecated
)))
401 (find (intern (concatenate 'string
"VERSION_" (substitute #\_
#\. deprecated-at
)))
404 (remf function-categories category-name
)))))
406 (function-definitions))
408 (let ((all-extension-specs-aliased (every #'(lambda (function-spec)
409 (or (core-of function-spec
)
410 (alias-of function-spec
)))
413 (loop for function-spec in function-specs do
415 ((find (category-of function-spec
) *core-opengl-versions
*)
416 (push (gl-function-definition function-spec
) function-definitions
))
418 (push function-spec extension-specs
)
419 (push (gl-extension-function-definition function-spec
) function-definitions
)
420 (when all-extension-specs-aliased
421 (push (printable-string "#-win32") function-definitions
)
422 (push (gl-extension-function-definition (alias-of function-spec
)) function-definitions
))))))
424 (setf extension-specs
(nreverse extension-specs
)
425 function-definitions
(nreverse function-definitions
))
427 (format t
"~A from ~A:~D functions (~D being extensions), ~D enumerations~%"
428 (string name
) (mapcar 'string category-names
)
429 (length function-definitions
)
430 (length extension-specs
)
431 (length enumerations
))
433 ;; only when we have either of these components, actually generate a system
434 (when (or enumerations function-definitions
)
435 (let* ((core-version (find name
*opengl-versions
*))
436 (top-level-asd core-version
))
437 ;; write out the ASD definition
438 (with-output-file (out (format nil
"~acl-glfw-opengl-~a.asd" (if top-level-asd
"" "lib/") name
))
439 (let* ((system-name (string-downcase (format nil
"cl-glfw-opengl-~a" name
)))
440 (system-package (make-symbol (string-upcase (concatenate 'string system-name
"-system")))))
441 (print `(defpackage ,system-package
(:use
#:asdf
#:cl
)) out
)
442 (print `(in-package ,system-package
) out
)
443 (print `(defsystem ,(intern (string-upcase system-name
))
444 :description
,(format nil
"cl-glfw's ~a binding" name
)
445 :author
,(format nil
"Generated by cl-glfw's ~a" *source-filename
*)
446 :licence
"Public Domain"
447 :depends-on
(cl-glfw-opengl-core)
448 :components
((:file
,(concatenate 'string
(if top-level-asd
"lib/" "") "opengl-" (string-downcase (symbol-name name
))))))
451 ;; write the enumerations and function bindings
452 (with-output-file (out (format nil
"lib/opengl-~a.lisp" name
))
453 (print '(in-package #:cl-glfw-opengl
) out
)
454 (format out
"~&~%;;;; ~a~&" name
)
456 (print `(eval-when (:load-toplevel
)
457 (when (and (boundp '*version-loaded
*)
458 (not (eq ',name
*version-loaded
*)))
459 (warn "Loading cl-glfw-opengl-~a over the top of already-loaded cl-glfw-opengl-~a~%" ',name
*version-loaded
*))
460 (defparameter *version-loaded
* ',name
)) out
))
461 (dolist (enumeration enumerations
) (print enumeration out
))
462 (dolist (function function-definitions
) (print function out
))
463 (when extension-specs
464 (push (format nil
"LOAD-~A" name
) *exports
*)
465 (print `(make-extension-loader ,name
,extension-specs
) out
))))))))
468 (defun output-everything ()
469 ;; some nice printing options
470 (let ((*print-case
* :downcase
)
476 ;;Write the bindings for the core versions
477 (let (current-categories)
478 (loop for extension-name in
*opengl-versions
* do
479 (let ((deprecated-extension-name (intern (concatenate 'string
(string extension-name
) "_DEPRECATED")))
481 (when (find extension-name
*extension-names
*)
482 (push extension-name current-categories
)
484 (when (find deprecated-extension-name
*extension-names
*)
485 (push deprecated-extension-name current-categories
)
488 (output-category extension-name
(reverse current-categories
)))))
489 ;;Remove them from the lists to be processed
490 (loop for name in current-categories do
491 (loop while
(getf *function-categories
* name
) do
(remf *function-categories
* name
))
492 (loop while
(getf *enum-specs
* name
) do
(remf *enum-specs
* name
))))
494 ;;Process all the extension categories
495 (dolist (category-name *extension-names
*)
496 (output-category category-name
(list category-name
))
497 (loop while
(getf *function-categories
* category-name
) do
(remf *function-categories
* category-name
))
498 (loop while
(getf *enum-specs
* category-name
) do
(remf *enum-specs
* category-name
)))
500 (with-output-file (out #P
"lib/opengl-type-map.lisp")
501 (print `(in-package #:cl-glfw-opengl
) out
)
502 (print `(setf *type-map
* ',*type-map
*) out
))
504 (with-output-file (out #P
"lib/opengl-package.lisp")
505 (print (make-opengl-defpackage (remove-duplicates (nreverse *exports
*))) out
)))
507 (when (and (getf *reports
* :leftover-functions
)
508 *function-categories
*)
509 (format t
"~&Leftover functions:~% ~s~%" *function-categories
*))
511 (when (and (getf *reports
* :leftover-enums
)
513 (format t
"~&Leftover enums:~% ~s~%" *enum-specs
*)))