blob: 96088fe781bcf67cf0d9e3ae8dce4965d8351817 [file] [log] [blame]
Dan Willemsen1e704462016-08-21 15:17:17 -07001// 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
15package build
16
17import (
Dan Willemsen29f88272017-02-18 18:12:41 -080018 "bytes"
19 "io"
Dan Willemsen1e704462016-08-21 15:17:17 -070020 "os"
21 "path/filepath"
22 "strings"
Dan Willemsen29f88272017-02-18 18:12:41 -080023 "syscall"
24 "unsafe"
Dan Willemsen1e704462016-08-21 15:17:17 -070025)
26
Dan Willemsend9e8f0a2017-10-30 13:42:06 -070027func 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
Dan Willemsen1e704462016-08-21 15:17:17 -070035// indexList finds the index of a string in a []string
36func 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
47func 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.
52func 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
Jeff Gastonefc1b412017-03-29 17:29:06 -070061// ensureEmptyDirectoriesExist ensures that the given directories exist and are empty
62func ensureEmptyDirectoriesExist(ctx Context, dirs ...string) {
63 // remove all the directories
64 for _, dir := range dirs {
Dan Willemsenfe8b6452018-05-12 18:34:24 -070065 seenErr := map[string]bool{}
66 for {
67 err := os.RemoveAll(dir)
68 if err == nil {
69 break
70 }
71
72 if pathErr, ok := err.(*os.PathError); !ok ||
73 dir == pathErr.Path || seenErr[pathErr.Path] {
74
75 ctx.Fatalf("Error removing %s: %q\n", dir, err)
76 } else {
77 seenErr[pathErr.Path] = true
78 err = os.Chmod(filepath.Dir(pathErr.Path), 0700)
79 if err != nil {
80 ctx.Fatal(err)
81 }
82 }
Jeff Gastonefc1b412017-03-29 17:29:06 -070083 }
84 }
85 // recreate all the directories
86 ensureDirectoriesExist(ctx, dirs...)
87}
88
Dan Willemsen1e704462016-08-21 15:17:17 -070089// ensureEmptyFileExists ensures that the containing directory exists, and the
90// specified file exists. If it doesn't exist, it will write an empty file.
91func ensureEmptyFileExists(ctx Context, file string) {
92 ensureDirectoriesExist(ctx, filepath.Dir(file))
93 if _, err := os.Stat(file); os.IsNotExist(err) {
94 f, err := os.Create(file)
95 if err != nil {
96 ctx.Fatalf("Error creating %s: %q\n", file, err)
97 }
98 f.Close()
99 } else if err != nil {
100 ctx.Fatalf("Error checking %s: %q\n", file, err)
101 }
102}
103
104// singleUnquote is similar to strconv.Unquote, but can handle multi-character strings inside single quotes.
105func singleUnquote(str string) (string, bool) {
106 if len(str) < 2 || str[0] != '\'' || str[len(str)-1] != '\'' {
107 return "", false
108 }
109 return str[1 : len(str)-1], true
110}
111
112// decodeKeyValue decodes a key=value string
113func decodeKeyValue(str string) (string, string, bool) {
114 idx := strings.IndexRune(str, '=')
115 if idx == -1 {
116 return "", "", false
117 }
118 return str[:idx], str[idx+1:], true
119}
Dan Willemsen29f88272017-02-18 18:12:41 -0800120
121func isTerminal(w io.Writer) bool {
122 if f, ok := w.(*os.File); ok {
123 var termios syscall.Termios
124 _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
125 ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
126 0, 0, 0)
127 return err == 0
128 }
129 return false
130}
131
132func termWidth(w io.Writer) (int, bool) {
133 if f, ok := w.(*os.File); ok {
134 var winsize struct {
135 ws_row, ws_column uint16
136 ws_xpixel, ws_ypixel uint16
137 }
138 _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
139 syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
140 0, 0, 0)
141 return int(winsize.ws_column), err == 0
142 }
143 return 0, false
144}
145
146// stripAnsiEscapes strips ANSI control codes from a byte array in place.
147func stripAnsiEscapes(input []byte) []byte {
148 // read represents the remaining part of input that needs to be processed.
149 read := input
150 // write represents where we should be writing in input.
151 // It will share the same backing store as input so that we make our modifications
152 // in place.
153 write := input
154
155 // advance will copy count bytes from read to write and advance those slices
156 advance := func(write, read []byte, count int) ([]byte, []byte) {
157 copy(write, read[:count])
158 return write[count:], read[count:]
159 }
160
161 for {
162 // Find the next escape sequence
163 i := bytes.IndexByte(read, 0x1b)
164 // If it isn't found, or if there isn't room for <ESC>[, finish
165 if i == -1 || i+1 >= len(read) {
166 copy(write, read)
167 break
168 }
169
170 // Not a CSI code, continue searching
171 if read[i+1] != '[' {
172 write, read = advance(write, read, i+1)
173 continue
174 }
175
176 // Found a CSI code, advance up to the <ESC>
177 write, read = advance(write, read, i)
178
179 // Find the end of the CSI code
180 i = bytes.IndexFunc(read, func(r rune) bool {
181 return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
182 })
183 if i == -1 {
184 // We didn't find the end of the code, just remove the rest
185 i = len(read) - 1
186 }
187
188 // Strip off the end marker too
189 i = i + 1
190
191 // Skip the reader forward and reduce final length by that amount
192 read = read[i:]
193 input = input[:len(input)-i]
194 }
195
196 return input
197}