8ac0a812f1
Prebuild a string of all spaces to avoid calling strings.Repeat for every indented line. Replace appendWithWrap function with a class that handles counting bytes on a line as they are written instead of buffering lines, and avoids extra string concatenations by providing a function that implicitly adds a space. Saves 10% wall time on one workload. Change-Id: I571d70366ee5db21c40c1c98d0fe92d640efefab
244 lines
5.2 KiB
Go
244 lines
5.2 KiB
Go
// Copyright 2014 Google Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package blueprint
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
const (
|
|
indentWidth = 4
|
|
maxIndentDepth = 2
|
|
lineWidth = 80
|
|
)
|
|
|
|
var indentString = strings.Repeat(" ", indentWidth*maxIndentDepth)
|
|
|
|
type ninjaWriter struct {
|
|
writer io.Writer
|
|
|
|
justDidBlankLine bool // true if the last operation was a BlankLine
|
|
}
|
|
|
|
func newNinjaWriter(writer io.Writer) *ninjaWriter {
|
|
return &ninjaWriter{
|
|
writer: writer,
|
|
}
|
|
}
|
|
|
|
func (n *ninjaWriter) Comment(comment string) error {
|
|
n.justDidBlankLine = false
|
|
|
|
const lineHeaderLen = len("# ")
|
|
const maxLineLen = lineWidth - lineHeaderLen
|
|
|
|
var lineStart, lastSplitPoint int
|
|
for i, r := range comment {
|
|
if unicode.IsSpace(r) {
|
|
// We know we can safely split the line here.
|
|
lastSplitPoint = i + 1
|
|
}
|
|
|
|
var line string
|
|
var writeLine bool
|
|
switch {
|
|
case r == '\n':
|
|
// Output the line without trimming the left so as to allow comments
|
|
// to contain their own indentation.
|
|
line = strings.TrimRightFunc(comment[lineStart:i], unicode.IsSpace)
|
|
writeLine = true
|
|
|
|
case (i-lineStart > maxLineLen) && (lastSplitPoint > lineStart):
|
|
// The line has grown too long and is splittable. Split it at the
|
|
// last split point.
|
|
line = strings.TrimSpace(comment[lineStart:lastSplitPoint])
|
|
writeLine = true
|
|
}
|
|
|
|
if writeLine {
|
|
line = strings.TrimSpace("# "+line) + "\n"
|
|
_, err := io.WriteString(n.writer, line)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lineStart = lastSplitPoint
|
|
}
|
|
}
|
|
|
|
if lineStart != len(comment) {
|
|
line := strings.TrimSpace(comment[lineStart:])
|
|
_, err := fmt.Fprintf(n.writer, "# %s\n", line)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (n *ninjaWriter) Pool(name string) error {
|
|
n.justDidBlankLine = false
|
|
_, err := fmt.Fprintf(n.writer, "pool %s\n", name)
|
|
return err
|
|
}
|
|
|
|
func (n *ninjaWriter) Rule(name string) error {
|
|
n.justDidBlankLine = false
|
|
_, err := fmt.Fprintf(n.writer, "rule %s\n", name)
|
|
return err
|
|
}
|
|
|
|
func (n *ninjaWriter) Build(rule string, outputs, explicitDeps, implicitDeps,
|
|
orderOnlyDeps []string) error {
|
|
|
|
n.justDidBlankLine = false
|
|
|
|
const lineWrapLen = len(" $")
|
|
const maxLineLen = lineWidth - lineWrapLen
|
|
|
|
wrapper := ninjaWriterWithWrap{
|
|
ninjaWriter: n,
|
|
maxLineLen: maxLineLen,
|
|
}
|
|
|
|
wrapper.WriteString("build")
|
|
|
|
for _, output := range outputs {
|
|
wrapper.WriteStringWithSpace(output)
|
|
}
|
|
|
|
wrapper.WriteString(":")
|
|
|
|
wrapper.WriteStringWithSpace(rule)
|
|
|
|
for _, dep := range explicitDeps {
|
|
wrapper.WriteStringWithSpace(dep)
|
|
}
|
|
|
|
if len(implicitDeps) > 0 {
|
|
wrapper.WriteStringWithSpace("|")
|
|
|
|
for _, dep := range implicitDeps {
|
|
wrapper.WriteStringWithSpace(dep)
|
|
}
|
|
}
|
|
|
|
if len(orderOnlyDeps) > 0 {
|
|
wrapper.WriteStringWithSpace("||")
|
|
|
|
for _, dep := range orderOnlyDeps {
|
|
wrapper.WriteStringWithSpace(dep)
|
|
}
|
|
}
|
|
|
|
return wrapper.Flush()
|
|
}
|
|
|
|
func (n *ninjaWriter) Assign(name, value string) error {
|
|
n.justDidBlankLine = false
|
|
_, err := fmt.Fprintf(n.writer, "%s = %s\n", name, value)
|
|
return err
|
|
}
|
|
|
|
func (n *ninjaWriter) ScopedAssign(name, value string) error {
|
|
n.justDidBlankLine = false
|
|
_, err := fmt.Fprintf(n.writer, "%s%s = %s\n", indentString[:indentWidth], name, value)
|
|
return err
|
|
}
|
|
|
|
func (n *ninjaWriter) Default(targets ...string) error {
|
|
n.justDidBlankLine = false
|
|
|
|
const lineWrapLen = len(" $")
|
|
const maxLineLen = lineWidth - lineWrapLen
|
|
|
|
wrapper := ninjaWriterWithWrap{
|
|
ninjaWriter: n,
|
|
maxLineLen: maxLineLen,
|
|
}
|
|
|
|
wrapper.WriteString("default")
|
|
|
|
for _, target := range targets {
|
|
wrapper.WriteString(" " + target)
|
|
}
|
|
|
|
return wrapper.Flush()
|
|
}
|
|
|
|
func (n *ninjaWriter) BlankLine() (err error) {
|
|
// We don't output multiple blank lines in a row.
|
|
if !n.justDidBlankLine {
|
|
n.justDidBlankLine = true
|
|
_, err = io.WriteString(n.writer, "\n")
|
|
}
|
|
return err
|
|
}
|
|
|
|
type ninjaWriterWithWrap struct {
|
|
*ninjaWriter
|
|
maxLineLen int
|
|
writtenLen int
|
|
err error
|
|
}
|
|
|
|
func (n *ninjaWriterWithWrap) writeString(s string, space bool) {
|
|
if n.err != nil {
|
|
return
|
|
}
|
|
|
|
spaceLen := 0
|
|
if space {
|
|
spaceLen = 1
|
|
}
|
|
|
|
if n.writtenLen+len(s)+spaceLen > n.maxLineLen {
|
|
_, n.err = io.WriteString(n.writer, " $\n")
|
|
if n.err != nil {
|
|
return
|
|
}
|
|
_, n.err = io.WriteString(n.writer, indentString[:indentWidth*2])
|
|
if n.err != nil {
|
|
return
|
|
}
|
|
n.writtenLen = indentWidth * 2
|
|
s = strings.TrimLeftFunc(s, unicode.IsSpace)
|
|
} else if space {
|
|
io.WriteString(n.writer, " ")
|
|
n.writtenLen++
|
|
}
|
|
|
|
_, n.err = io.WriteString(n.writer, s)
|
|
n.writtenLen += len(s)
|
|
}
|
|
|
|
func (n *ninjaWriterWithWrap) WriteString(s string) {
|
|
n.writeString(s, false)
|
|
}
|
|
|
|
func (n *ninjaWriterWithWrap) WriteStringWithSpace(s string) {
|
|
n.writeString(s, true)
|
|
}
|
|
|
|
func (n *ninjaWriterWithWrap) Flush() error {
|
|
if n.err != nil {
|
|
return n.err
|
|
}
|
|
_, err := io.WriteString(n.writer, "\n")
|
|
return err
|
|
}
|