xref: /aosp_15_r20/external/bazelbuild-rules_android/src/common/golang/flagfile.go (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
1*9e965d6fSRomain Jobredeaux// Copyright 2018 The Bazel Authors. All rights reserved.
2*9e965d6fSRomain Jobredeaux//
3*9e965d6fSRomain Jobredeaux// Licensed under the Apache License, Version 2.0 (the "License");
4*9e965d6fSRomain Jobredeaux// you may not use this file except in compliance with the License.
5*9e965d6fSRomain Jobredeaux// You may obtain a copy of the License at
6*9e965d6fSRomain Jobredeaux//
7*9e965d6fSRomain Jobredeaux//    http://www.apache.org/licenses/LICENSE-2.0
8*9e965d6fSRomain Jobredeaux//
9*9e965d6fSRomain Jobredeaux// Unless required by applicable law or agreed to in writing, software
10*9e965d6fSRomain Jobredeaux// distributed under the License is distributed on an "AS IS" BASIS,
11*9e965d6fSRomain Jobredeaux// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9e965d6fSRomain Jobredeaux// See the License for the specific language governing permissions and
13*9e965d6fSRomain Jobredeaux// limitations under the License.
14*9e965d6fSRomain Jobredeaux
15*9e965d6fSRomain Jobredeaux// Package flagfile installs a -flagfile command line flag.
16*9e965d6fSRomain Jobredeaux// This package is only imported for the side effect of installing the flag
17*9e965d6fSRomain Jobredeauxpackage flagfile
18*9e965d6fSRomain Jobredeaux
19*9e965d6fSRomain Jobredeauximport (
20*9e965d6fSRomain Jobredeaux	"bufio"
21*9e965d6fSRomain Jobredeaux	"flag"
22*9e965d6fSRomain Jobredeaux	"fmt"
23*9e965d6fSRomain Jobredeaux	"io"
24*9e965d6fSRomain Jobredeaux	"os"
25*9e965d6fSRomain Jobredeaux	"strings"
26*9e965d6fSRomain Jobredeaux)
27*9e965d6fSRomain Jobredeaux
28*9e965d6fSRomain Jobredeauxtype flagFile string
29*9e965d6fSRomain Jobredeaux
30*9e965d6fSRomain Jobredeauxfunc (f *flagFile) String() string {
31*9e965d6fSRomain Jobredeaux	return string(*f)
32*9e965d6fSRomain Jobredeaux}
33*9e965d6fSRomain Jobredeaux
34*9e965d6fSRomain Jobredeauxfunc (f *flagFile) Get() interface{} {
35*9e965d6fSRomain Jobredeaux	return string(*f)
36*9e965d6fSRomain Jobredeaux}
37*9e965d6fSRomain Jobredeaux
38*9e965d6fSRomain Jobredeauxfunc (f *flagFile) Set(fn string) error {
39*9e965d6fSRomain Jobredeaux	file, err := os.Open(fn)
40*9e965d6fSRomain Jobredeaux	if err != nil {
41*9e965d6fSRomain Jobredeaux		return fmt.Errorf("error parsing flagfile %s: %v", fn, err)
42*9e965d6fSRomain Jobredeaux	}
43*9e965d6fSRomain Jobredeaux	defer file.Close()
44*9e965d6fSRomain Jobredeaux
45*9e965d6fSRomain Jobredeaux	fMap, err := parseFlags(bufio.NewReader(file))
46*9e965d6fSRomain Jobredeaux	if err != nil {
47*9e965d6fSRomain Jobredeaux		return err
48*9e965d6fSRomain Jobredeaux	}
49*9e965d6fSRomain Jobredeaux	for k, v := range fMap {
50*9e965d6fSRomain Jobredeaux		flag.Set(k, v)
51*9e965d6fSRomain Jobredeaux	}
52*9e965d6fSRomain Jobredeaux	return nil
53*9e965d6fSRomain Jobredeaux}
54*9e965d6fSRomain Jobredeaux
55*9e965d6fSRomain Jobredeaux// parseFlags parses the contents is a naive flag file parser.
56*9e965d6fSRomain Jobredeauxfunc parseFlags(r *bufio.Reader) (map[string]string, error) {
57*9e965d6fSRomain Jobredeaux	fMap := make(map[string]string)
58*9e965d6fSRomain Jobredeaux	eof := false
59*9e965d6fSRomain Jobredeaux	for !eof {
60*9e965d6fSRomain Jobredeaux		line, err := r.ReadString('\n')
61*9e965d6fSRomain Jobredeaux		if err != nil && err != io.EOF {
62*9e965d6fSRomain Jobredeaux			return nil, err
63*9e965d6fSRomain Jobredeaux		}
64*9e965d6fSRomain Jobredeaux		if err == io.EOF {
65*9e965d6fSRomain Jobredeaux			eof = true
66*9e965d6fSRomain Jobredeaux		}
67*9e965d6fSRomain Jobredeaux		line = strings.TrimSpace(line)
68*9e965d6fSRomain Jobredeaux		if line == "" {
69*9e965d6fSRomain Jobredeaux			continue
70*9e965d6fSRomain Jobredeaux		}
71*9e965d6fSRomain Jobredeaux		// When Bazel is used to create flag files, it may create entries that are wrapped within
72*9e965d6fSRomain Jobredeaux		// quotations '--a=b'. Verify that it is balanced and strip first and last quotation.
73*9e965d6fSRomain Jobredeaux		if strings.HasPrefix(line, "'") || strings.HasPrefix(line, "\"") {
74*9e965d6fSRomain Jobredeaux			if !strings.HasSuffix(line, line[:1]) {
75*9e965d6fSRomain Jobredeaux				return nil, fmt.Errorf("error parsing flags, found unbalanced quotation marks around flag entry: %s", line)
76*9e965d6fSRomain Jobredeaux			}
77*9e965d6fSRomain Jobredeaux			line = line[1 : len(line)-1]
78*9e965d6fSRomain Jobredeaux		}
79*9e965d6fSRomain Jobredeaux		// Check that the flag has at least 1 "-" but no more than 2 ("-a" or "--a").
80*9e965d6fSRomain Jobredeaux		if !strings.HasPrefix(line, "-") || strings.HasPrefix(line, "---") {
81*9e965d6fSRomain Jobredeaux			return nil, fmt.Errorf("error parsing flags, expected flag start definition ('-' or '--') but, got: %s", line)
82*9e965d6fSRomain Jobredeaux		}
83*9e965d6fSRomain Jobredeaux		split := strings.SplitN(strings.TrimLeft(line, "-"), "=", 2)
84*9e965d6fSRomain Jobredeaux		k := split[0]
85*9e965d6fSRomain Jobredeaux		if len(split) == 2 {
86*9e965d6fSRomain Jobredeaux			fMap[k] = split[1]
87*9e965d6fSRomain Jobredeaux			continue
88*9e965d6fSRomain Jobredeaux		}
89*9e965d6fSRomain Jobredeaux		v, err := parseFlagValue(r)
90*9e965d6fSRomain Jobredeaux		if err != nil {
91*9e965d6fSRomain Jobredeaux			return nil, fmt.Errorf("error parsing flag value, got: %v", err)
92*9e965d6fSRomain Jobredeaux		}
93*9e965d6fSRomain Jobredeaux		fMap[k] = v
94*9e965d6fSRomain Jobredeaux	}
95*9e965d6fSRomain Jobredeaux	return fMap, nil
96*9e965d6fSRomain Jobredeaux}
97*9e965d6fSRomain Jobredeaux
98*9e965d6fSRomain Jobredeauxfunc parseFlagValue(r *bufio.Reader) (string, error) {
99*9e965d6fSRomain Jobredeaux	pBytes, err := r.Peek(2)
100*9e965d6fSRomain Jobredeaux	if err != nil && err != io.EOF {
101*9e965d6fSRomain Jobredeaux		return "", err
102*9e965d6fSRomain Jobredeaux	}
103*9e965d6fSRomain Jobredeaux	peeked := string(pBytes)
104*9e965d6fSRomain Jobredeaux	// If the next line starts with "-", "'-" or '"-' assume it is the beginning of a new flag definition.
105*9e965d6fSRomain Jobredeaux	if strings.HasPrefix(peeked, "-") || peeked == "'-" || peeked == "\"-" {
106*9e965d6fSRomain Jobredeaux		return "", nil
107*9e965d6fSRomain Jobredeaux	}
108*9e965d6fSRomain Jobredeaux	// Next line contains the flag value.
109*9e965d6fSRomain Jobredeaux	line, err := r.ReadString('\n')
110*9e965d6fSRomain Jobredeaux	if err != nil && err != io.EOF {
111*9e965d6fSRomain Jobredeaux		return "", err
112*9e965d6fSRomain Jobredeaux	}
113*9e965d6fSRomain Jobredeaux	return strings.TrimSpace(line), nil
114*9e965d6fSRomain Jobredeaux}
115*9e965d6fSRomain Jobredeaux
116*9e965d6fSRomain Jobredeauxfunc init() {
117*9e965d6fSRomain Jobredeaux	flag.Var(new(flagFile), "flagfile", "Path to flagfile containing flag values, --key=val on each line")
118*9e965d6fSRomain Jobredeaux}
119