forked from Moonchild/fancy-build
117 lines
4.8 KiB
Plaintext
117 lines
4.8 KiB
Plaintext
(in-package :fancybuild)
|
|
|
|
(defun runp (cmd)
|
|
(assert (zerop (uiop:wait-process (uiop:launch-program cmd :output *standard-output* :error-output *error-output*)))))
|
|
|
|
(defun target (name target)
|
|
(setf (gethash name *targets*) target))
|
|
|
|
(defun mtime (x) (osicat-posix:stat-mtime (osicat-posix:stat x)))
|
|
; todo blake3?
|
|
(defun sha256 (fn)
|
|
(with-open-file (fp fn
|
|
:direction :input
|
|
:if-does-not-exist :error
|
|
:element-type '(unsigned-byte 8))
|
|
(let ((buf (make-array (file-length fp) :element-type '(unsigned-byte 8))))
|
|
(read-sequence buf fp)
|
|
(ironclad:digest-sequence :sha256 buf))))
|
|
|
|
(defun any (pred &rest lis)
|
|
(if (null (car lis))
|
|
nil
|
|
(or (apply pred (mapcar #'car lis))
|
|
(apply #'any (cons pred (mapcar #'cdr lis))))))
|
|
(defun all (pred &rest lis)
|
|
(if (null (car lis))
|
|
t
|
|
(and (apply pred (mapcar #'car lis))
|
|
(apply #'all (cons pred (mapcar #'cdr lis))))))
|
|
|
|
(defun freeze-file-state (fpath)
|
|
(cons (mtime fpath) (sha256 fpath)))
|
|
|
|
(defun has-file-changed (fpath state)
|
|
(and (not (= (mtime fpath) (car state)))
|
|
(not (equalp (sha256 fpath) (cdr state)))))
|
|
|
|
; Rebuild whenever any of the output files doesn't exist
|
|
; Or, whenever any of the input files has a newer mtime than any of the output files
|
|
; (that is, whenever the newest input file is newer than the oldest output file)
|
|
; Or, whenever any of the input files has been modified
|
|
(defclass rebuilt-on-change (buildable)
|
|
((input-files :initarg :input-files :initform nil)
|
|
(output-files :initarg :output-files :initform nil)))
|
|
|
|
(defmethod snapshot ((b rebuilt-on-change))
|
|
(mapcar #'freeze-file-state (slot-value b 'input-files)))
|
|
|
|
(defmethod is-dirty ((b rebuilt-on-change) old)
|
|
(or (not (any #'probe-file (slot-value b 'output-files)))
|
|
(> (reduce #'max (mapcar #'mtime (slot-value b 'input-files)))
|
|
(reduce #'min (mapcar #'mtime (slot-value b 'output-files))))
|
|
(any #'has-file-changed (slot-value b 'input-files) old)))
|
|
|
|
(defclass c-source-buildable (rebuilt-on-change)
|
|
((source-file :initarg :source-file)
|
|
(obj-file :initarg :obj-file)))
|
|
|
|
(defclass c-binary-buildable (rebuilt-on-change)
|
|
((target-binary :initarg :target-binary)
|
|
(obj-files)))
|
|
|
|
(defmethod initialize-instance :after ((b c-binary-buildable) &key)
|
|
(setf (slot-value b 'obj-files) (mapcar #'(lambda (x) (slot-value x 'obj-file)) (remove-if-not #'(lambda (x) (typep x 'c-source-buildable)) (slot-value b 'dependencies))))
|
|
(setf (slot-value b 'input-files) (slot-value b 'obj-files))
|
|
(setf (slot-value b 'output-files) (list (slot-value b 'target-binary)))
|
|
(setf (slot-value b 'pretty-name) (slot-value b 'target-binary))
|
|
(setf (slot-value b 'unique-key) (cons "c-binary-buildable" (cons (slot-value b 'target-binary) (slot-value b 'obj-files)))))
|
|
|
|
(defmethod do-build ((b c-binary-buildable))
|
|
(runp (format nil "cc -o ~a ~{~a~^ ~}" (slot-value b 'target-binary) (slot-value b 'obj-files))))
|
|
|
|
(defmethod initialize-instance :after ((b c-source-buildable) &key)
|
|
(setf (slot-value b 'input-files) (list (slot-value b 'source-file)))
|
|
(setf (slot-value b 'output-files) (list (slot-value b 'obj-file)))
|
|
(setf (slot-value b 'pretty-name) (slot-value b 'source-file))
|
|
; conspack doesn't automatically intern symbols so we have to use "c-source-buildable" instead of 'c-source-buildable
|
|
; (because uninterned symbols aren't equal to anything)
|
|
(setf (slot-value b 'unique-key) (list "c-source-buildable" (slot-value b 'obj-file) (slot-value b 'source-file))))
|
|
|
|
(defmethod do-build ((b c-source-buildable))
|
|
(ensure-directories-exist (directory-namestring (slot-value b 'obj-file)))
|
|
(runp (format nil "cc -c -o ~a ~a" (slot-value b 'obj-file) (slot-value b 'source-file))))
|
|
|
|
(defun c-source (file)
|
|
(make-instance 'c-source-buildable
|
|
:source-file file
|
|
:obj-file (format nil "build/~a.o" file)))
|
|
(defun c-sourcelist (srclist)
|
|
(mapcar #'c-source srclist))
|
|
;(c-sourcelist srclist)
|
|
|
|
(defun c-binary (bin deps)
|
|
(make-instance 'c-binary-buildable
|
|
:target-binary (format nil "build/~a" bin)
|
|
:dependencies deps))
|
|
|
|
(defparameter *t1*
|
|
(c-binary "test"
|
|
(c-sourcelist '("src/add.c" "src/test.c"))))
|
|
(defparameter *t2*
|
|
(c-binary "test2"
|
|
(c-sourcelist '("src/add.c" "src/test2.c"))))
|
|
|
|
;(defparameter x (make-instance 'c-source-buildable
|
|
; :source-file "test/test.c"
|
|
; :obj-file "build/test/test.o"))
|
|
;(defparameter y (make-instance 'c-source-buildable
|
|
; :source-file "test/add.c"
|
|
; :obj-file "build/test/add.o"))
|
|
;(defparameter z (make-instance 'c-binary-buildable
|
|
; :target-binary "build/test/test"
|
|
; :dependencies (list x y)))
|
|
|
|
(target "default" *t1*)
|
|
(target "other" *t2*)
|