blob: 5366f3ff7cd0fa1cd15667fb9c5dffce570f3a07 [file] [log] [blame]
Colin Cross8e0c5112015-01-23 14:15:10 -08001// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Jamie Gennis1bc967e2014-05-27 16:34:41 -070015package blueprint
16
17import (
18 "fmt"
19 "io"
20 "strings"
21 "unicode"
22)
23
24const (
Colin Cross8ac0a812015-04-14 15:38:36 -070025 indentWidth = 4
26 maxIndentDepth = 2
27 lineWidth = 80
Jamie Gennis1bc967e2014-05-27 16:34:41 -070028)
29
Colin Cross8ac0a812015-04-14 15:38:36 -070030var indentString = strings.Repeat(" ", indentWidth*maxIndentDepth)
31
Jamie Gennis1bc967e2014-05-27 16:34:41 -070032type ninjaWriter struct {
33 writer io.Writer
34
35 justDidBlankLine bool // true if the last operation was a BlankLine
36}
37
38func newNinjaWriter(writer io.Writer) *ninjaWriter {
39 return &ninjaWriter{
40 writer: writer,
41 }
42}
43
44func (n *ninjaWriter) Comment(comment string) error {
45 n.justDidBlankLine = false
46
47 const lineHeaderLen = len("# ")
48 const maxLineLen = lineWidth - lineHeaderLen
49
50 var lineStart, lastSplitPoint int
51 for i, r := range comment {
52 if unicode.IsSpace(r) {
53 // We know we can safely split the line here.
54 lastSplitPoint = i + 1
55 }
56
57 var line string
58 var writeLine bool
59 switch {
60 case r == '\n':
61 // Output the line without trimming the left so as to allow comments
62 // to contain their own indentation.
63 line = strings.TrimRightFunc(comment[lineStart:i], unicode.IsSpace)
64 writeLine = true
65
66 case (i-lineStart > maxLineLen) && (lastSplitPoint > lineStart):
67 // The line has grown too long and is splittable. Split it at the
68 // last split point.
69 line = strings.TrimSpace(comment[lineStart:lastSplitPoint])
70 writeLine = true
71 }
72
73 if writeLine {
74 line = strings.TrimSpace("# "+line) + "\n"
75 _, err := io.WriteString(n.writer, line)
76 if err != nil {
77 return err
78 }
79 lineStart = lastSplitPoint
80 }
81 }
82
83 if lineStart != len(comment) {
84 line := strings.TrimSpace(comment[lineStart:])
85 _, err := fmt.Fprintf(n.writer, "# %s\n", line)
86 if err != nil {
87 return err
88 }
89 }
90
91 return nil
92}
93
94func (n *ninjaWriter) Pool(name string) error {
95 n.justDidBlankLine = false
96 _, err := fmt.Fprintf(n.writer, "pool %s\n", name)
97 return err
98}
99
100func (n *ninjaWriter) Rule(name string) error {
101 n.justDidBlankLine = false
102 _, err := fmt.Fprintf(n.writer, "rule %s\n", name)
103 return err
104}
105
Dan Willemsen5c43e072016-10-25 21:26:12 -0700106func (n *ninjaWriter) Build(comment string, rule string, outputs, implicitOuts,
107 explicitDeps, implicitDeps, orderOnlyDeps []string) error {
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700108
109 n.justDidBlankLine = false
110
111 const lineWrapLen = len(" $")
112 const maxLineLen = lineWidth - lineWrapLen
113
Colin Cross8ac0a812015-04-14 15:38:36 -0700114 wrapper := ninjaWriterWithWrap{
115 ninjaWriter: n,
116 maxLineLen: maxLineLen,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700117 }
118
Doug Evansfcc67392015-11-08 12:21:58 -0800119 if comment != "" {
Colin Crossde7afaa2019-01-23 13:23:00 -0800120 err := wrapper.Comment(comment)
121 if err != nil {
122 return err
123 }
Doug Evansfcc67392015-11-08 12:21:58 -0800124 }
125
Colin Cross8ac0a812015-04-14 15:38:36 -0700126 wrapper.WriteString("build")
127
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700128 for _, output := range outputs {
Colin Cross8ac0a812015-04-14 15:38:36 -0700129 wrapper.WriteStringWithSpace(output)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700130 }
131
Dan Willemsen5c43e072016-10-25 21:26:12 -0700132 if len(implicitOuts) > 0 {
133 wrapper.WriteStringWithSpace("|")
134
135 for _, out := range implicitOuts {
136 wrapper.WriteStringWithSpace(out)
137 }
138 }
139
Colin Cross8ac0a812015-04-14 15:38:36 -0700140 wrapper.WriteString(":")
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700141
Colin Cross8ac0a812015-04-14 15:38:36 -0700142 wrapper.WriteStringWithSpace(rule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700143
144 for _, dep := range explicitDeps {
Colin Cross8ac0a812015-04-14 15:38:36 -0700145 wrapper.WriteStringWithSpace(dep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700146 }
147
148 if len(implicitDeps) > 0 {
Colin Cross8ac0a812015-04-14 15:38:36 -0700149 wrapper.WriteStringWithSpace("|")
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700150
151 for _, dep := range implicitDeps {
Colin Cross8ac0a812015-04-14 15:38:36 -0700152 wrapper.WriteStringWithSpace(dep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700153 }
154 }
155
156 if len(orderOnlyDeps) > 0 {
Colin Cross8ac0a812015-04-14 15:38:36 -0700157 wrapper.WriteStringWithSpace("||")
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700158
159 for _, dep := range orderOnlyDeps {
Colin Cross8ac0a812015-04-14 15:38:36 -0700160 wrapper.WriteStringWithSpace(dep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700161 }
162 }
163
Colin Cross8ac0a812015-04-14 15:38:36 -0700164 return wrapper.Flush()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700165}
166
167func (n *ninjaWriter) Assign(name, value string) error {
168 n.justDidBlankLine = false
169 _, err := fmt.Fprintf(n.writer, "%s = %s\n", name, value)
170 return err
171}
172
173func (n *ninjaWriter) ScopedAssign(name, value string) error {
174 n.justDidBlankLine = false
Colin Cross8ac0a812015-04-14 15:38:36 -0700175 _, err := fmt.Fprintf(n.writer, "%s%s = %s\n", indentString[:indentWidth], name, value)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700176 return err
177}
178
179func (n *ninjaWriter) Default(targets ...string) error {
180 n.justDidBlankLine = false
181
182 const lineWrapLen = len(" $")
183 const maxLineLen = lineWidth - lineWrapLen
184
Colin Cross8ac0a812015-04-14 15:38:36 -0700185 wrapper := ninjaWriterWithWrap{
186 ninjaWriter: n,
187 maxLineLen: maxLineLen,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700188 }
189
Colin Cross8ac0a812015-04-14 15:38:36 -0700190 wrapper.WriteString("default")
191
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700192 for _, target := range targets {
Colin Cross8ac0a812015-04-14 15:38:36 -0700193 wrapper.WriteString(" " + target)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700194 }
195
Colin Cross8ac0a812015-04-14 15:38:36 -0700196 return wrapper.Flush()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700197}
198
Dan Willemsenab223a52018-07-05 21:56:59 -0700199func (n *ninjaWriter) Subninja(file string) error {
200 n.justDidBlankLine = false
201 _, err := fmt.Fprintf(n.writer, "subninja %s\n", file)
202 return err
203}
204
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700205func (n *ninjaWriter) BlankLine() (err error) {
206 // We don't output multiple blank lines in a row.
207 if !n.justDidBlankLine {
208 n.justDidBlankLine = true
209 _, err = io.WriteString(n.writer, "\n")
210 }
211 return err
212}
213
Colin Cross8ac0a812015-04-14 15:38:36 -0700214type ninjaWriterWithWrap struct {
215 *ninjaWriter
216 maxLineLen int
217 writtenLen int
218 err error
219}
220
221func (n *ninjaWriterWithWrap) writeString(s string, space bool) {
222 if n.err != nil {
223 return
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700224 }
225
Colin Cross8ac0a812015-04-14 15:38:36 -0700226 spaceLen := 0
227 if space {
228 spaceLen = 1
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700229 }
230
Colin Cross8ac0a812015-04-14 15:38:36 -0700231 if n.writtenLen+len(s)+spaceLen > n.maxLineLen {
232 _, n.err = io.WriteString(n.writer, " $\n")
233 if n.err != nil {
234 return
235 }
236 _, n.err = io.WriteString(n.writer, indentString[:indentWidth*2])
237 if n.err != nil {
238 return
239 }
240 n.writtenLen = indentWidth * 2
241 s = strings.TrimLeftFunc(s, unicode.IsSpace)
242 } else if space {
Colin Crossde7afaa2019-01-23 13:23:00 -0800243 _, n.err = io.WriteString(n.writer, " ")
244 if n.err != nil {
245 return
246 }
Colin Cross8ac0a812015-04-14 15:38:36 -0700247 n.writtenLen++
248 }
249
250 _, n.err = io.WriteString(n.writer, s)
251 n.writtenLen += len(s)
252}
253
254func (n *ninjaWriterWithWrap) WriteString(s string) {
255 n.writeString(s, false)
256}
257
258func (n *ninjaWriterWithWrap) WriteStringWithSpace(s string) {
259 n.writeString(s, true)
260}
261
262func (n *ninjaWriterWithWrap) Flush() error {
263 if n.err != nil {
264 return n.err
265 }
266 _, err := io.WriteString(n.writer, "\n")
267 return err
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700268}