1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2014 Google Inc. All rights reserved. 2*1fa6dee9SAndroid Build Coastguard Worker// 3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*1fa6dee9SAndroid Build Coastguard Worker// 7*1fa6dee9SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*1fa6dee9SAndroid Build Coastguard Worker// 9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License. 14*1fa6dee9SAndroid Build Coastguard Worker 15*1fa6dee9SAndroid Build Coastguard Workerpackage blueprint 16*1fa6dee9SAndroid Build Coastguard Worker 17*1fa6dee9SAndroid Build Coastguard Workerimport ( 18*1fa6dee9SAndroid Build Coastguard Worker "io" 19*1fa6dee9SAndroid Build Coastguard Worker "strings" 20*1fa6dee9SAndroid Build Coastguard Worker "unicode" 21*1fa6dee9SAndroid Build Coastguard Worker) 22*1fa6dee9SAndroid Build Coastguard Worker 23*1fa6dee9SAndroid Build Coastguard Workerconst ( 24*1fa6dee9SAndroid Build Coastguard Worker indentWidth = 4 25*1fa6dee9SAndroid Build Coastguard Worker maxIndentDepth = 2 26*1fa6dee9SAndroid Build Coastguard Worker lineWidth = 80 27*1fa6dee9SAndroid Build Coastguard Worker) 28*1fa6dee9SAndroid Build Coastguard Worker 29*1fa6dee9SAndroid Build Coastguard Workervar indentString = strings.Repeat(" ", indentWidth*maxIndentDepth) 30*1fa6dee9SAndroid Build Coastguard Worker 31*1fa6dee9SAndroid Build Coastguard Workertype StringWriterWriter interface { 32*1fa6dee9SAndroid Build Coastguard Worker io.StringWriter 33*1fa6dee9SAndroid Build Coastguard Worker io.Writer 34*1fa6dee9SAndroid Build Coastguard Worker} 35*1fa6dee9SAndroid Build Coastguard Worker 36*1fa6dee9SAndroid Build Coastguard Workertype ninjaWriter struct { 37*1fa6dee9SAndroid Build Coastguard Worker writer StringWriterWriter 38*1fa6dee9SAndroid Build Coastguard Worker 39*1fa6dee9SAndroid Build Coastguard Worker justDidBlankLine bool // true if the last operation was a BlankLine 40*1fa6dee9SAndroid Build Coastguard Worker} 41*1fa6dee9SAndroid Build Coastguard Worker 42*1fa6dee9SAndroid Build Coastguard Workerfunc newNinjaWriter(writer StringWriterWriter) *ninjaWriter { 43*1fa6dee9SAndroid Build Coastguard Worker return &ninjaWriter{ 44*1fa6dee9SAndroid Build Coastguard Worker writer: writer, 45*1fa6dee9SAndroid Build Coastguard Worker } 46*1fa6dee9SAndroid Build Coastguard Worker} 47*1fa6dee9SAndroid Build Coastguard Worker 48*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriter) Comment(comment string) error { 49*1fa6dee9SAndroid Build Coastguard Worker n.justDidBlankLine = false 50*1fa6dee9SAndroid Build Coastguard Worker 51*1fa6dee9SAndroid Build Coastguard Worker const lineHeaderLen = len("# ") 52*1fa6dee9SAndroid Build Coastguard Worker const maxLineLen = lineWidth - lineHeaderLen 53*1fa6dee9SAndroid Build Coastguard Worker 54*1fa6dee9SAndroid Build Coastguard Worker var lineStart, lastSplitPoint int 55*1fa6dee9SAndroid Build Coastguard Worker for i, r := range comment { 56*1fa6dee9SAndroid Build Coastguard Worker if unicode.IsSpace(r) { 57*1fa6dee9SAndroid Build Coastguard Worker // We know we can safely split the line here. 58*1fa6dee9SAndroid Build Coastguard Worker lastSplitPoint = i + 1 59*1fa6dee9SAndroid Build Coastguard Worker } 60*1fa6dee9SAndroid Build Coastguard Worker 61*1fa6dee9SAndroid Build Coastguard Worker var line string 62*1fa6dee9SAndroid Build Coastguard Worker var writeLine bool 63*1fa6dee9SAndroid Build Coastguard Worker switch { 64*1fa6dee9SAndroid Build Coastguard Worker case r == '\n': 65*1fa6dee9SAndroid Build Coastguard Worker // Output the line without trimming the left so as to allow comments 66*1fa6dee9SAndroid Build Coastguard Worker // to contain their own indentation. 67*1fa6dee9SAndroid Build Coastguard Worker line = strings.TrimRightFunc(comment[lineStart:i], unicode.IsSpace) 68*1fa6dee9SAndroid Build Coastguard Worker writeLine = true 69*1fa6dee9SAndroid Build Coastguard Worker 70*1fa6dee9SAndroid Build Coastguard Worker case (i-lineStart > maxLineLen) && (lastSplitPoint > lineStart): 71*1fa6dee9SAndroid Build Coastguard Worker // The line has grown too long and is splittable. Split it at the 72*1fa6dee9SAndroid Build Coastguard Worker // last split point. 73*1fa6dee9SAndroid Build Coastguard Worker line = strings.TrimSpace(comment[lineStart:lastSplitPoint]) 74*1fa6dee9SAndroid Build Coastguard Worker writeLine = true 75*1fa6dee9SAndroid Build Coastguard Worker } 76*1fa6dee9SAndroid Build Coastguard Worker 77*1fa6dee9SAndroid Build Coastguard Worker if writeLine { 78*1fa6dee9SAndroid Build Coastguard Worker line = strings.TrimSpace("# "+line) + "\n" 79*1fa6dee9SAndroid Build Coastguard Worker _, err := n.writer.WriteString(line) 80*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 81*1fa6dee9SAndroid Build Coastguard Worker return err 82*1fa6dee9SAndroid Build Coastguard Worker } 83*1fa6dee9SAndroid Build Coastguard Worker lineStart = lastSplitPoint 84*1fa6dee9SAndroid Build Coastguard Worker } 85*1fa6dee9SAndroid Build Coastguard Worker } 86*1fa6dee9SAndroid Build Coastguard Worker 87*1fa6dee9SAndroid Build Coastguard Worker if lineStart != len(comment) { 88*1fa6dee9SAndroid Build Coastguard Worker line := strings.TrimSpace(comment[lineStart:]) 89*1fa6dee9SAndroid Build Coastguard Worker _, err := n.writer.WriteString("# ") 90*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 91*1fa6dee9SAndroid Build Coastguard Worker return err 92*1fa6dee9SAndroid Build Coastguard Worker } 93*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString(line) 94*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 95*1fa6dee9SAndroid Build Coastguard Worker return err 96*1fa6dee9SAndroid Build Coastguard Worker } 97*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString("\n") 98*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 99*1fa6dee9SAndroid Build Coastguard Worker return err 100*1fa6dee9SAndroid Build Coastguard Worker } 101*1fa6dee9SAndroid Build Coastguard Worker } 102*1fa6dee9SAndroid Build Coastguard Worker 103*1fa6dee9SAndroid Build Coastguard Worker return nil 104*1fa6dee9SAndroid Build Coastguard Worker} 105*1fa6dee9SAndroid Build Coastguard Worker 106*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriter) Pool(name string) error { 107*1fa6dee9SAndroid Build Coastguard Worker n.justDidBlankLine = false 108*1fa6dee9SAndroid Build Coastguard Worker return n.writeStatement("pool", name) 109*1fa6dee9SAndroid Build Coastguard Worker} 110*1fa6dee9SAndroid Build Coastguard Worker 111*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriter) Rule(name string) error { 112*1fa6dee9SAndroid Build Coastguard Worker n.justDidBlankLine = false 113*1fa6dee9SAndroid Build Coastguard Worker return n.writeStatement("rule", name) 114*1fa6dee9SAndroid Build Coastguard Worker} 115*1fa6dee9SAndroid Build Coastguard Worker 116*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriter) Build(comment string, rule string, outputs, implicitOuts, 117*1fa6dee9SAndroid Build Coastguard Worker explicitDeps, implicitDeps, orderOnlyDeps, validations []*ninjaString, 118*1fa6dee9SAndroid Build Coastguard Worker outputStrings, implicitOutStrings, explicitDepStrings, 119*1fa6dee9SAndroid Build Coastguard Worker implicitDepStrings, orderOnlyDepStrings, validationStrings []string, 120*1fa6dee9SAndroid Build Coastguard Worker nameTracker *nameTracker) error { 121*1fa6dee9SAndroid Build Coastguard Worker 122*1fa6dee9SAndroid Build Coastguard Worker n.justDidBlankLine = false 123*1fa6dee9SAndroid Build Coastguard Worker 124*1fa6dee9SAndroid Build Coastguard Worker const lineWrapLen = len(" $") 125*1fa6dee9SAndroid Build Coastguard Worker const maxLineLen = lineWidth - lineWrapLen 126*1fa6dee9SAndroid Build Coastguard Worker 127*1fa6dee9SAndroid Build Coastguard Worker wrapper := &ninjaWriterWithWrap{ 128*1fa6dee9SAndroid Build Coastguard Worker ninjaWriter: n, 129*1fa6dee9SAndroid Build Coastguard Worker maxLineLen: maxLineLen, 130*1fa6dee9SAndroid Build Coastguard Worker } 131*1fa6dee9SAndroid Build Coastguard Worker 132*1fa6dee9SAndroid Build Coastguard Worker if comment != "" { 133*1fa6dee9SAndroid Build Coastguard Worker err := wrapper.Comment(comment) 134*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 135*1fa6dee9SAndroid Build Coastguard Worker return err 136*1fa6dee9SAndroid Build Coastguard Worker } 137*1fa6dee9SAndroid Build Coastguard Worker } 138*1fa6dee9SAndroid Build Coastguard Worker 139*1fa6dee9SAndroid Build Coastguard Worker wrapper.WriteString("build") 140*1fa6dee9SAndroid Build Coastguard Worker 141*1fa6dee9SAndroid Build Coastguard Worker for _, output := range outputStrings { 142*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 143*1fa6dee9SAndroid Build Coastguard Worker outputEscaper.WriteString(wrapper, output) 144*1fa6dee9SAndroid Build Coastguard Worker } 145*1fa6dee9SAndroid Build Coastguard Worker for _, output := range outputs { 146*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 147*1fa6dee9SAndroid Build Coastguard Worker output.ValueWithEscaper(wrapper, nameTracker, outputEscaper) 148*1fa6dee9SAndroid Build Coastguard Worker } 149*1fa6dee9SAndroid Build Coastguard Worker 150*1fa6dee9SAndroid Build Coastguard Worker if len(implicitOuts) > 0 || len(implicitOutStrings) > 0 { 151*1fa6dee9SAndroid Build Coastguard Worker wrapper.WriteStringWithSpace("|") 152*1fa6dee9SAndroid Build Coastguard Worker 153*1fa6dee9SAndroid Build Coastguard Worker for _, out := range implicitOutStrings { 154*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 155*1fa6dee9SAndroid Build Coastguard Worker outputEscaper.WriteString(wrapper, out) 156*1fa6dee9SAndroid Build Coastguard Worker } 157*1fa6dee9SAndroid Build Coastguard Worker for _, out := range implicitOuts { 158*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 159*1fa6dee9SAndroid Build Coastguard Worker out.ValueWithEscaper(wrapper, nameTracker, outputEscaper) 160*1fa6dee9SAndroid Build Coastguard Worker } 161*1fa6dee9SAndroid Build Coastguard Worker } 162*1fa6dee9SAndroid Build Coastguard Worker 163*1fa6dee9SAndroid Build Coastguard Worker wrapper.WriteString(":") 164*1fa6dee9SAndroid Build Coastguard Worker 165*1fa6dee9SAndroid Build Coastguard Worker wrapper.WriteStringWithSpace(rule) 166*1fa6dee9SAndroid Build Coastguard Worker 167*1fa6dee9SAndroid Build Coastguard Worker for _, dep := range explicitDepStrings { 168*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 169*1fa6dee9SAndroid Build Coastguard Worker inputEscaper.WriteString(wrapper, dep) 170*1fa6dee9SAndroid Build Coastguard Worker } 171*1fa6dee9SAndroid Build Coastguard Worker for _, dep := range explicitDeps { 172*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 173*1fa6dee9SAndroid Build Coastguard Worker dep.ValueWithEscaper(wrapper, nameTracker, inputEscaper) 174*1fa6dee9SAndroid Build Coastguard Worker } 175*1fa6dee9SAndroid Build Coastguard Worker 176*1fa6dee9SAndroid Build Coastguard Worker if len(implicitDeps) > 0 || len(implicitDepStrings) > 0 { 177*1fa6dee9SAndroid Build Coastguard Worker wrapper.WriteStringWithSpace("|") 178*1fa6dee9SAndroid Build Coastguard Worker 179*1fa6dee9SAndroid Build Coastguard Worker for _, dep := range implicitDepStrings { 180*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 181*1fa6dee9SAndroid Build Coastguard Worker inputEscaper.WriteString(wrapper, dep) 182*1fa6dee9SAndroid Build Coastguard Worker } 183*1fa6dee9SAndroid Build Coastguard Worker for _, dep := range implicitDeps { 184*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 185*1fa6dee9SAndroid Build Coastguard Worker dep.ValueWithEscaper(wrapper, nameTracker, inputEscaper) 186*1fa6dee9SAndroid Build Coastguard Worker } 187*1fa6dee9SAndroid Build Coastguard Worker } 188*1fa6dee9SAndroid Build Coastguard Worker 189*1fa6dee9SAndroid Build Coastguard Worker if len(orderOnlyDeps) > 0 || len(orderOnlyDepStrings) > 0 { 190*1fa6dee9SAndroid Build Coastguard Worker wrapper.WriteStringWithSpace("||") 191*1fa6dee9SAndroid Build Coastguard Worker 192*1fa6dee9SAndroid Build Coastguard Worker for _, dep := range orderOnlyDepStrings { 193*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 194*1fa6dee9SAndroid Build Coastguard Worker inputEscaper.WriteString(wrapper, dep) 195*1fa6dee9SAndroid Build Coastguard Worker } 196*1fa6dee9SAndroid Build Coastguard Worker for _, dep := range orderOnlyDeps { 197*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 198*1fa6dee9SAndroid Build Coastguard Worker dep.ValueWithEscaper(wrapper, nameTracker, inputEscaper) 199*1fa6dee9SAndroid Build Coastguard Worker } 200*1fa6dee9SAndroid Build Coastguard Worker } 201*1fa6dee9SAndroid Build Coastguard Worker 202*1fa6dee9SAndroid Build Coastguard Worker if len(validations) > 0 || len(validationStrings) > 0 { 203*1fa6dee9SAndroid Build Coastguard Worker wrapper.WriteStringWithSpace("|@") 204*1fa6dee9SAndroid Build Coastguard Worker 205*1fa6dee9SAndroid Build Coastguard Worker for _, dep := range validationStrings { 206*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 207*1fa6dee9SAndroid Build Coastguard Worker inputEscaper.WriteString(wrapper, dep) 208*1fa6dee9SAndroid Build Coastguard Worker } 209*1fa6dee9SAndroid Build Coastguard Worker for _, dep := range validations { 210*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 211*1fa6dee9SAndroid Build Coastguard Worker dep.ValueWithEscaper(wrapper, nameTracker, inputEscaper) 212*1fa6dee9SAndroid Build Coastguard Worker } 213*1fa6dee9SAndroid Build Coastguard Worker } 214*1fa6dee9SAndroid Build Coastguard Worker 215*1fa6dee9SAndroid Build Coastguard Worker return wrapper.Flush() 216*1fa6dee9SAndroid Build Coastguard Worker} 217*1fa6dee9SAndroid Build Coastguard Worker 218*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriter) Assign(name, value string) error { 219*1fa6dee9SAndroid Build Coastguard Worker n.justDidBlankLine = false 220*1fa6dee9SAndroid Build Coastguard Worker _, err := n.writer.WriteString(name) 221*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 222*1fa6dee9SAndroid Build Coastguard Worker return err 223*1fa6dee9SAndroid Build Coastguard Worker } 224*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString(" = ") 225*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 226*1fa6dee9SAndroid Build Coastguard Worker return err 227*1fa6dee9SAndroid Build Coastguard Worker } 228*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString(value) 229*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 230*1fa6dee9SAndroid Build Coastguard Worker return err 231*1fa6dee9SAndroid Build Coastguard Worker } 232*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString("\n") 233*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 234*1fa6dee9SAndroid Build Coastguard Worker return err 235*1fa6dee9SAndroid Build Coastguard Worker } 236*1fa6dee9SAndroid Build Coastguard Worker return nil 237*1fa6dee9SAndroid Build Coastguard Worker} 238*1fa6dee9SAndroid Build Coastguard Worker 239*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriter) ScopedAssign(name, value string) error { 240*1fa6dee9SAndroid Build Coastguard Worker n.justDidBlankLine = false 241*1fa6dee9SAndroid Build Coastguard Worker _, err := n.writer.WriteString(indentString[:indentWidth]) 242*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 243*1fa6dee9SAndroid Build Coastguard Worker return err 244*1fa6dee9SAndroid Build Coastguard Worker } 245*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString(name) 246*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 247*1fa6dee9SAndroid Build Coastguard Worker return err 248*1fa6dee9SAndroid Build Coastguard Worker } 249*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString(" = ") 250*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 251*1fa6dee9SAndroid Build Coastguard Worker return err 252*1fa6dee9SAndroid Build Coastguard Worker } 253*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString(value) 254*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 255*1fa6dee9SAndroid Build Coastguard Worker return err 256*1fa6dee9SAndroid Build Coastguard Worker } 257*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString("\n") 258*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 259*1fa6dee9SAndroid Build Coastguard Worker return err 260*1fa6dee9SAndroid Build Coastguard Worker } 261*1fa6dee9SAndroid Build Coastguard Worker return nil 262*1fa6dee9SAndroid Build Coastguard Worker} 263*1fa6dee9SAndroid Build Coastguard Worker 264*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriter) Default(nameTracker *nameTracker, targets []*ninjaString, targetStrings []string) error { 265*1fa6dee9SAndroid Build Coastguard Worker n.justDidBlankLine = false 266*1fa6dee9SAndroid Build Coastguard Worker 267*1fa6dee9SAndroid Build Coastguard Worker const lineWrapLen = len(" $") 268*1fa6dee9SAndroid Build Coastguard Worker const maxLineLen = lineWidth - lineWrapLen 269*1fa6dee9SAndroid Build Coastguard Worker 270*1fa6dee9SAndroid Build Coastguard Worker wrapper := &ninjaWriterWithWrap{ 271*1fa6dee9SAndroid Build Coastguard Worker ninjaWriter: n, 272*1fa6dee9SAndroid Build Coastguard Worker maxLineLen: maxLineLen, 273*1fa6dee9SAndroid Build Coastguard Worker } 274*1fa6dee9SAndroid Build Coastguard Worker 275*1fa6dee9SAndroid Build Coastguard Worker wrapper.WriteString("default") 276*1fa6dee9SAndroid Build Coastguard Worker 277*1fa6dee9SAndroid Build Coastguard Worker for _, target := range targetStrings { 278*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 279*1fa6dee9SAndroid Build Coastguard Worker outputEscaper.WriteString(wrapper, target) 280*1fa6dee9SAndroid Build Coastguard Worker } 281*1fa6dee9SAndroid Build Coastguard Worker for _, target := range targets { 282*1fa6dee9SAndroid Build Coastguard Worker wrapper.Space() 283*1fa6dee9SAndroid Build Coastguard Worker target.ValueWithEscaper(wrapper, nameTracker, outputEscaper) 284*1fa6dee9SAndroid Build Coastguard Worker } 285*1fa6dee9SAndroid Build Coastguard Worker 286*1fa6dee9SAndroid Build Coastguard Worker return wrapper.Flush() 287*1fa6dee9SAndroid Build Coastguard Worker} 288*1fa6dee9SAndroid Build Coastguard Worker 289*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriter) Subninja(file string) error { 290*1fa6dee9SAndroid Build Coastguard Worker n.justDidBlankLine = false 291*1fa6dee9SAndroid Build Coastguard Worker return n.writeStatement("subninja", file) 292*1fa6dee9SAndroid Build Coastguard Worker} 293*1fa6dee9SAndroid Build Coastguard Worker 294*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriter) BlankLine() (err error) { 295*1fa6dee9SAndroid Build Coastguard Worker // We don't output multiple blank lines in a row. 296*1fa6dee9SAndroid Build Coastguard Worker if !n.justDidBlankLine { 297*1fa6dee9SAndroid Build Coastguard Worker n.justDidBlankLine = true 298*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString("\n") 299*1fa6dee9SAndroid Build Coastguard Worker } 300*1fa6dee9SAndroid Build Coastguard Worker return err 301*1fa6dee9SAndroid Build Coastguard Worker} 302*1fa6dee9SAndroid Build Coastguard Worker 303*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriter) writeStatement(directive, name string) error { 304*1fa6dee9SAndroid Build Coastguard Worker _, err := n.writer.WriteString(directive + " ") 305*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 306*1fa6dee9SAndroid Build Coastguard Worker return err 307*1fa6dee9SAndroid Build Coastguard Worker } 308*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString(name) 309*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 310*1fa6dee9SAndroid Build Coastguard Worker return err 311*1fa6dee9SAndroid Build Coastguard Worker } 312*1fa6dee9SAndroid Build Coastguard Worker _, err = n.writer.WriteString("\n") 313*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 314*1fa6dee9SAndroid Build Coastguard Worker return err 315*1fa6dee9SAndroid Build Coastguard Worker } 316*1fa6dee9SAndroid Build Coastguard Worker return nil 317*1fa6dee9SAndroid Build Coastguard Worker} 318*1fa6dee9SAndroid Build Coastguard Worker 319*1fa6dee9SAndroid Build Coastguard Worker// ninjaWriterWithWrap is an io.StringWriter that writes through to a ninjaWriter, but supports 320*1fa6dee9SAndroid Build Coastguard Worker// user-readable line wrapping on boundaries when ninjaWriterWithWrap.Space is called. 321*1fa6dee9SAndroid Build Coastguard Worker// It collects incoming calls to WriteString until either the line length is exceeded, in which case 322*1fa6dee9SAndroid Build Coastguard Worker// it inserts a wrap before the pending strings and then writes them, or the next call to Space, in 323*1fa6dee9SAndroid Build Coastguard Worker// which case it writes out the pending strings. 324*1fa6dee9SAndroid Build Coastguard Worker// 325*1fa6dee9SAndroid Build Coastguard Worker// WriteString never returns an error, all errors are held until Flush is called. Once an error has 326*1fa6dee9SAndroid Build Coastguard Worker// occurred all writes become noops. 327*1fa6dee9SAndroid Build Coastguard Workertype ninjaWriterWithWrap struct { 328*1fa6dee9SAndroid Build Coastguard Worker *ninjaWriter 329*1fa6dee9SAndroid Build Coastguard Worker // pending lists the strings that have been written since the last call to Space. 330*1fa6dee9SAndroid Build Coastguard Worker pending []string 331*1fa6dee9SAndroid Build Coastguard Worker 332*1fa6dee9SAndroid Build Coastguard Worker // pendingLen accumulates the lengths of the strings in pending. 333*1fa6dee9SAndroid Build Coastguard Worker pendingLen int 334*1fa6dee9SAndroid Build Coastguard Worker 335*1fa6dee9SAndroid Build Coastguard Worker // lineLen accumulates the number of bytes on the current line. 336*1fa6dee9SAndroid Build Coastguard Worker lineLen int 337*1fa6dee9SAndroid Build Coastguard Worker 338*1fa6dee9SAndroid Build Coastguard Worker // maxLineLen is the length of the line before wrapping. 339*1fa6dee9SAndroid Build Coastguard Worker maxLineLen int 340*1fa6dee9SAndroid Build Coastguard Worker 341*1fa6dee9SAndroid Build Coastguard Worker // space is true if the strings in pending should be preceded by a space. 342*1fa6dee9SAndroid Build Coastguard Worker space bool 343*1fa6dee9SAndroid Build Coastguard Worker 344*1fa6dee9SAndroid Build Coastguard Worker // err holds any error that has occurred to return in Flush. 345*1fa6dee9SAndroid Build Coastguard Worker err error 346*1fa6dee9SAndroid Build Coastguard Worker} 347*1fa6dee9SAndroid Build Coastguard Worker 348*1fa6dee9SAndroid Build Coastguard Worker// WriteString writes the string to buffer, wrapping on a previous Space call if necessary. 349*1fa6dee9SAndroid Build Coastguard Worker// It never returns an error, all errors are held until Flush is called. 350*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriterWithWrap) WriteString(s string) (written int, noError error) { 351*1fa6dee9SAndroid Build Coastguard Worker // Always return the full length of the string and a nil error. 352*1fa6dee9SAndroid Build Coastguard Worker // ninjaWriterWithWrap doesn't return errors to the caller, it saves them until Flush() 353*1fa6dee9SAndroid Build Coastguard Worker written = len(s) 354*1fa6dee9SAndroid Build Coastguard Worker 355*1fa6dee9SAndroid Build Coastguard Worker if n.err != nil { 356*1fa6dee9SAndroid Build Coastguard Worker return 357*1fa6dee9SAndroid Build Coastguard Worker } 358*1fa6dee9SAndroid Build Coastguard Worker 359*1fa6dee9SAndroid Build Coastguard Worker const spaceLen = 1 360*1fa6dee9SAndroid Build Coastguard Worker if !n.space { 361*1fa6dee9SAndroid Build Coastguard Worker // No space is pending, so a line wrap can't be inserted before this, so just write 362*1fa6dee9SAndroid Build Coastguard Worker // the string. 363*1fa6dee9SAndroid Build Coastguard Worker n.lineLen += len(s) 364*1fa6dee9SAndroid Build Coastguard Worker _, n.err = n.writer.WriteString(s) 365*1fa6dee9SAndroid Build Coastguard Worker } else if n.lineLen+len(s)+spaceLen > n.maxLineLen { 366*1fa6dee9SAndroid Build Coastguard Worker // A space is pending, and the pending strings plus the current string would exceed the 367*1fa6dee9SAndroid Build Coastguard Worker // maximum line length. Wrap and indent before the pending space and strings, then write 368*1fa6dee9SAndroid Build Coastguard Worker // the pending and current strings. 369*1fa6dee9SAndroid Build Coastguard Worker _, n.err = n.writer.WriteString(" $\n") 370*1fa6dee9SAndroid Build Coastguard Worker if n.err != nil { 371*1fa6dee9SAndroid Build Coastguard Worker return 372*1fa6dee9SAndroid Build Coastguard Worker } 373*1fa6dee9SAndroid Build Coastguard Worker _, n.err = n.writer.WriteString(indentString[:indentWidth*2]) 374*1fa6dee9SAndroid Build Coastguard Worker if n.err != nil { 375*1fa6dee9SAndroid Build Coastguard Worker return 376*1fa6dee9SAndroid Build Coastguard Worker } 377*1fa6dee9SAndroid Build Coastguard Worker n.lineLen = indentWidth*2 + n.pendingLen 378*1fa6dee9SAndroid Build Coastguard Worker s = strings.TrimLeftFunc(s, unicode.IsSpace) 379*1fa6dee9SAndroid Build Coastguard Worker n.pending = append(n.pending, s) 380*1fa6dee9SAndroid Build Coastguard Worker n.lineLen += len(s) 381*1fa6dee9SAndroid Build Coastguard Worker n.writePending() 382*1fa6dee9SAndroid Build Coastguard Worker 383*1fa6dee9SAndroid Build Coastguard Worker n.space = false 384*1fa6dee9SAndroid Build Coastguard Worker } else { 385*1fa6dee9SAndroid Build Coastguard Worker // A space is pending but the current string would not reach the maximum line length, 386*1fa6dee9SAndroid Build Coastguard Worker // add it to the pending list. 387*1fa6dee9SAndroid Build Coastguard Worker n.pending = append(n.pending, s) 388*1fa6dee9SAndroid Build Coastguard Worker n.pendingLen += len(s) 389*1fa6dee9SAndroid Build Coastguard Worker n.lineLen += len(s) 390*1fa6dee9SAndroid Build Coastguard Worker } 391*1fa6dee9SAndroid Build Coastguard Worker 392*1fa6dee9SAndroid Build Coastguard Worker return 393*1fa6dee9SAndroid Build Coastguard Worker} 394*1fa6dee9SAndroid Build Coastguard Worker 395*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriterWithWrap) Write(p []byte) (written int, noError error) { 396*1fa6dee9SAndroid Build Coastguard Worker // Write is rarely used, implement it via WriteString. 397*1fa6dee9SAndroid Build Coastguard Worker return n.WriteString(string(p)) 398*1fa6dee9SAndroid Build Coastguard Worker} 399*1fa6dee9SAndroid Build Coastguard Worker 400*1fa6dee9SAndroid Build Coastguard Worker// Space inserts a space that is also a possible wrapping point into the string. 401*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriterWithWrap) Space() { 402*1fa6dee9SAndroid Build Coastguard Worker if n.err != nil { 403*1fa6dee9SAndroid Build Coastguard Worker return 404*1fa6dee9SAndroid Build Coastguard Worker } 405*1fa6dee9SAndroid Build Coastguard Worker if n.space { 406*1fa6dee9SAndroid Build Coastguard Worker // A space was already pending, and the space plus any strings written after the space did 407*1fa6dee9SAndroid Build Coastguard Worker // not reach the maxmimum line length, so write out the old space and pending strings. 408*1fa6dee9SAndroid Build Coastguard Worker _, n.err = n.writer.WriteString(" ") 409*1fa6dee9SAndroid Build Coastguard Worker n.lineLen++ 410*1fa6dee9SAndroid Build Coastguard Worker n.writePending() 411*1fa6dee9SAndroid Build Coastguard Worker } 412*1fa6dee9SAndroid Build Coastguard Worker n.space = true 413*1fa6dee9SAndroid Build Coastguard Worker} 414*1fa6dee9SAndroid Build Coastguard Worker 415*1fa6dee9SAndroid Build Coastguard Worker// writePending writes out all the strings stored in pending and resets it. 416*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriterWithWrap) writePending() { 417*1fa6dee9SAndroid Build Coastguard Worker if n.err != nil { 418*1fa6dee9SAndroid Build Coastguard Worker return 419*1fa6dee9SAndroid Build Coastguard Worker } 420*1fa6dee9SAndroid Build Coastguard Worker for _, pending := range n.pending { 421*1fa6dee9SAndroid Build Coastguard Worker _, n.err = n.writer.WriteString(pending) 422*1fa6dee9SAndroid Build Coastguard Worker if n.err != nil { 423*1fa6dee9SAndroid Build Coastguard Worker return 424*1fa6dee9SAndroid Build Coastguard Worker } 425*1fa6dee9SAndroid Build Coastguard Worker } 426*1fa6dee9SAndroid Build Coastguard Worker // Reset the length of pending back to 0 without reducing its capacity to avoid reallocating 427*1fa6dee9SAndroid Build Coastguard Worker // the backing array. 428*1fa6dee9SAndroid Build Coastguard Worker n.pending = n.pending[:0] 429*1fa6dee9SAndroid Build Coastguard Worker n.pendingLen = 0 430*1fa6dee9SAndroid Build Coastguard Worker} 431*1fa6dee9SAndroid Build Coastguard Worker 432*1fa6dee9SAndroid Build Coastguard Worker// WriteStringWithSpace is a helper that calls Space and WriteString. 433*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriterWithWrap) WriteStringWithSpace(s string) { 434*1fa6dee9SAndroid Build Coastguard Worker n.Space() 435*1fa6dee9SAndroid Build Coastguard Worker _, _ = n.WriteString(s) 436*1fa6dee9SAndroid Build Coastguard Worker} 437*1fa6dee9SAndroid Build Coastguard Worker 438*1fa6dee9SAndroid Build Coastguard Worker// Flush writes out any pending space or strings and then a newline. It also returns any errors 439*1fa6dee9SAndroid Build Coastguard Worker// that have previously occurred. 440*1fa6dee9SAndroid Build Coastguard Workerfunc (n *ninjaWriterWithWrap) Flush() error { 441*1fa6dee9SAndroid Build Coastguard Worker if n.space { 442*1fa6dee9SAndroid Build Coastguard Worker _, n.err = n.writer.WriteString(" ") 443*1fa6dee9SAndroid Build Coastguard Worker } 444*1fa6dee9SAndroid Build Coastguard Worker n.writePending() 445*1fa6dee9SAndroid Build Coastguard Worker if n.err != nil { 446*1fa6dee9SAndroid Build Coastguard Worker return n.err 447*1fa6dee9SAndroid Build Coastguard Worker } 448*1fa6dee9SAndroid Build Coastguard Worker _, err := n.writer.WriteString("\n") 449*1fa6dee9SAndroid Build Coastguard Worker return err 450*1fa6dee9SAndroid Build Coastguard Worker} 451