blob: a61667da8936d3387507596e58473ddc8087dff3 [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 != "" {
120 wrapper.Comment(comment)
121 }
122
Colin Cross8ac0a812015-04-14 15:38:36 -0700123 wrapper.WriteString("build")
124
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700125 for _, output := range outputs {
Colin Cross8ac0a812015-04-14 15:38:36 -0700126 wrapper.WriteStringWithSpace(output)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700127 }
128
Dan Willemsen5c43e072016-10-25 21:26:12 -0700129 if len(implicitOuts) > 0 {
130 wrapper.WriteStringWithSpace("|")
131
132 for _, out := range implicitOuts {
133 wrapper.WriteStringWithSpace(out)
134 }
135 }
136
Colin Cross8ac0a812015-04-14 15:38:36 -0700137 wrapper.WriteString(":")
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700138
Colin Cross8ac0a812015-04-14 15:38:36 -0700139 wrapper.WriteStringWithSpace(rule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700140
141 for _, dep := range explicitDeps {
Colin Cross8ac0a812015-04-14 15:38:36 -0700142 wrapper.WriteStringWithSpace(dep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700143 }
144
145 if len(implicitDeps) > 0 {
Colin Cross8ac0a812015-04-14 15:38:36 -0700146 wrapper.WriteStringWithSpace("|")
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700147
148 for _, dep := range implicitDeps {
Colin Cross8ac0a812015-04-14 15:38:36 -0700149 wrapper.WriteStringWithSpace(dep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700150 }
151 }
152
153 if len(orderOnlyDeps) > 0 {
Colin Cross8ac0a812015-04-14 15:38:36 -0700154 wrapper.WriteStringWithSpace("||")
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700155
156 for _, dep := range orderOnlyDeps {
Colin Cross8ac0a812015-04-14 15:38:36 -0700157 wrapper.WriteStringWithSpace(dep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700158 }
159 }
160
Colin Cross8ac0a812015-04-14 15:38:36 -0700161 return wrapper.Flush()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700162}
163
164func (n *ninjaWriter) Assign(name, value string) error {
165 n.justDidBlankLine = false
166 _, err := fmt.Fprintf(n.writer, "%s = %s\n", name, value)
167 return err
168}
169
170func (n *ninjaWriter) ScopedAssign(name, value string) error {
171 n.justDidBlankLine = false
Colin Cross8ac0a812015-04-14 15:38:36 -0700172 _, err := fmt.Fprintf(n.writer, "%s%s = %s\n", indentString[:indentWidth], name, value)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700173 return err
174}
175
176func (n *ninjaWriter) Default(targets ...string) error {
177 n.justDidBlankLine = false
178
179 const lineWrapLen = len(" $")
180 const maxLineLen = lineWidth - lineWrapLen
181
Colin Cross8ac0a812015-04-14 15:38:36 -0700182 wrapper := ninjaWriterWithWrap{
183 ninjaWriter: n,
184 maxLineLen: maxLineLen,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700185 }
186
Colin Cross8ac0a812015-04-14 15:38:36 -0700187 wrapper.WriteString("default")
188
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700189 for _, target := range targets {
Colin Cross8ac0a812015-04-14 15:38:36 -0700190 wrapper.WriteString(" " + target)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700191 }
192
Colin Cross8ac0a812015-04-14 15:38:36 -0700193 return wrapper.Flush()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700194}
195
196func (n *ninjaWriter) BlankLine() (err error) {
197 // We don't output multiple blank lines in a row.
198 if !n.justDidBlankLine {
199 n.justDidBlankLine = true
200 _, err = io.WriteString(n.writer, "\n")
201 }
202 return err
203}
204
Colin Cross8ac0a812015-04-14 15:38:36 -0700205type ninjaWriterWithWrap struct {
206 *ninjaWriter
207 maxLineLen int
208 writtenLen int
209 err error
210}
211
212func (n *ninjaWriterWithWrap) writeString(s string, space bool) {
213 if n.err != nil {
214 return
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700215 }
216
Colin Cross8ac0a812015-04-14 15:38:36 -0700217 spaceLen := 0
218 if space {
219 spaceLen = 1
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700220 }
221
Colin Cross8ac0a812015-04-14 15:38:36 -0700222 if n.writtenLen+len(s)+spaceLen > n.maxLineLen {
223 _, n.err = io.WriteString(n.writer, " $\n")
224 if n.err != nil {
225 return
226 }
227 _, n.err = io.WriteString(n.writer, indentString[:indentWidth*2])
228 if n.err != nil {
229 return
230 }
231 n.writtenLen = indentWidth * 2
232 s = strings.TrimLeftFunc(s, unicode.IsSpace)
233 } else if space {
234 io.WriteString(n.writer, " ")
235 n.writtenLen++
236 }
237
238 _, n.err = io.WriteString(n.writer, s)
239 n.writtenLen += len(s)
240}
241
242func (n *ninjaWriterWithWrap) WriteString(s string) {
243 n.writeString(s, false)
244}
245
246func (n *ninjaWriterWithWrap) WriteStringWithSpace(s string) {
247 n.writeString(s, true)
248}
249
250func (n *ninjaWriterWithWrap) Flush() error {
251 if n.err != nil {
252 return n.err
253 }
254 _, err := io.WriteString(n.writer, "\n")
255 return err
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700256}