Home | History | Annotate | Download | only in build
      1 // Copyright 2017 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 
     15 package build
     16 
     17 import (
     18 	"bytes"
     19 	"io"
     20 	"os"
     21 	"path/filepath"
     22 	"strings"
     23 	"syscall"
     24 	"unsafe"
     25 )
     26 
     27 func absPath(ctx Context, p string) string {
     28 	ret, err := filepath.Abs(p)
     29 	if err != nil {
     30 		ctx.Fatalf("Failed to get absolute path: %v", err)
     31 	}
     32 	return ret
     33 }
     34 
     35 // indexList finds the index of a string in a []string
     36 func indexList(s string, list []string) int {
     37 	for i, l := range list {
     38 		if l == s {
     39 			return i
     40 		}
     41 	}
     42 
     43 	return -1
     44 }
     45 
     46 // inList determines whether a string is in a []string
     47 func inList(s string, list []string) bool {
     48 	return indexList(s, list) != -1
     49 }
     50 
     51 // ensureDirectoriesExist is a shortcut to os.MkdirAll, sending errors to the ctx logger.
     52 func ensureDirectoriesExist(ctx Context, dirs ...string) {
     53 	for _, dir := range dirs {
     54 		err := os.MkdirAll(dir, 0777)
     55 		if err != nil {
     56 			ctx.Fatalf("Error creating %s: %q\n", dir, err)
     57 		}
     58 	}
     59 }
     60 
     61 // ensureEmptyDirectoriesExist ensures that the given directories exist and are empty
     62 func ensureEmptyDirectoriesExist(ctx Context, dirs ...string) {
     63 	// remove all the directories
     64 	for _, dir := range dirs {
     65 		err := os.RemoveAll(dir)
     66 		if err != nil {
     67 			ctx.Fatalf("Error removing %s: %q\n", dir, err)
     68 		}
     69 	}
     70 	// recreate all the directories
     71 	ensureDirectoriesExist(ctx, dirs...)
     72 }
     73 
     74 // ensureEmptyFileExists ensures that the containing directory exists, and the
     75 // specified file exists. If it doesn't exist, it will write an empty file.
     76 func ensureEmptyFileExists(ctx Context, file string) {
     77 	ensureDirectoriesExist(ctx, filepath.Dir(file))
     78 	if _, err := os.Stat(file); os.IsNotExist(err) {
     79 		f, err := os.Create(file)
     80 		if err != nil {
     81 			ctx.Fatalf("Error creating %s: %q\n", file, err)
     82 		}
     83 		f.Close()
     84 	} else if err != nil {
     85 		ctx.Fatalf("Error checking %s: %q\n", file, err)
     86 	}
     87 }
     88 
     89 // singleUnquote is similar to strconv.Unquote, but can handle multi-character strings inside single quotes.
     90 func singleUnquote(str string) (string, bool) {
     91 	if len(str) < 2 || str[0] != '\'' || str[len(str)-1] != '\'' {
     92 		return "", false
     93 	}
     94 	return str[1 : len(str)-1], true
     95 }
     96 
     97 // decodeKeyValue decodes a key=value string
     98 func decodeKeyValue(str string) (string, string, bool) {
     99 	idx := strings.IndexRune(str, '=')
    100 	if idx == -1 {
    101 		return "", "", false
    102 	}
    103 	return str[:idx], str[idx+1:], true
    104 }
    105 
    106 func isTerminal(w io.Writer) bool {
    107 	if f, ok := w.(*os.File); ok {
    108 		var termios syscall.Termios
    109 		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
    110 			ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
    111 			0, 0, 0)
    112 		return err == 0
    113 	}
    114 	return false
    115 }
    116 
    117 func termWidth(w io.Writer) (int, bool) {
    118 	if f, ok := w.(*os.File); ok {
    119 		var winsize struct {
    120 			ws_row, ws_column    uint16
    121 			ws_xpixel, ws_ypixel uint16
    122 		}
    123 		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
    124 			syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
    125 			0, 0, 0)
    126 		return int(winsize.ws_column), err == 0
    127 	}
    128 	return 0, false
    129 }
    130 
    131 // stripAnsiEscapes strips ANSI control codes from a byte array in place.
    132 func stripAnsiEscapes(input []byte) []byte {
    133 	// read represents the remaining part of input that needs to be processed.
    134 	read := input
    135 	// write represents where we should be writing in input.
    136 	// It will share the same backing store as input so that we make our modifications
    137 	// in place.
    138 	write := input
    139 
    140 	// advance will copy count bytes from read to write and advance those slices
    141 	advance := func(write, read []byte, count int) ([]byte, []byte) {
    142 		copy(write, read[:count])
    143 		return write[count:], read[count:]
    144 	}
    145 
    146 	for {
    147 		// Find the next escape sequence
    148 		i := bytes.IndexByte(read, 0x1b)
    149 		// If it isn't found, or if there isn't room for <ESC>[, finish
    150 		if i == -1 || i+1 >= len(read) {
    151 			copy(write, read)
    152 			break
    153 		}
    154 
    155 		// Not a CSI code, continue searching
    156 		if read[i+1] != '[' {
    157 			write, read = advance(write, read, i+1)
    158 			continue
    159 		}
    160 
    161 		// Found a CSI code, advance up to the <ESC>
    162 		write, read = advance(write, read, i)
    163 
    164 		// Find the end of the CSI code
    165 		i = bytes.IndexFunc(read, func(r rune) bool {
    166 			return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
    167 		})
    168 		if i == -1 {
    169 			// We didn't find the end of the code, just remove the rest
    170 			i = len(read) - 1
    171 		}
    172 
    173 		// Strip off the end marker too
    174 		i = i + 1
    175 
    176 		// Skip the reader forward and reduce final length by that amount
    177 		read = read[i:]
    178 		input = input[:len(input)-i]
    179 	}
    180 
    181 	return input
    182 }
    183