// Copyright 2017 The Bazel Authors. 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 main import ( "errors" "fmt" "go/build" "strings" "unicode" ) // multiFlag allows repeated string flags to be collected into a slice type multiFlag []string func (m *multiFlag) String() string { if m == nil || len(*m) == 0 { return "" } return fmt.Sprint(*m) } func (m *multiFlag) Set(v string) error { (*m) = append(*m, v) return nil } // quoteMultiFlag allows repeated string flags to be collected into a slice. // Flags are split on spaces. Single quotes are removed, and spaces within // quotes are removed. Literal quotes may be escaped with a backslash. type quoteMultiFlag []string func (m *quoteMultiFlag) String() string { if m == nil || len(*m) == 0 { return "" } return fmt.Sprint(*m) } func (m *quoteMultiFlag) Set(v string) error { fs, err := splitQuoted(v) if err != nil { return err } *m = append(*m, fs...) return nil } // splitQuoted splits the string s around each instance of one or more consecutive // white space characters while taking into account quotes and escaping, and // returns an array of substrings of s or an empty list if s contains only white space. // Single quotes and double quotes are recognized to prevent splitting within the // quoted region, and are removed from the resulting substrings. If a quote in s // isn't closed err will be set and r will have the unclosed argument as the // last element. The backslash is used for escaping. // // For example, the following string: // // a b:"c d" 'e''f' "g\"" // // Would be parsed as: // // []string{"a", "b:c d", "ef", `g"`} // // Copied from go/build.splitQuoted. Also in Gazelle (where tests are). func splitQuoted(s string) (r []string, err error) { var args []string arg := make([]rune, len(s)) escaped := false quoted := false quote := '\x00' i := 0 for _, rune := range s { switch { case escaped: escaped = false case rune == '\\': escaped = true continue case quote != '\x00': if rune == quote { quote = '\x00' continue } case rune == '"' || rune == '\'': quoted = true quote = rune continue case unicode.IsSpace(rune): if quoted || i > 0 { quoted = false args = append(args, string(arg[:i])) i = 0 } continue } arg[i] = rune i++ } if quoted || i > 0 { args = append(args, string(arg[:i])) } if quote != 0 { err = errors.New("unclosed quote") } else if escaped { err = errors.New("unfinished escaping") } return args, err } // tagFlag adds tags to the build.Default context. Tags are expected to be // formatted as a comma-separated list. type tagFlag struct{} func (f *tagFlag) String() string { return strings.Join(build.Default.BuildTags, ",") } func (f *tagFlag) Set(opt string) error { tags := strings.Split(opt, ",") build.Default.BuildTags = append(build.Default.BuildTags, tags...) return nil }