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 15package main 16 17import ( 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 26type multiFlag []string 27 28func (m *multiFlag) String() string { 29 if m == nil || len(*m) == 0 { 30 return "" 31 } 32 return fmt.Sprint(*m) 33} 34 35func (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. 43type quoteMultiFlag []string 44 45func (m *quoteMultiFlag) String() string { 46 if m == nil || len(*m) == 0 { 47 return "" 48 } 49 return fmt.Sprint(*m) 50} 51 52func (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). 78func 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. 125type tagFlag struct{} 126 127func (f *tagFlag) String() string { 128 return strings.Join(build.Default.BuildTags, ",") 129} 130 131func (f *tagFlag) Set(opt string) error { 132 tags := strings.Split(opt, ",") 133 build.Default.BuildTags = append(build.Default.BuildTags, tags...) 134 return nil 135} 136