1*760c253cSXin Li// Copyright 2019 The ChromiumOS Authors 2*760c253cSXin Li// Use of this source code is governed by a BSD-style license that can be 3*760c253cSXin Li// found in the LICENSE file. 4*760c253cSXin Li 5*760c253cSXin Lipackage main 6*760c253cSXin Li 7*760c253cSXin Liimport ( 8*760c253cSXin Li "errors" 9*760c253cSXin Li "fmt" 10*760c253cSXin Li "os" 11*760c253cSXin Li "strings" 12*760c253cSXin Li) 13*760c253cSXin Li 14*760c253cSXin Livar errNoSuchCmdlineArg = errors.New("no such commandline argument") 15*760c253cSXin Li 16*760c253cSXin Li// Removes one flag from `builder`, assuming that a value follows the flag. Two formats are 17*760c253cSXin Li// supported for this: `--foo=bar` and `--foo bar`. In either case, "bar" will be returned as the 18*760c253cSXin Li// `value`. 19*760c253cSXin Li// 20*760c253cSXin Li// If no flag is found on the commandline, this returns the `errNoSuchCmdlineArg` error. `builder` 21*760c253cSXin Li// is unmodified if this error is returned, but its contents are unspecified if any other error is 22*760c253cSXin Li// returned. 23*760c253cSXin Li// 24*760c253cSXin Li// In the case of multiple such flags, only the first encountered will be removed. 25*760c253cSXin Lifunc removeOneUserCmdlineFlagWithValue(builder *commandBuilder, flagName string) (flagValue string, err error) { 26*760c253cSXin Li const ( 27*760c253cSXin Li searchingForFlag uint8 = iota 28*760c253cSXin Li searchingForValue 29*760c253cSXin Li searchComplete 30*760c253cSXin Li ) 31*760c253cSXin Li 32*760c253cSXin Li flagRequiresAValue := func() error { return newUserErrorf("flag %q requires a value", flagName) } 33*760c253cSXin Li searchState := searchingForFlag 34*760c253cSXin Li builder.transformArgs(func(arg builderArg) string { 35*760c253cSXin Li if err != nil { 36*760c253cSXin Li return arg.value 37*760c253cSXin Li } 38*760c253cSXin Li 39*760c253cSXin Li switch searchState { 40*760c253cSXin Li case searchingForFlag: 41*760c253cSXin Li if !arg.fromUser { 42*760c253cSXin Li return arg.value 43*760c253cSXin Li } 44*760c253cSXin Li 45*760c253cSXin Li if arg.value == flagName { 46*760c253cSXin Li searchState = searchingForValue 47*760c253cSXin Li return "" 48*760c253cSXin Li } 49*760c253cSXin Li 50*760c253cSXin Li isArgEq := strings.HasPrefix(arg.value, flagName) && arg.value[len(flagName)] == '=' 51*760c253cSXin Li if !isArgEq { 52*760c253cSXin Li return arg.value 53*760c253cSXin Li } 54*760c253cSXin Li 55*760c253cSXin Li flagValue = arg.value[len(flagName)+1:] 56*760c253cSXin Li searchState = searchComplete 57*760c253cSXin Li return "" 58*760c253cSXin Li 59*760c253cSXin Li case searchingForValue: 60*760c253cSXin Li if !arg.fromUser { 61*760c253cSXin Li err = flagRequiresAValue() 62*760c253cSXin Li return arg.value 63*760c253cSXin Li } 64*760c253cSXin Li 65*760c253cSXin Li flagValue = arg.value 66*760c253cSXin Li searchState = searchComplete 67*760c253cSXin Li return "" 68*760c253cSXin Li 69*760c253cSXin Li case searchComplete: 70*760c253cSXin Li return arg.value 71*760c253cSXin Li 72*760c253cSXin Li default: 73*760c253cSXin Li panic(fmt.Sprintf("unknown search state: %v", searchState)) 74*760c253cSXin Li } 75*760c253cSXin Li }) 76*760c253cSXin Li 77*760c253cSXin Li if err != nil { 78*760c253cSXin Li return "", err 79*760c253cSXin Li } 80*760c253cSXin Li 81*760c253cSXin Li switch searchState { 82*760c253cSXin Li case searchingForFlag: 83*760c253cSXin Li return "", errNoSuchCmdlineArg 84*760c253cSXin Li 85*760c253cSXin Li case searchingForValue: 86*760c253cSXin Li return "", flagRequiresAValue() 87*760c253cSXin Li 88*760c253cSXin Li case searchComplete: 89*760c253cSXin Li return flagValue, nil 90*760c253cSXin Li 91*760c253cSXin Li default: 92*760c253cSXin Li panic(fmt.Sprintf("unknown search state: %v", searchState)) 93*760c253cSXin Li } 94*760c253cSXin Li} 95*760c253cSXin Li 96*760c253cSXin Lifunc processGomaCccFlags(builder *commandBuilder, inheritFromEnv bool) (gomaUsed bool, err error) { 97*760c253cSXin Li gomaPath, err := removeOneUserCmdlineFlagWithValue(builder, "--gomacc-path") 98*760c253cSXin Li if err != nil && err != errNoSuchCmdlineArg { 99*760c253cSXin Li return false, err 100*760c253cSXin Li } 101*760c253cSXin Li 102*760c253cSXin Li if inheritFromEnv && (err == errNoSuchCmdlineArg || gomaPath == "") { 103*760c253cSXin Li gomaPath, _ = builder.env.getenv("GOMACC_PATH") 104*760c253cSXin Li } 105*760c253cSXin Li 106*760c253cSXin Li if gomaPath != "" { 107*760c253cSXin Li if _, err := os.Lstat(gomaPath); err == nil { 108*760c253cSXin Li builder.wrapPath(gomaPath) 109*760c253cSXin Li return true, nil 110*760c253cSXin Li } 111*760c253cSXin Li } 112*760c253cSXin Li return false, nil 113*760c253cSXin Li} 114*760c253cSXin Li 115*760c253cSXin Lifunc processRewrapperCcFlags(builder *commandBuilder) (rewrapperUsed bool, err error) { 116*760c253cSXin Li rewrapperPath, pathErr := removeOneUserCmdlineFlagWithValue(builder, "--rewrapper-path") 117*760c253cSXin Li if pathErr != nil && pathErr != errNoSuchCmdlineArg { 118*760c253cSXin Li return false, err 119*760c253cSXin Li } 120*760c253cSXin Li 121*760c253cSXin Li rewrapperCfg, cfgErr := removeOneUserCmdlineFlagWithValue(builder, "--rewrapper-cfg") 122*760c253cSXin Li if cfgErr != nil && cfgErr != errNoSuchCmdlineArg { 123*760c253cSXin Li return false, err 124*760c253cSXin Li } 125*760c253cSXin Li 126*760c253cSXin Li if pathErr == errNoSuchCmdlineArg { 127*760c253cSXin Li if cfgErr != errNoSuchCmdlineArg { 128*760c253cSXin Li return false, newUserErrorf("--rewrapper-path must be specified if --rewrapper-cfg is") 129*760c253cSXin Li } 130*760c253cSXin Li return false, nil 131*760c253cSXin Li } 132*760c253cSXin Li 133*760c253cSXin Li if cfgErr == errNoSuchCmdlineArg { 134*760c253cSXin Li return false, newUserErrorf("--rewrapper-cfg must be specified if --rewrapper-path is") 135*760c253cSXin Li } 136*760c253cSXin Li 137*760c253cSXin Li // It's unclear that we should have a similar fallback to gomacc if --rewrapper-path doesn't 138*760c253cSXin Li // exist, so don't until it's obviously necessary. 139*760c253cSXin Li builder.wrapPath(rewrapperPath, "-cfg", rewrapperCfg) 140*760c253cSXin Li return true, nil 141*760c253cSXin Li} 142*760c253cSXin Li 143*760c253cSXin Lifunc processRemoteBuildFlags(builder *commandBuilder) (remoteBuildUsed bool, err error) { 144*760c253cSXin Li rewrapperUsed, err := processRewrapperCcFlags(builder) 145*760c253cSXin Li if err != nil { 146*760c253cSXin Li return rewrapperUsed, err 147*760c253cSXin Li } 148*760c253cSXin Li 149*760c253cSXin Li inheritGomaFromEnv := !rewrapperUsed 150*760c253cSXin Li gomaUsed, err := processGomaCccFlags(builder, inheritGomaFromEnv) 151*760c253cSXin Li remoteBuildUsed = gomaUsed || rewrapperUsed 152*760c253cSXin Li if err != nil { 153*760c253cSXin Li return remoteBuildUsed, err 154*760c253cSXin Li } 155*760c253cSXin Li 156*760c253cSXin Li if gomaUsed && rewrapperUsed { 157*760c253cSXin Li return true, newUserErrorf("rewrapper and gomacc are mutually exclusive") 158*760c253cSXin Li } 159*760c253cSXin Li return remoteBuildUsed, nil 160*760c253cSXin Li} 161