1 ;; You should nominially invoke this file via ./generators/make-opengl-bindings.rb
2 ;; as that performs the necessary setup.
4 (declaim (optimize (debug 3)))
7 (defparameter *reports
* '(:type-map nil
12 :function-category-counts nil
))
15 (defparameter *opengl-version-systems
* '("cl-glfw-opengl-version_1_1"
16 "cl-glfw-opengl-version_1_2"
17 "cl-glfw-opengl-version_1_3"
18 "cl-glfw-opengl-version_1_4"
19 "cl-glfw-opengl-version_1_5"
20 "cl-glfw-opengl-version_2_0"
21 "cl-glfw-opengl-version_2_1")
22 "List of versioned extensions for dependency generation.
23 Must be in the correct order.")
26 (defparameter *base
* (merge-pathnames #P
"../" (load-time-value *load-truename
*)))
27 (defparameter *spec
* nil
)
28 (defparameter *type-map
* nil
)
30 (defparameter *enum-specs
* nil
)
31 (defparameter *function-specs
* nil
)
33 (defparameter *base-categories
*
34 '(|display-list| |drawing| |drawing-control| |feedback|
35 |framebuf| |misc| |modeling| |pixel-op| |pixel-rw|
38 (defparameter *exports
* nil
)
40 (defparameter *function-categories
* nil
)
42 (defparameter *predefined-enumerants
* nil
)
47 (defun plist-keys (plist)
48 "Return all of the keys of a plist"
49 (loop for key in plist by
#'cddr collect key
))
51 (defun plist-values (plist)
52 "Return all of the values of a plist"
53 (loop for key in
(cdr plist
) by
#'cddr collect key
))
55 (defun constantize (symbol)
56 "Converts a symbol into a nice constant-style symbol,
57 changing non-alphanumeric characters to - and surrounding it
59 (intern (format nil
"+~a+"
60 (map 'string
#'(lambda (c) (if (alphanumericp c
) c
#\-
))
61 (string-upcase (string symbol
))))))
63 (defun deconstant (symbol)
64 "Sometimes argument names of OpenGL™ functions have silly names like
65 't', this is a generalised way to rename them to something more sensible."
66 (if (not (constantp symbol
))
68 (deconstant (intern (concatenate 'string
"_" (symbol-name symbol
))))))
73 (defmacro func-spec-accessors
(names)
74 "Makes a bunch of nice “name-of” accessors for a func-spec plist
75 to all symbols listed in names. "
76 `(progn ,@(mapcar #'(lambda (k)
77 `(defun ,(intern (concatenate 'string
(string k
) "-OF")) (func-spec)
79 (getf (rest func-spec
)
80 ,(intern (symbol-name k
) '#:keyword
)))))
83 (defun c-name-of (func-spec)
84 (first (first func-spec
)))
86 (defun lisp-name-of (func-spec)
87 (second (first func-spec
)))
89 (func-spec-accessors (offset wglflags glsopcode glxsingle version category dlflags param
90 glxropcode glxflags glsflags vectorequiv extension glxvendorpriv glsalias
91 alias glfflags glxvectorequiv beginend
))
93 (defun freturn-of (func-spec)
94 "Returns the return type of the func-spec"
95 (first (getf (rest func-spec
) :return
)))
97 (defun args-of (func-spec)
98 "Returns the list of argument plists of the func-spec"
99 (getf (rest func-spec
) :args
))
102 ;;; {{{ FIX TYPE-MAPS
103 (defparameter *strippable-type-endings
*
104 (list "NV" "ARB" "SGIX" "EXT" "ATI" "IBM" "3DFX" "SGIS"
105 "SUNX" "HP" "GREMEDY" "APPLE" "MESA" "SUN" "INTEL"
108 (defun string-ends-with (string ending
)
109 "Returns t if string ends with ending."
110 (and (>= (length string
) (length ending
))
111 (string= string ending
:start1
(- (length string
) (length ending
)))))
113 (defun string-strip-ending (string ending
)
114 "Returns string (with ending removed, if it was there)."
115 (if (string-ends-with string ending
)
116 (subseq string
0 (- (length string
) (length ending
)))
119 (defun string-strip-endings (string endings
)
120 "Removes any of multiple endings from string, if it has any of them."
122 (string-strip-endings (string-strip-ending string
(car endings
)) (cdr endings
))
123 (string-strip-ending string
(first endings
))))
125 (defun type-map-type-to-gl-type (type-map-type)
126 "Strips the extension suffix off a type and returns an appropriate type symbol
127 suitable for cl-glfw-types or CFFI."
128 (let ((s (string-strip-endings (symbol-name type-map-type
) *strippable-type-endings
*)))
129 (cond ((equal s
"*") :void
)
130 ((find #\
* (format nil
"~a" s
)) :pointer
)
131 ((equal (subseq s
0 2) "GL") (intern (string-upcase (subseq s
2))))
132 ((equal s
"_GLfuncptr") :pointer
)
135 (defun set-type-maps ()
136 "Fix mappings of specification type names onto valid cl-glfw-types/CFFI symbols."
139 (loop for src-type in
(getf *spec
* :type-map
) by
#'cddr
140 for dst-type in
(cdr (getf *spec
* :type-map
)) by
#'cddr
141 nconc
(list src-type
(type-map-type-to-gl-type dst-type
)))))
145 ;;; {{{ FIX ENUM SPECS
146 (defun set-enum-specs ()
147 "Extract the enum specs from *spec* and resolve all the values"
150 (labels ((resolve-enum (enum-name enum-value
&optional used-groups
)
152 ;; the only end-value type (there are no strings or anything)
153 ((numberp enum-value
) enum-value
)
154 ;; nil value means we have to look everywhere for a value
159 (loop for enum-group-name in
(getf *spec
* :enum-spec
) by
#'cddr
160 for enum-group in
(cdr (getf *spec
* :enum-spec
)) by
#'cddr
161 do
(unless (find enum-group-name used-groups
)
162 (let ((resolved-value (getf enum-group enum-name
)))
164 (push enum-group-name used-groups
)
165 (return-from find-value resolved-value
)))))
166 (return-from resolve-enum
:unable-to-resolve
))
168 ;; it's a name of another symbol, re-resolve with that name
169 ((symbolp enum-value
) (resolve-enum enum-value nil
))
170 ;; a use list means we look in another group for it
171 ((and (listp enum-value
)
172 (eql (first enum-value
) :use
))
175 (getf (getf (getf *spec
* :enum-spec
) (second enum-value
))
178 (t (error "I don't know what to do with the enum definition ~s -> ~s" enum-name enum-value
)))))
179 (loop for enum-group-name in
(getf *spec
* :enum-spec
) by
#'cddr
180 for enum-group in
(cdr (getf *spec
* :enum-spec
)) by
#'cddr
181 unless
(eql enum-group-name
:extensions
)
184 (list enum-group-name
185 (loop for enum-name in enum-group by
#'cddr
186 for enum-value in
(cdr enum-group
) by
#'cddr
189 (resolve-enum enum-name enum-value
(list enum-group-name
)))))))))
194 (setf *spec
* (with-open-file (in (merge-pathnames #P
"src/gl.spec.lisp" *base
*)) (read in
))
195 *function-specs
* (rest (getf *spec
* :functions
)))
197 (when (getf *reports
* :type-map
)
199 (sort (loop for name in
*type-map
* by
#'cddr
200 for value in
(cdr *type-map
*) by
#'cddr
201 collect
(cons name value
))
203 (string-lessp (string (cdr a
)) (string (cdr b
)))))
204 do
(format t
"~& ~s:~40t~s~%" (car n-v
) (cdr n-v
))))
207 (remf *enum-specs
* :extensions
)
208 ;; print out initial statistics
209 (format t
"~a functions~%" (length *function-specs
*))
210 (format t
"~a type-maps~%" (/ (length *type-map
*) 2))
211 (format t
"~a enum-specs~%" (length *enum-specs
*))
214 (when (getf *reports
* :property-counts
)
215 ;; count up the properties of functions, what's useful for parsing?
216 (let ((property-counts ()))
217 (dolist (function-spec *function-specs
*)
218 (dolist (property (plist-keys (rest function-spec
)))
219 (incf (getf property-counts property
0))))
220 (let ((*print-pretty
* t
))
221 (format t
"Property counts: ~a~%" property-counts
))))
223 ;; collect arguments of functions into ordered list with all meta-data attached
224 (dolist (func-spec *function-specs
*)
226 (do* ((arg-spec (getf (rest func-spec
) :param
) (getf (rest func-spec
) :param
)))
228 (setf (getf arg-specs
(getf arg-spec
:name
))
230 (remf (rest func-spec
) :param
))
231 (setf (getf (rest func-spec
) :args
)
232 (loop for arg-name in
(args-of func-spec
) collecting
233 (getf arg-specs arg-name
)))))
235 ;; categorize functions
236 (dolist (function-spec *function-specs
*)
238 (getf *function-categories
* (intern (category-of function-spec
)))))
239 (when (getf *reports
* :function-category-counts
)
240 (format t
"Category counts:~%")
241 (loop for cat-name in
*function-categories
* by
#'cddr
242 for cat-contents in
(cdr *function-categories
*) by
#'cddr
243 do
(format t
" ~S: ~S~%" cat-name
(length cat-contents
)))))
247 (defun gl-extension-function-definition (func-spec)
248 (push (lisp-name-of func-spec
) *exports
*)
249 `(defglextfun ,func-spec
))
251 (defun gl-function-definition (func-spec)
252 (push (lisp-name-of func-spec
) *exports
*)
253 `(defglfun ,func-spec
))
259 ;; this is the real template opengl defpackage
260 (defun make-opengl-defpackage (exports)
261 "Returns the defpackage for opengl with the exports list given."
262 `(defpackage #:cl-glfw-opengl
263 (:use
#:cffi
#:cl
#:cl-glfw-types
#:cl-glfw-scaffolding
)
264 (:nicknames
#:gl
#:opengl
)
265 (:shadowing-import-from
#:cl-glfw-types
#:boolean
#:byte
#:float
#:char
#:string
)
267 #:enum
#:boolean
#:bitfield
#:byte
#:short
#:int
#:sizei
#:ubyte
#:ushort
#:uint
268 #:float
#:clampf
#:double
#:clampd
#:void
#:uint64
#:int64
273 ,@(mapcar #'make-symbol
(mapcar #'string-upcase
(mapcar #'string exports
))))))
276 (defmacro with-output-file
((out name
) &body forms
)
277 (declare (type symbol out
))
278 `(with-open-file (,out
(merge-pathnames ,name
*base
*) :direction
:output
:if-exists
:supersede
)
279 (if (getf *reports
* :files-output
)
280 (format t
"Generating ~s~%" (truename ,out
))
281 (progn (write-char #\.
)
283 (format ,out
";;;; This file was automatically generated by ~a~%" (load-time-value *load-truename
*))
289 (defun output-extension (category-name &optional
(function-transform #'gl-extension-function-definition
) (extension-name category-name
))
290 "write out the extension named by category name"
292 ;; collect up the elements of the extension, the enums and functions
294 (loop while
(getf *enum-specs
* category-name
) nconcing
295 (prog1 (loop for enum-name in
(getf *enum-specs
* category-name
) by
#'cddr
296 for enum-value in
(cdr (getf *enum-specs
* category-name
)) by
#'cddr
297 unless
(find enum-name
*predefined-enumerants
*)
299 (let ((constant-name (constantize enum-name
)))
300 (push constant-name
*exports
*)
301 `(defconstant ,constant-name
,enum-value
)))
302 (remf *enum-specs
* category-name
))))
304 (loop while
(getf *function-categories
* category-name
) nconcing
306 (mapcar function-transform
(getf *function-categories
* category-name
))
307 (remf *function-categories
* category-name
)))))
308 ;; only when we have either of these components, actually generate a system
309 (when (or enumerations functions
)
310 ;; write out the ASD definition
311 (with-output-file (out (format nil
"lib/cl-glfw-opengl-~a.asd" extension-name
))
312 (let* ((system-name (string-downcase (format nil
"cl-glfw-opengl-~a" extension-name
)))
313 (system-package (make-symbol (string-upcase (concatenate 'string system-name
"-system")))))
314 (print `(defpackage ,system-package
(:use
#:asdf
#:cl
)) out
)
315 (print `(in-package ,system-package
) out
)
316 (print `(defsystem ,(intern (string-upcase system-name
))
317 :description
,(format nil
"cl-glfw's ~a binding" extension-name
)
318 :author
,(format nil
"Generated by cl-glfw's ~a" (load-time-value *load-truename
*))
319 :licence
"Public Domain"
320 :depends-on
(cl-glfw-opengl
321 ,@(let ((pos (position system-name
*opengl-version-systems
* :test
'equal
)))
322 (when (and pos
(plusp pos
))
323 (list (intern (elt *opengl-version-systems
* (1- pos
)))))))
324 :components
((:file
,(concatenate 'string
"opengl-"
325 (string-downcase (symbol-name extension-name
))))))
328 ;; write the enumerations and function bindings
329 (with-output-file (out (format nil
"lib/opengl-~a.lisp" extension-name
))
330 (print '(in-package #:cl-glfw-opengl
) out
)
331 (format out
"~&~%;;;; ~a~&" category-name
)
332 (dolist (enumeration enumerations
) (print enumeration out
))
333 (dolist (function functions
) (print function out
))))))
336 (defun output-category (category-name out
)
337 "Output a whole category and remove it from the list."
338 (dolist (func-spec (getf *function-categories
* category-name
))
339 (print (gl-function-definition func-spec
) out
))
340 (remf *function-categories
* category-name
))
342 (defun output-core ()
343 ;; write the main bindings file...
344 (with-output-file (out #P
"lib/opengl.lisp")
346 (print `(in-package #:cl-glfw-opengl
) out
)
348 ;; dump all enumerations not in an extension
349 (loop for enum-group-name in
*enum-specs
* by
#'cddr
350 for enum-group in
(cdr *enum-specs
*) by
#'cddr
351 unless
(getf *function-categories
* enum-group-name
)
353 ;; when this group is not empty and there is a name that isn't already defined
354 (when (and enum-group
355 (loop for enum-name in enum-group by
#'cddr
356 when
(not (find enum-name
*predefined-enumerants
*))
358 (format out
"~&~%;;;; {{{ ~a~%" enum-group-name
)
359 (loop for enum-name in enum-group by
#'cddr
360 for enum-value in
(cdr enum-group
) by
#'cddr
361 unless
(find enum-name
*predefined-enumerants
*)
363 (push enum-name
*predefined-enumerants
*)
364 (let ((constant-name (constantize enum-name
)))
365 (push constant-name
*exports
*)
366 (print `(defconstant ,constant-name
,enum-value
) out
)))
367 (format out
"~&~%;;;; }}}~%"))
368 (remf *enum-specs
* enum-group
))
370 ;; all base 1.0 categories
371 (loop for category-name in
*base-categories
*
373 (format out
"~&~%;;;; {{{ ~a~%" category-name
)
374 (output-category category-name out
)
375 (format out
"~&~%;;;; }}}~%"))))
378 (defun output-everything ()
379 ;; some nice printing options
380 (let ((*print-case
* :downcase
)
386 (output-extension '|
1_1|
#'gl-function-definition
'VERSION_1_1
)
388 (output-extension 'VERSION_1_2
#'gl-function-definition
)
389 (output-extension 'VERSION_1_3
#'gl-function-definition
)
390 (output-extension 'VERSION_1_4
#'gl-function-definition
)
391 (output-extension 'VERSION_1_5
#'gl-function-definition
)
392 (output-extension 'VERSION_2_0
#'gl-function-definition
)
393 (output-extension 'VERSION_2_1
#'gl-function-definition
)
395 (dolist (category-name (remove-duplicates
396 (nconc (plist-keys *function-categories
*)
397 (plist-keys *enum-specs
*))))
398 (output-extension category-name
))
400 (with-output-file (out #P
"lib/opengl-type-map.lisp")
401 (print `(in-package #:cl-glfw-opengl
) out
)
402 (print `(setf *type-map
* ',*type-map
*) out
))
404 (with-output-file (out #P
"lib/opengl-package.lisp")
405 (print (make-opengl-defpackage (remove-duplicates (nreverse *exports
*))) out
)))
407 (when (and (getf *reports
* :leftover-functions
)
408 *function-categories
*)
409 (format t
"~&Leftover functions:~% ~s~%" *function-categories
*))
411 (when (and (getf *reports
* :leftover-enums
)
413 (format t
"~&Leftover enums:~% ~s~%" *enum-specs
*)))