xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/builders/flags.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
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