| 1 | (defpackage #:pkg-config |
| 2 | (:use #:common-lisp #:clg-utils #+(or cmu clisp) #:ext #+sbcl #:sb-ext) |
| 3 | #+sbcl |
| 4 | (:import-from #:sb-int #:featurep) |
| 5 | (:export #:pkg-cflags #:pkg-libs #:pkg-exists-p #:pkg-version #:pkg-variable) |
| 6 | (:export #:featurep #:sbcl>=)) |
| 7 | |
| 8 | (in-package #:pkg-config) |
| 9 | |
| 10 | (defparameter *pkg-config* "/usr/bin/pkg-config") |
| 11 | |
| 12 | |
| 13 | #+(or sbcl cmu) |
| 14 | (defun run-pkg-config (package error-p &rest options) |
| 15 | (let ((process |
| 16 | (run-program |
| 17 | *pkg-config* (cons package options) :wait t :output :stream))) |
| 18 | (unless process |
| 19 | (error "Unable to run ~A" *pkg-config*)) |
| 20 | (let ((exit-code (process-exit-code process))) |
| 21 | (unless (or (not error-p) (zerop exit-code)) |
| 22 | (error |
| 23 | (or |
| 24 | (format nil "~A: ~{~A~%~}" *pkg-config* (read-lines (process-error process))) |
| 25 | (format nil "~A terminated with exit code ~A" |
| 26 | *pkg-config* exit-code)))) |
| 27 | (let ((output (read-lines (process-output process)))) |
| 28 | (process-close process) |
| 29 | (values output exit-code))))) |
| 30 | |
| 31 | #+clisp |
| 32 | (defun run-pkg-config (package error-p &rest options) |
| 33 | (let ((outfile (format nil "/tmp/clg-pkg-config-~A-output" (os:process-id))) |
| 34 | (errfile (format nil "/tmp/clg-pkg-config-~A-error" (os:process-id)))) |
| 35 | (unwind-protect |
| 36 | (let ((exit-code |
| 37 | (run-shell-command |
| 38 | (format nil "~A ~A ~{~A ~}2>~A" |
| 39 | *pkg-config* package |
| 40 | (mapcar #'(lambda (option) |
| 41 | (format nil "'~A'" option)) |
| 42 | options) |
| 43 | errfile) |
| 44 | :output outfile :if-output-exists :overwrite))) |
| 45 | (cond |
| 46 | ((= exit-code 127) (error "Unable to run ~A" *pkg-config*)) |
| 47 | ((and error-p (not (zerop exit-code))) |
| 48 | (with-open-file (output errfile) |
| 49 | (let ((errmsg (read-lines output))) |
| 50 | (error |
| 51 | (if (not errmsg) |
| 52 | (format nil "~A terminated with exit code ~A" *pkg-config* exit-code) |
| 53 | (format nil "~A: ~{~A~%~}" *pkg-config* errmsg)))))) |
| 54 | (t |
| 55 | (values |
| 56 | (with-open-file (output outfile) |
| 57 | (read-lines output)) |
| 58 | exit-code)))) |
| 59 | (progn |
| 60 | (delete-file outfile) |
| 61 | (delete-file errfile))))) |
| 62 | |
| 63 | |
| 64 | (defun pkg-cflags (package) |
| 65 | (split-string (first (run-pkg-config package t "--cflags")))) |
| 66 | |
| 67 | (defun pkg-libs (package) |
| 68 | (split-string (first (run-pkg-config package t "--libs")))) |
| 69 | |
| 70 | |
| 71 | (defun pkg-exists-p (package &key version atleast-version max-version error) |
| 72 | (let ((version-check |
| 73 | (cond |
| 74 | (version (format nil "= ~A" version)) |
| 75 | (atleast-version (format nil ">= ~A" atleast-version)) |
| 76 | (max-version (format nil "<= ~A" max-version)) |
| 77 | (t "")))) |
| 78 | (if error |
| 79 | (progn |
| 80 | (run-pkg-config package t "--print-errors" "--exists" version-check) |
| 81 | t) |
| 82 | (multiple-value-bind (output exit-code) |
| 83 | (run-pkg-config package nil "--exists" version-check) |
| 84 | (declare (ignore output)) |
| 85 | (zerop exit-code))))) |
| 86 | |
| 87 | |
| 88 | (defun pkg-version (package) |
| 89 | (first (run-pkg-config package t "--modversion"))) |
| 90 | |
| 91 | |
| 92 | (defun pkg-variable (package variable) |
| 93 | (first (run-pkg-config package t "--variable" variable))) |
| 94 | |
| 95 | |
| 96 | (defun |#?-reader| (stream subchar arg) |
| 97 | (declare (ignore subchar arg)) |
| 98 | (let ((not-p (when (char= (peek-char nil stream) #\-) |
| 99 | (read-char stream))) |
| 100 | (conditional (read stream t nil t))) |
| 101 | (cond |
| 102 | (*read-suppress* (read stream t nil t)) |
| 103 | ((not *read-eval*) |
| 104 | (error 'reader-error |
| 105 | :format-control "Attempt to read #? while *READ-EVAL* is bound to NIL." |
| 106 | :format-arguments nil :stream stream)) |
| 107 | ((if not-p |
| 108 | (eval conditional) |
| 109 | (not (eval conditional))) |
| 110 | (let ((*read-suppress* t)) |
| 111 | (read stream t nil t))))) |
| 112 | (values)) |
| 113 | |
| 114 | (set-dispatch-macro-character #\# #\? #'|#?-reader|) |
| 115 | |
| 116 | |
| 117 | #+sbcl |
| 118 | (progn |
| 119 | (defun sbcl-version () |
| 120 | (let* ((dot1 (position #\. (lisp-implementation-version))) |
| 121 | (dot2 (position #\. (lisp-implementation-version) :start (1+ dot1)))) |
| 122 | (values |
| 123 | (parse-integer (lisp-implementation-version) :end dot1) |
| 124 | (parse-integer (lisp-implementation-version) :start (1+ dot1) :end dot2) |
| 125 | (parse-integer (lisp-implementation-version) :start (1+ dot2) :junk-allowed t)))) |
| 126 | (defun sbcl>= (req-major req-minor req-micro) |
| 127 | (multiple-value-bind (major minor micro) (sbcl-version) |
| 128 | (or |
| 129 | (> major req-major) |
| 130 | (and (= major req-major) (> minor req-minor)) |
| 131 | (and (= major req-major) (= minor req-minor) (>= micro req-micro)))))) |
| 132 | |
| 133 | #-sbcl |
| 134 | (defun sbcl>= (req-major req-minor req-micro) |
| 135 | (declare (ignore req-major req-minor req-micro)) |
| 136 | nil) |