| 1 | (defpackage #:pkg-config |
| 2 | (:use #:common-lisp #+cmu #:ext #+sbcl #:sb-ext) |
| 3 | (:export #:pkg-cflags #:pkg-libs #:pkg-exists-p #:pkg-version |
| 4 | #:pkg-variable)) |
| 5 | |
| 6 | (in-package #:pkg-config) |
| 7 | |
| 8 | (defparameter *pkg-config* "/usr/bin/pkg-config") |
| 9 | |
| 10 | (defun split-string (string &key (start 0) (end (length string))) |
| 11 | (let ((position (position #\sp string :start start :end end))) |
| 12 | (cond |
| 13 | ((zerop (- end start)) nil) |
| 14 | ((not position) (list (subseq string start end))) |
| 15 | ((= position start) (split-string string :start (1+ start) :end end)) |
| 16 | (t (cons |
| 17 | (subseq string start position) |
| 18 | (split-string string :start (1+ position) :end end)))))) |
| 19 | |
| 20 | |
| 21 | (defun read-lines (&optional (stream *standard-input*)) |
| 22 | (let ((line (read-line stream nil))) |
| 23 | (when line |
| 24 | (cons line (read-lines stream))))) |
| 25 | |
| 26 | |
| 27 | (defun read-string (&optional (stream *standard-input*) |
| 28 | (delimiter #\newline) (eof-error-p t) eof-value) |
| 29 | (let ((string (make-array 0 :element-type 'character |
| 30 | :fill-pointer t :adjustable t))) |
| 31 | ;; I really need to learn how to use the loop facility |
| 32 | (labels ((read-chars () |
| 33 | (let ((char (read-char stream (and eof-error-p delimiter)))) |
| 34 | (when char |
| 35 | (vector-push-extend char string) |
| 36 | (unless (eq char delimiter) |
| 37 | (read-chars)))))) |
| 38 | (read-chars)) |
| 39 | (cond |
| 40 | ((not (zerop (length string))) string) |
| 41 | ((not eof-error-p) eof-value) |
| 42 | ((error 'end-of-file :stream stream))))) |
| 43 | |
| 44 | |
| 45 | (defun run-pkg-config (package error &rest options) |
| 46 | (let ((process |
| 47 | (run-program |
| 48 | *pkg-config* (cons package options) :wait t :output :stream))) |
| 49 | (unless process |
| 50 | (error "Unable to run ~A" *pkg-config*)) |
| 51 | (let ((exit-code (process-exit-code process))) |
| 52 | (unless (or (not error) (zerop exit-code)) |
| 53 | (error |
| 54 | (or |
| 55 | (read-string (process-error process) nil) |
| 56 | (format nil "~A terminated with exit code ~A" |
| 57 | *pkg-config* exit-code)))) |
| 58 | (let ((output (read-lines (process-output process)))) |
| 59 | (process-close process) |
| 60 | (values output exit-code))))) |
| 61 | |
| 62 | |
| 63 | (defun pkg-cflags (package) |
| 64 | (split-string (first (run-pkg-config package t "--cflags")))) |
| 65 | |
| 66 | (defun pkg-libs (package) |
| 67 | (split-string (first (run-pkg-config package t "--libs")))) |
| 68 | |
| 69 | |
| 70 | (defun pkg-exists-p (package &key version atleast-version max-version |
| 71 | ( error t)) |
| 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))) |