blob: 883228d4b741b68ae39c4c4246f7a1ac648650d3 [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
106func (n *ninjaWriter) Build(rule string, outputs, explicitDeps, implicitDeps,
107 orderOnlyDeps []string) error {
108
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
Colin Cross8ac0a812015-04-14 15:38:36 -0700119 wrapper.WriteString("build")
120
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700121 for _, output := range outputs {
Colin Cross8ac0a812015-04-14 15:38:36 -0700122 wrapper.WriteStringWithSpace(output)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700123 }
124
Colin Cross8ac0a812015-04-14 15:38:36 -0700125 wrapper.WriteString(":")
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700126
Colin Cross8ac0a812015-04-14 15:38:36 -0700127 wrapper.WriteStringWithSpace(rule)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700128
129 for _, dep := range explicitDeps {
Colin Cross8ac0a812015-04-14 15:38:36 -0700130 wrapper.WriteStringWithSpace(dep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700131 }
132
133 if len(implicitDeps) > 0 {
Colin Cross8ac0a812015-04-14 15:38:36 -0700134 wrapper.WriteStringWithSpace("|")
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700135
136 for _, dep := range implicitDeps {
Colin Cross8ac0a812015-04-14 15:38:36 -0700137 wrapper.WriteStringWithSpace(dep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700138 }
139 }
140
141 if len(orderOnlyDeps) > 0 {
Colin Cross8ac0a812015-04-14 15:38:36 -0700142 wrapper.WriteStringWithSpace("||")
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700143
144 for _, dep := range orderOnlyDeps {
Colin Cross8ac0a812015-04-14 15:38:36 -0700145 wrapper.WriteStringWithSpace(dep)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700146 }
147 }
148
Colin Cross8ac0a812015-04-14 15:38:36 -0700149 return wrapper.Flush()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700150}
151
152func (n *ninjaWriter) Assign(name, value string) error {
153 n.justDidBlankLine = false
154 _, err := fmt.Fprintf(n.writer, "%s = %s\n", name, value)
155 return err
156}
157
158func (n *ninjaWriter) ScopedAssign(name, value string) error {
159 n.justDidBlankLine = false
Colin Cross8ac0a812015-04-14 15:38:36 -0700160 _, err := fmt.Fprintf(n.writer, "%s%s = %s\n", indentString[:indentWidth], name, value)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700161 return err
162}
163
164func (n *ninjaWriter) Default(targets ...string) error {
165 n.justDidBlankLine = false
166
167 const lineWrapLen = len(" $")
168 const maxLineLen = lineWidth - lineWrapLen
169
Colin Cross8ac0a812015-04-14 15:38:36 -0700170 wrapper := ninjaWriterWithWrap{
171 ninjaWriter: n,
172 maxLineLen: maxLineLen,
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700173 }
174
Colin Cross8ac0a812015-04-14 15:38:36 -0700175 wrapper.WriteString("default")
176
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700177 for _, target := range targets {
Colin Cross8ac0a812015-04-14 15:38:36 -0700178 wrapper.WriteString(" " + target)
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700179 }
180
Colin Cross8ac0a812015-04-14 15:38:36 -0700181 return wrapper.Flush()
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700182}
183
184func (n *ninjaWriter) BlankLine() (err error) {
185 // We don't output multiple blank lines in a row.
186 if !n.justDidBlankLine {
187 n.justDidBlankLine = true
188 _, err = io.WriteString(n.writer, "\n")
189 }
190 return err
191}
192
Colin Cross8ac0a812015-04-14 15:38:36 -0700193type ninjaWriterWithWrap struct {
194 *ninjaWriter
195 maxLineLen int
196 writtenLen int
197 err error
198}
199
200func (n *ninjaWriterWithWrap) writeString(s string, space bool) {
201 if n.err != nil {
202 return
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700203 }
204
Colin Cross8ac0a812015-04-14 15:38:36 -0700205 spaceLen := 0
206 if space {
207 spaceLen = 1
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700208 }
209
Colin Cross8ac0a812015-04-14 15:38:36 -0700210 if n.writtenLen+len(s)+spaceLen > n.maxLineLen {
211 _, n.err = io.WriteString(n.writer, " $\n")
212 if n.err != nil {
213 return
214 }
215 _, n.err = io.WriteString(n.writer, indentString[:indentWidth*2])
216 if n.err != nil {
217 return
218 }
219 n.writtenLen = indentWidth * 2
220 s = strings.TrimLeftFunc(s, unicode.IsSpace)
221 } else if space {
222 io.WriteString(n.writer, " ")
223 n.writtenLen++
224 }
225
226 _, n.err = io.WriteString(n.writer, s)
227 n.writtenLen += len(s)
228}
229
230func (n *ninjaWriterWithWrap) WriteString(s string) {
231 n.writeString(s, false)
232}
233
234func (n *ninjaWriterWithWrap) WriteStringWithSpace(s string) {
235 n.writeString(s, true)
236}
237
238func (n *ninjaWriterWithWrap) Flush() error {
239 if n.err != nil {
240 return n.err
241 }
242 _, err := io.WriteString(n.writer, "\n")
243 return err
Jamie Gennis1bc967e2014-05-27 16:34:41 -0700244}