91
|
1
|
;;; makefile.lisp --- GNU Makefile compiler |
|
2
|
|
|
3
|
;; GNU Makefile compiler. |
|
4
|
|
|
5
|
;;; Commentary: |
|
6
|
|
|
7
|
;; Makefiles are our 'portable' build medium. We can parse them using |
|
8
|
;; the same general strategy as GNU make and compile them from |
|
9
|
;; skelfiles (rule, source, target, command). |
|
10
|
|
|
11
|
;; HACK 2023-09-15: MVP |
|
12
|
|
|
13
|
;; SO, the absolute priority ATM is to transpile our `sk-rule' objects |
|
14
|
;; into a working Makefile. We're ignoring most of the niceties like |
|
15
|
;; line-splitting and any JIT or compile-time execution. |
|
16
|
|
|
17
|
;; https://github.com/takagi/lake |
|
18
|
|
|
19
|
;; https://www.gnu.org/software/make/manual/html_node/Parsing-Makefiles.html |
|
20
|
|
|
21
|
;;; Code: |
|
22
|
(in-package :skel/comp) |
|
23
|
|
|
24
|
(defparameter *default-makefile* "makefile") |
|
25
|
(defparameter *makefile-extension* "mk") |
|
26
|
|
|
27
|
;; TODO 2023-09-27: what is $(@D) ?? (target-dir) |
|
28
|
(defvar *mk-magic-vars* #(#\@ #\< #\^ #\* #\+ #\? #\|)) |
|
29
|
|
|
30
|
(defvar *mk-command-prefixes* #(#\@ #\- #\+)) |
|
31
|
|
|
32
|
(deftype mk-val-designator () '(member nil :simple :immediate :conditional :recursive :once :append :shell)) |
|
33
|
|
96
|
34
|
(defstruct mk-val "" (kind nil :type mk-val-designator) (val nil :type sxp:form)) |
91
|
35
|
|
|
36
|
(defstruct mk-var "" |
|
37
|
(key "" :type string) |
|
38
|
(val (make-mk-val) :type mk-val)) |
|
39
|
|
|
40
|
;; https://www.gnu.org/software/make/manual/html_node/Makefile-Contents.html |
|
41
|
(defclass makefile (skel sk-meta) |
|
42
|
((directives :initform (make-array 0 :element-type 'sk-command :adjustable t :fill-pointer 0) |
|
43
|
:type (vector sk-command) :accessor mk-directives) |
|
44
|
(variables :initform (make-hash-table) |
|
45
|
:type (hash-table) :accessor mk-vars) |
|
46
|
(explicit :initform (make-array 0 :element-type 'sk-rule :adjustable t :fill-pointer 0) |
|
47
|
:type (vector sk-rule) :accessor mk-erules) |
|
48
|
(implicit :initform (make-array 0 :element-type 'sk-rule :adjustable t :fill-pointer 0) |
|
49
|
:type (vector sk-rule) :accessor mk-irules)) |
|
50
|
(:documentation "A virtual GNU Makefile.")) |
|
51
|
|
|
52
|
(defmethod push-rule ((self sk-rule) (place makefile) &optional implicit) |
|
53
|
(if implicit |
|
54
|
(vector-push-extend self (mk-irules place)) |
|
55
|
(vector-push-extend self (mk-erules place)))) |
|
56
|
|
|
57
|
(defmethod push-directive ((self sk-command) (place makefile)) |
|
58
|
(vector-push-extend self (mk-directives place))) |
|
59
|
|
|
60
|
(defmethod push-var ((self cons) (place makefile)) |
|
61
|
(destructuring-bind (k v) self |
|
62
|
(setf (gethash k (mk-vars place)) v))) |
|
63
|
|
|
64
|
(defmethod sk-compile ((self makefile) stream &key &allow-other-keys) |
|
65
|
"Compile the makefile SELF to output STREAM." |
|
66
|
(with-open-stream (s stream) |
|
67
|
(with-slots (directives variables explicit implicit) self |
|
68
|
;; directives |
|
69
|
(loop for d across directives |
|
70
|
do (sk-writeln d s)) |
|
71
|
;; variables |
|
72
|
(maphash (lambda (x y) (format s "~A=~A~%" x y)) variables) |
|
73
|
;; explicit rules |
|
74
|
(loop for exp across explicit |
|
75
|
do (format s "~A:~A;~A~%" |
|
76
|
(sk-write-string (sk-rule-target exp)) |
|
77
|
(sk-write-string (sk-rule-source exp)) |
|
78
|
(sk-write-string (sk-rule-recipe exp)))) |
|
79
|
;; TODO implicit rules |
|
80
|
(loop for imp across implicit |
|
81
|
do (format s "~A:~A;~A~%" |
|
82
|
(sk-write-string (sk-rule-target imp)) |
|
83
|
(sk-write-string (sk-rule-source imp)) |
|
84
|
(sk-write-string (sk-rule-recipe imp))))))) |
|
85
|
|
|
86
|
(defmethod sk-write-file ((self makefile) &key (path *default-makefile*) (comment t) (if-exists :overwrite)) |
|
87
|
(with-open-file (out path |
|
88
|
:direction :output |
|
89
|
:if-exists if-exists |
|
90
|
:if-does-not-exist :create) |
|
91
|
(when comment (princ |
|
92
|
(make-source-header-comment |
|
93
|
(sk-name self) |
|
94
|
:cchar #\# |
|
95
|
:timestamp t |
|
96
|
:description (sk-description self) |
|
97
|
:opts '("mode: makefile-gmake;")) |
|
98
|
out)) |
|
99
|
(sk-compile self out))) |
|
100
|
|
162
|
101
|
(defmethod sk-read-file ((self makefile) path) |
163
|
102
|
(with-open-file (in path :direction :input))) |