1 ;; You should nominially invoke this file via ./generators/make-opengl-bindings.rb
2 ;; as that performs the necessary setup.
4 ;; proto-package for type-mappings only
6 (defpackage #:cl-glfw-opengl
8 (:nicknames
#:gl
#:opengl
)
9 (:shadow boolean byte float char string
)
11 enum boolean bitfield byte short int sizei ubyte ushort uint float clampf
12 double clampd void uint64 int64 intptr sizeiptr handle char string half
))
15 (declaim (optimize (debug 3)))
18 (defparameter *reports
* '(:type-map nil
23 :function-category-counts t
))
26 (defparameter *opengl-version-systems
* '("cl-glfw-opengl-version_1_1"
27 "cl-glfw-opengl-version_1_2"
28 "cl-glfw-opengl-version_1_3"
29 "cl-glfw-opengl-version_1_4"
30 "cl-glfw-opengl-version_1_5"
31 "cl-glfw-opengl-version_2_0"
32 "cl-glfw-opengl-version_2_1")
33 "List of versioned extensions for dependency generation.
34 Must be in the correct order.")
37 (defparameter *base
* (merge-pathnames #P
"../" (load-time-value *load-truename
*)))
38 (defparameter *spec
* nil
)
39 (defparameter *type-map
* nil
)
41 (defparameter *enum-specs
* nil
)
42 (defparameter *function-specs
* nil
)
44 (defparameter *base-categories
*
45 '(|display-list| |drawing| |drawing-control| |feedback|
46 |framebuf| |misc| |modeling| |pixel-op| |pixel-rw|
49 (defparameter *exports
* nil
)
51 (defparameter *function-categories
* nil
)
53 (defparameter *predefined-enumerants
* nil
)
58 (defun plist-keys (plist)
59 "Return all of the keys of a plist"
60 (loop for key in plist by
#'cddr collect key
))
62 (defun plist-values (plist)
63 "Return all of the values of a plist"
64 (loop for key in
(cdr plist
) by
#'cddr collect key
))
66 (defun constantize (symbol)
67 "Converts a symbol into a nice constant-style symbol,
68 changing non-alphanumeric characters to - and surrounding it
70 (intern (format nil
"+~a+"
71 (map 'string
#'(lambda (c) (if (alphanumericp c
) c
#\-
))
72 (string-upcase (string symbol
))))))
74 (defun deconstant (symbol)
75 "Sometimes argument names of OpenGL™ functions have silly names like
76 't', this is a generalised way to rename them to something more sensible."
77 (if (not (constantp symbol
))
79 (deconstant (intern (concatenate 'string
"_" (symbol-name symbol
))))))
84 (defmacro func-spec-accessors
(names)
85 "Makes a bunch of nice “name-of” accessors for a func-spec plist
86 to all symbols listed in names. "
87 `(progn ,@(mapcar #'(lambda (k)
88 `(defun ,(intern (concatenate 'string
(string k
) "-OF")) (func-spec)
90 (getf (rest func-spec
)
91 ,(intern (symbol-name k
) '#:keyword
)))))
94 (defun c-name-of (func-spec)
95 (first (first func-spec
)))
97 (defun lisp-name-of (func-spec)
98 (second (first func-spec
)))
100 (func-spec-accessors (offset wglflags glsopcode glxsingle version category dlflags param
101 glxropcode glxflags glsflags vectorequiv extension glxvendorpriv glsalias
102 alias glfflags glxvectorequiv beginend
))
104 (defun freturn-of (func-spec)
105 "Returns the return type of the func-spec"
106 (first (getf (rest func-spec
) :return
)))
108 (defun args-of (func-spec)
109 "Returns the list of argument plists of the func-spec"
110 (getf (rest func-spec
) :args
))
113 ;;; {{{ FIX TYPE-MAPS
114 (defparameter *strippable-type-endings
*
115 (list "NV" "ARB" "SGIX" "EXT" "ATI" "IBM" "3DFX" "SGIS"
116 "SUNX" "HP" "GREMEDY" "APPLE" "MESA" "SUN" "INTEL"
119 (defun string-ends-with (string ending
)
120 "Returns t if string ends with ending."
121 (and (>= (length string
) (length ending
))
122 (string= string ending
:start1
(- (length string
) (length ending
)))))
124 (defun string-strip-ending (string ending
)
125 "Returns string (with ending removed, if it was there)."
126 (if (string-ends-with string ending
)
127 (subseq string
0 (- (length string
) (length ending
)))
130 (defun string-strip-endings (string endings
)
131 "Removes any of multiple endings from string, if it has any of them."
133 (string-strip-endings (string-strip-ending string
(car endings
)) (cdr endings
))
134 (string-strip-ending string
(first endings
))))
136 (defun type-map-type-to-gl-type (type-map-type)
137 "Strips the extension suffix off a type and returns an appropriate type symbol
138 suitable for cl-glfw-types or CFFI."
139 (let ((s (string-strip-endings (symbol-name type-map-type
) *strippable-type-endings
*)))
140 (cond ((equal s
"*") :void
)
141 ((find #\
* (format nil
"~a" s
)) :pointer
)
142 ((equal (subseq s
0 2) "GL") (intern (string-upcase (subseq s
2)) (find-package '#:gl
)))
143 ((equal s
"_GLfuncptr") :pointer
)
146 (defun set-type-maps ()
147 "Fix mappings of specification type names onto valid cl-glfw-types/CFFI symbols."
150 (loop for src-type in
(getf *spec
* :type-map
) by
#'cddr
151 for dst-type in
(cdr (getf *spec
* :type-map
)) by
#'cddr
152 nconc
(list src-type
(type-map-type-to-gl-type dst-type
)))))
156 ;;; {{{ FIX ENUM SPECS
157 (defun set-enum-specs ()
158 "Extract the enum specs from *spec* and resolve all the values"
161 (labels ((resolve-enum (enum-name enum-value
&optional used-groups
)
163 ;; the only end-value type (there are no strings or anything)
164 ((numberp enum-value
) enum-value
)
165 ;; nil value means we have to look everywhere for a value
170 (loop for enum-group-name in
(getf *spec
* :enum-spec
) by
#'cddr
171 for enum-group in
(cdr (getf *spec
* :enum-spec
)) by
#'cddr
172 do
(unless (find enum-group-name used-groups
)
173 (let ((resolved-value (getf enum-group enum-name
)))
175 (push enum-group-name used-groups
)
176 (return-from find-value resolved-value
)))))
177 (return-from resolve-enum
:unable-to-resolve
))
179 ;; it's a name of another symbol, re-resolve with that name
180 ((symbolp enum-value
) (resolve-enum enum-value nil
))
181 ;; a use list means we look in another group for it
182 ((and (listp enum-value
)
183 (eql (first enum-value
) :use
))
186 (getf (getf (getf *spec
* :enum-spec
) (second enum-value
))
189 (t (error "I don't know what to do with the enum definition ~s -> ~s" enum-name enum-value
)))))
190 (loop for enum-group-name in
(getf *spec
* :enum-spec
) by
#'cddr
191 for enum-group in
(cdr (getf *spec
* :enum-spec
)) by
#'cddr
192 unless
(eql enum-group-name
:extensions
)
194 (list enum-group-name
195 (loop for enum-name in enum-group by
#'cddr
196 for enum-value in
(cdr enum-group
) by
#'cddr
199 (resolve-enum enum-name enum-value
(list enum-group-name
)))))))))
204 (setf *spec
* (with-open-file (in (merge-pathnames #P
"src/gl.spec.lisp" *base
*)) (read in
))
205 *function-specs
* (rest (getf *spec
* :functions
)))
207 (when (getf *reports
* :type-map
)
209 (sort (loop for name in
*type-map
* by
#'cddr
210 for value in
(cdr *type-map
*) by
#'cddr
211 collect
(cons name value
))
213 (string-lessp (string (cdr a
)) (string (cdr b
)))))
214 do
(format t
"~& ~s:~40t~s~%" (car n-v
) (cdr n-v
))))
217 (remf *enum-specs
* :extensions
)
218 ;; print out initial statistics
219 (format t
"~a functions~%" (length *function-specs
*))
220 (format t
"~a type-maps~%" (/ (length *type-map
*) 2))
221 (format t
"~a enum-specs~%" (length *enum-specs
*))
224 (when (getf *reports
* :property-counts
)
225 ;; count up the properties of functions, what's useful for parsing?
226 (let ((property-counts ()))
227 (dolist (function-spec *function-specs
*)
228 (dolist (property (plist-keys (rest function-spec
)))
229 (incf (getf property-counts property
0))))
230 (let ((*print-pretty
* t
))
231 (format t
"Property counts: ~a~%" property-counts
))))
233 ;; collect arguments of functions into ordered list with all meta-data attached
234 (dolist (func-spec *function-specs
*)
236 (do* ((arg-spec (getf (rest func-spec
) :param
) (getf (rest func-spec
) :param
)))
238 (setf (getf arg-specs
(getf arg-spec
:name
))
240 (remf (rest func-spec
) :param
))
241 (setf (getf (rest func-spec
) :args
)
242 (loop for arg-name in
(args-of func-spec
) collecting
243 (getf arg-specs arg-name
)))))
245 ;; categorize functions
246 (dolist (function-spec *function-specs
*)
248 (getf *function-categories
* (intern (category-of function-spec
)))))
249 (when (getf *reports
* :function-category-counts
)
250 (format t
"Category counts:~%")
251 (loop for cat-name in
*function-categories
* by
#'cddr
252 for cat-contents in
(cdr *function-categories
*) by
#'cddr
253 do
(format t
" ~S: ~S~%" cat-name
(length cat-contents
)))))
257 (defun gl-extension-function-definition (func-spec)
258 (push (lisp-name-of func-spec
) *exports
*)
259 `(defglextfun ,func-spec
))
261 (defun gl-function-definition (func-spec)
262 (push (lisp-name-of func-spec
) *exports
*)
263 `(defglfun ,func-spec
))
269 ;; this is the real template opengl defpackage
270 (defun make-opengl-defpackage (exports)
271 "Returns the defpackage for opengl with the exports list given."
272 `(defpackage #:cl-glfw-opengl
273 (:use
#:cffi
#:cl
#:cl-glfw-types
#:cl-glfw-scaffolding
)
274 (:nicknames
#:gl
#:opengl
)
275 (:shadowing-import-from
#:cl-glfw-types
#:boolean
#:byte
#:float
#:char
#:string
)
277 #:enum
#:boolean
#:bitfield
#:byte
#:short
#:int
#:sizei
#:ubyte
#:ushort
#:uint
278 #:float
#:clampf
#:double
#:clampd
#:void
#:uint64
#:int64
283 ,@(mapcar #'make-symbol
(mapcar #'string-upcase
(mapcar #'string exports
))))))
286 (defmacro with-output-file
((out name
) &body forms
)
287 (declare (type symbol out
))
288 `(with-open-file (,out
(merge-pathnames ,name
*base
*) :direction
:output
:if-exists
:supersede
)
289 (if (getf *reports
* :files-output
)
290 (format t
"Generating ~s~%" (truename ,out
))
292 (format ,out
";;;; This file was automatically generated by ~a~%" (load-time-value *load-truename
*))
298 (defun output-extension (category-name &optional
(function-transform #'gl-extension-function-definition
) (extension-name category-name
))
299 "write out the extension named by category name"
301 ;; collect up the elements of the extension, the enums and functions
303 (loop while
(getf *enum-specs
* category-name
) nconcing
304 (prog1 (loop for enum-name in
(getf *enum-specs
* category-name
) by
#'cddr
305 for enum-value in
(cdr (getf *enum-specs
* category-name
)) by
#'cddr
306 unless
(find enum-name
*predefined-enumerants
*)
308 (let ((constant-name (constantize enum-name
)))
309 (push constant-name
*exports
*)
310 `(defconstant ,constant-name
,enum-value
)))
311 (remf *enum-specs
* category-name
))))
313 (loop while
(getf *function-categories
* category-name
) nconcing
315 (mapcar function-transform
(getf *function-categories
* category-name
))
316 (remf *function-categories
* category-name
)))))
317 ;; only when we have either of these components, actually generate a system
318 (when (or enumerations functions
)
319 ;; write out the ASD definition
320 (with-output-file (out (format nil
"lib/cl-glfw-opengl-~a.asd" extension-name
))
321 (let* ((system-name (string-downcase (format nil
"cl-glfw-opengl-~a" extension-name
)))
322 (system-package (make-symbol (string-upcase (concatenate 'string system-name
"-system")))))
323 (print `(defpackage ,system-package
(:use
#:asdf
#:cl
)) out
)
324 (print `(in-package ,system-package
) out
)
325 (print `(defsystem ,(intern (string-upcase system-name
))
326 :description
,(format nil
"cl-glfw's ~a binding" extension-name
)
327 :author
,(format nil
"Generated by cl-glfw's ~a" (load-time-value *load-truename
*))
328 :licence
"Public Domain"
329 :depends-on
(cl-glfw-opengl
330 ,@(let ((pos (position system-name
*opengl-version-systems
* :test
'equal
)))
331 (when (and pos
(plusp pos
))
332 (list (intern (elt *opengl-version-systems
* (1- pos
)))))))
333 :components
((:file
,(concatenate 'string
"opengl-"
334 (string-downcase (symbol-name extension-name
))))))
337 ;; write the enumerations and function bindings
338 (with-output-file (out (format nil
"lib/opengl-~a.lisp" extension-name
))
339 (print '(in-package #:cl-glfw-opengl
) out
)
340 (format out
"~&~%;;;; ~a~&" category-name
)
341 (dolist (enumeration enumerations
) (print enumeration out
))
342 (dolist (function functions
) (print function out
))))))
345 (defun output-category (category-name out
)
346 "Output a whole category and remove it from the list."
347 (dolist (func-spec (getf *function-categories
* category-name
))
348 (print (gl-function-definition func-spec
) out
))
349 (remf *function-categories
* category-name
))
351 (defun output-core ()
352 ;; write the main bindings file...
353 (with-output-file (out #P
"lib/opengl.lisp")
355 (print `(in-package #:cl-glfw-opengl
) out
)
357 ;; dump all enumerations not in an extension
358 (loop for enum-group-name in
*enum-specs
* by
#'cddr
359 for enum-group in
(cdr *enum-specs
*) by
#'cddr
360 unless
(getf *function-categories
* enum-group-name
)
362 ;; when this group is not empty and there is a name that isn't already defined
363 (when (and enum-group
364 (loop for enum-name in enum-group by
#'cddr
365 when
(not (find enum-name
*predefined-enumerants
*))
367 (format out
"~&~%;;;; {{{ ~a~%" enum-group-name
)
368 (loop for enum-name in enum-group by
#'cddr
369 for enum-value in
(cdr enum-group
) by
#'cddr
370 unless
(find enum-name
*predefined-enumerants
*)
372 (push enum-name
*predefined-enumerants
*)
373 (let ((constant-name (constantize enum-name
)))
374 (push constant-name
*exports
*)
375 (print `(defconstant ,constant-name
,enum-value
) out
)))
376 (format out
"~&~%;;;; }}}~%"))
377 (remf *enum-specs
* enum-group
))
379 ;; all base 1.0 categories
380 (loop for category-name in
*base-categories
*
382 (format out
"~&~%;;;; {{{ ~a~%" category-name
)
383 (output-category category-name out
)
384 (format out
"~&~%;;;; }}}~%"))))
387 (defun output-everything ()
388 ;; some nice printing options
389 (let ((*print-case
* :downcase
)
395 (output-extension '|
1_1|
#'gl-function-definition
'VERSION_1_1
)
397 (output-extension 'VERSION_1_2
#'gl-function-definition
)
398 (output-extension 'VERSION_1_3
#'gl-function-definition
)
399 (output-extension 'VERSION_1_4
#'gl-function-definition
)
400 (output-extension 'VERSION_1_5
#'gl-function-definition
)
401 (output-extension 'VERSION_2_0
#'gl-function-definition
)
402 (output-extension 'VERSION_2_1
#'gl-function-definition
)
404 (dolist (category-name (remove-duplicates
405 (nconc (plist-keys *function-categories
*)
406 (plist-keys *enum-specs
*))))
407 (output-extension category-name
))
409 (with-output-file (out #P
"lib/opengl-type-map.lisp")
410 (print `(in-package #:cl-glfw-opengl
) out
)
411 (print `(setf *type-map
* ',*type-map
*) out
))
413 (with-output-file (out #P
"lib/opengl-package.lisp")
414 (print (make-opengl-defpackage (remove-duplicates (nreverse *exports
*))) out
)))
416 (when (and (getf *reports
* :leftover-functions
)
417 *function-categories
*)
418 (format t
"~&Leftover functions:~% ~s~%" *function-categories
*))
420 (when (and (getf *reports
* :leftover-enums
)
422 (format t
"~&Leftover enums:~% ~s~%" *enum-specs
*)))