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