github.com/0xKiwi/rules_go@v0.24.3/go/tools/builders/flags.go (about) 1 // Copyright 2017 The Bazel Authors. 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 main 16 17 import ( 18 "errors" 19 "fmt" 20 "go/build" 21 "strings" 22 "unicode" 23 ) 24 25 // multiFlag allows repeated string flags to be collected into a slice 26 type multiFlag []string 27 28 func (m *multiFlag) String() string { 29 if m == nil || len(*m) == 0 { 30 return "" 31 } 32 return fmt.Sprint(*m) 33 } 34 35 func (m *multiFlag) Set(v string) error { 36 (*m) = append(*m, v) 37 return nil 38 } 39 40 // quoteMultiFlag allows repeated string flags to be collected into a slice. 41 // Flags are split on spaces. Single quotes are removed, and spaces within 42 // quotes are removed. Literal quotes may be escaped with a backslash. 43 type quoteMultiFlag []string 44 45 func (m *quoteMultiFlag) String() string { 46 if m == nil || len(*m) == 0 { 47 return "" 48 } 49 return fmt.Sprint(*m) 50 } 51 52 func (m *quoteMultiFlag) Set(v string) error { 53 fs, err := splitQuoted(v) 54 if err != nil { 55 return err 56 } 57 *m = append(*m, fs...) 58 return nil 59 } 60 61 // splitQuoted splits the string s around each instance of one or more consecutive 62 // white space characters while taking into account quotes and escaping, and 63 // returns an array of substrings of s or an empty list if s contains only white space. 64 // Single quotes and double quotes are recognized to prevent splitting within the 65 // quoted region, and are removed from the resulting substrings. If a quote in s 66 // isn't closed err will be set and r will have the unclosed argument as the 67 // last element. The backslash is used for escaping. 68 // 69 // For example, the following string: 70 // 71 // a b:"c d" 'e''f' "g\"" 72 // 73 // Would be parsed as: 74 // 75 // []string{"a", "b:c d", "ef", `g"`} 76 // 77 // Copied from go/build.splitQuoted. Also in Gazelle (where tests are). 78 func splitQuoted(s string) (r []string, err error) { 79 var args []string 80 arg := make([]rune, len(s)) 81 escaped := false 82 quoted := false 83 quote := '\x00' 84 i := 0 85 for _, rune := range s { 86 switch { 87 case escaped: 88 escaped = false 89 case rune == '\\': 90 escaped = true 91 continue 92 case quote != '\x00': 93 if rune == quote { 94 quote = '\x00' 95 continue 96 } 97 case rune == '"' || rune == '\'': 98 quoted = true 99 quote = rune 100 continue 101 case unicode.IsSpace(rune): 102 if quoted || i > 0 { 103 quoted = false 104 args = append(args, string(arg[:i])) 105 i = 0 106 } 107 continue 108 } 109 arg[i] = rune 110 i++ 111 } 112 if quoted || i > 0 { 113 args = append(args, string(arg[:i])) 114 } 115 if quote != 0 { 116 err = errors.New("unclosed quote") 117 } else if escaped { 118 err = errors.New("unfinished escaping") 119 } 120 return args, err 121 } 122 123 // tagFlag adds tags to the build.Default context. Tags are expected to be 124 // formatted as a comma-separated list. 125 type tagFlag struct{} 126 127 func (f *tagFlag) String() string { 128 return strings.Join(build.Default.BuildTags, ",") 129 } 130 131 func (f *tagFlag) Set(opt string) error { 132 tags := strings.Split(opt, ",") 133 build.Default.BuildTags = append(build.Default.BuildTags, tags...) 134 return nil 135 }