• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 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
15// Package flagfile installs a -flagfile command line flag.
16// This package is only imported for the side effect of installing the flag
17package flagfile
18
19import (
20	"bufio"
21	"flag"
22	"fmt"
23	"io"
24	"os"
25	"strings"
26)
27
28type flagFile string
29
30func (f *flagFile) String() string {
31	return string(*f)
32}
33
34func (f *flagFile) Get() interface{} {
35	return string(*f)
36}
37
38func (f *flagFile) Set(fn string) error {
39	file, err := os.Open(fn)
40	if err != nil {
41		return fmt.Errorf("error parsing flagfile %s: %v", fn, err)
42	}
43	defer file.Close()
44
45	fMap, err := parseFlags(bufio.NewReader(file))
46	if err != nil {
47		return err
48	}
49	for k, v := range fMap {
50		flag.Set(k, v)
51	}
52	return nil
53}
54
55// parseFlags parses the contents is a naive flag file parser.
56func parseFlags(r *bufio.Reader) (map[string]string, error) {
57	fMap := make(map[string]string)
58	eof := false
59	for !eof {
60		line, err := r.ReadString('\n')
61		if err != nil && err != io.EOF {
62			return nil, err
63		}
64		if err == io.EOF {
65			eof = true
66		}
67		line = strings.TrimSpace(line)
68		if line == "" {
69			continue
70		}
71		// When Bazel is used to create flag files, it may create entries that are wrapped within
72		// quotations '--a=b'. Verify that it is balanced and strip first and last quotation.
73		if strings.HasPrefix(line, "'") || strings.HasPrefix(line, "\"") {
74			if !strings.HasSuffix(line, line[:1]) {
75				return nil, fmt.Errorf("error parsing flags, found unbalanced quotation marks around flag entry: %s", line)
76			}
77			line = line[1 : len(line)-1]
78		}
79		// Check that the flag has at least 1 "-" but no more than 2 ("-a" or "--a").
80		if !strings.HasPrefix(line, "-") || strings.HasPrefix(line, "---") {
81			return nil, fmt.Errorf("error parsing flags, expected flag start definition ('-' or '--') but, got: %s", line)
82		}
83		split := strings.SplitN(strings.TrimLeft(line, "-"), "=", 2)
84		k := split[0]
85		if len(split) == 2 {
86			fMap[k] = split[1]
87			continue
88		}
89		v, err := parseFlagValue(r)
90		if err != nil {
91			return nil, fmt.Errorf("error parsing flag value, got: %v", err)
92		}
93		fMap[k] = v
94	}
95	return fMap, nil
96}
97
98func parseFlagValue(r *bufio.Reader) (string, error) {
99	pBytes, err := r.Peek(2)
100	if err != nil && err != io.EOF {
101		return "", err
102	}
103	peeked := string(pBytes)
104	// If the next line starts with "-", "'-" or '"-' assume it is the beginning of a new flag definition.
105	if strings.HasPrefix(peeked, "-") || peeked == "'-" || peeked == "\"-" {
106		return "", nil
107	}
108	// Next line contains the flag value.
109	line, err := r.ReadString('\n')
110	if err != nil && err != io.EOF {
111		return "", err
112	}
113	return strings.TrimSpace(line), nil
114}
115
116func init() {
117	flag.Var(new(flagFile), "flagfile", "Path to flagfile containing flag values, --key=val on each line")
118}
119