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