|  | // Copyright 2017 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 build | 
|  |  | 
|  | import ( | 
|  | "compress/gzip" | 
|  | "fmt" | 
|  | "io" | 
|  | "os" | 
|  | "path/filepath" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | func absPath(ctx Context, p string) string { | 
|  | ret, err := filepath.Abs(p) | 
|  | if err != nil { | 
|  | ctx.Fatalf("Failed to get absolute path: %v", err) | 
|  | } | 
|  | return ret | 
|  | } | 
|  |  | 
|  | // indexList finds the index of a string in a []string | 
|  | func indexList(s string, list []string) int { | 
|  | for i, l := range list { | 
|  | if l == s { | 
|  | return i | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1 | 
|  | } | 
|  |  | 
|  | // inList determines whether a string is in a []string | 
|  | func inList(s string, list []string) bool { | 
|  | return indexList(s, list) != -1 | 
|  | } | 
|  |  | 
|  | // removeFromlist removes all occurrences of the string in list. | 
|  | func removeFromList(s string, list []string) []string { | 
|  | filteredList := make([]string, 0, len(list)) | 
|  | for _, ls := range list { | 
|  | if s != ls { | 
|  | filteredList = append(filteredList, ls) | 
|  | } | 
|  | } | 
|  | return filteredList | 
|  | } | 
|  |  | 
|  | // ensureDirectoriesExist is a shortcut to os.MkdirAll, sending errors to the ctx logger. | 
|  | func ensureDirectoriesExist(ctx Context, dirs ...string) { | 
|  | for _, dir := range dirs { | 
|  | err := os.MkdirAll(dir, 0777) | 
|  | if err != nil { | 
|  | ctx.Fatalf("Error creating %s: %q\n", dir, err) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // ensureEmptyDirectoriesExist ensures that the given directories exist and are empty | 
|  | func ensureEmptyDirectoriesExist(ctx Context, dirs ...string) { | 
|  | // remove all the directories | 
|  | for _, dir := range dirs { | 
|  | seenErr := map[string]bool{} | 
|  | for { | 
|  | err := os.RemoveAll(dir) | 
|  | if err == nil { | 
|  | break | 
|  | } | 
|  |  | 
|  | if pathErr, ok := err.(*os.PathError); !ok || | 
|  | dir == pathErr.Path || seenErr[pathErr.Path] { | 
|  |  | 
|  | ctx.Fatalf("Error removing %s: %q\n", dir, err) | 
|  | } else { | 
|  | seenErr[pathErr.Path] = true | 
|  | err = os.Chmod(filepath.Dir(pathErr.Path), 0700) | 
|  | if err != nil { | 
|  | ctx.Fatal(err) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | // recreate all the directories | 
|  | ensureDirectoriesExist(ctx, dirs...) | 
|  | } | 
|  |  | 
|  | // ensureEmptyFileExists ensures that the containing directory exists, and the | 
|  | // specified file exists. If it doesn't exist, it will write an empty file. | 
|  | func ensureEmptyFileExists(ctx Context, file string) { | 
|  | ensureDirectoriesExist(ctx, filepath.Dir(file)) | 
|  | if _, err := os.Stat(file); os.IsNotExist(err) { | 
|  | f, err := os.Create(file) | 
|  | if err != nil { | 
|  | ctx.Fatalf("Error creating %s: %q\n", file, err) | 
|  | } | 
|  | f.Close() | 
|  | } else if err != nil { | 
|  | ctx.Fatalf("Error checking %s: %q\n", file, err) | 
|  | } | 
|  | } | 
|  |  | 
|  | // singleUnquote is similar to strconv.Unquote, but can handle multi-character strings inside single quotes. | 
|  | func singleUnquote(str string) (string, bool) { | 
|  | if len(str) < 2 || str[0] != '\'' || str[len(str)-1] != '\'' { | 
|  | return "", false | 
|  | } | 
|  | return str[1 : len(str)-1], true | 
|  | } | 
|  |  | 
|  | // decodeKeyValue decodes a key=value string | 
|  | func decodeKeyValue(str string) (string, string, bool) { | 
|  | idx := strings.IndexRune(str, '=') | 
|  | if idx == -1 { | 
|  | return "", "", false | 
|  | } | 
|  | return str[:idx], str[idx+1:], true | 
|  | } | 
|  |  | 
|  | // copyFile copies a file from src to dst. filepath.Dir(dst) must exist. | 
|  | func copyFile(src, dst string) (int64, error) { | 
|  | source, err := os.Open(src) | 
|  | if err != nil { | 
|  | return 0, err | 
|  | } | 
|  | defer source.Close() | 
|  |  | 
|  | destination, err := os.Create(dst) | 
|  | if err != nil { | 
|  | return 0, err | 
|  | } | 
|  | defer destination.Close() | 
|  |  | 
|  | return io.Copy(destination, source) | 
|  | } | 
|  |  | 
|  | // gzipFileToDir writes a compressed copy of src to destDir with the suffix ".gz". | 
|  | func gzipFileToDir(src, destDir string) error { | 
|  | in, err := os.Open(src) | 
|  | if err != nil { | 
|  | return fmt.Errorf("failed to open %s: %s", src, err.Error()) | 
|  | } | 
|  | defer in.Close() | 
|  |  | 
|  | dest := filepath.Join(destDir, filepath.Base(src)+".gz") | 
|  |  | 
|  | out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 0666) | 
|  | if err != nil { | 
|  | return fmt.Errorf("failed to open %s: %s", dest, err.Error()) | 
|  | } | 
|  | defer out.Close() | 
|  | gz := gzip.NewWriter(out) | 
|  | defer gz.Close() | 
|  |  | 
|  | _, err = io.Copy(gz, in) | 
|  | if err != nil { | 
|  | return fmt.Errorf("failed to gzip %s: %s", dest, err.Error()) | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } |