blob: e3604cbd7b093e3961ee738ac3b72a4c8128df7b [file] [log] [blame] [edit]
// 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
}