// Copyright 2019 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package main import ( "errors" "fmt" "os" "strings" ) var errNoSuchCmdlineArg = errors.New("no such commandline argument") // Removes one flag from `builder`, assuming that a value follows the flag. Two formats are // supported for this: `--foo=bar` and `--foo bar`. In either case, "bar" will be returned as the // `value`. // // If no flag is found on the commandline, this returns the `errNoSuchCmdlineArg` error. `builder` // is unmodified if this error is returned, but its contents are unspecified if any other error is // returned. // // In the case of multiple such flags, only the first encountered will be removed. func removeOneUserCmdlineFlagWithValue(builder *commandBuilder, flagName string) (flagValue string, err error) { const ( searchingForFlag uint8 = iota searchingForValue searchComplete ) flagRequiresAValue := func() error { return newUserErrorf("flag %q requires a value", flagName) } searchState := searchingForFlag builder.transformArgs(func(arg builderArg) string { if err != nil { return arg.value } switch searchState { case searchingForFlag: if !arg.fromUser { return arg.value } if arg.value == flagName { searchState = searchingForValue return "" } isArgEq := strings.HasPrefix(arg.value, flagName) && arg.value[len(flagName)] == '=' if !isArgEq { return arg.value } flagValue = arg.value[len(flagName)+1:] searchState = searchComplete return "" case searchingForValue: if !arg.fromUser { err = flagRequiresAValue() return arg.value } flagValue = arg.value searchState = searchComplete return "" case searchComplete: return arg.value default: panic(fmt.Sprintf("unknown search state: %v", searchState)) } }) if err != nil { return "", err } switch searchState { case searchingForFlag: return "", errNoSuchCmdlineArg case searchingForValue: return "", flagRequiresAValue() case searchComplete: return flagValue, nil default: panic(fmt.Sprintf("unknown search state: %v", searchState)) } } func processGomaCccFlags(builder *commandBuilder, inheritFromEnv bool) (gomaUsed bool, err error) { gomaPath, err := removeOneUserCmdlineFlagWithValue(builder, "--gomacc-path") if err != nil && err != errNoSuchCmdlineArg { return false, err } if inheritFromEnv && (err == errNoSuchCmdlineArg || gomaPath == "") { gomaPath, _ = builder.env.getenv("GOMACC_PATH") } if gomaPath != "" { if _, err := os.Lstat(gomaPath); err == nil { builder.wrapPath(gomaPath) return true, nil } } return false, nil } func processRewrapperCcFlags(builder *commandBuilder) (rewrapperUsed bool, err error) { rewrapperPath, pathErr := removeOneUserCmdlineFlagWithValue(builder, "--rewrapper-path") if pathErr != nil && pathErr != errNoSuchCmdlineArg { return false, err } rewrapperCfg, cfgErr := removeOneUserCmdlineFlagWithValue(builder, "--rewrapper-cfg") if cfgErr != nil && cfgErr != errNoSuchCmdlineArg { return false, err } if pathErr == errNoSuchCmdlineArg { if cfgErr != errNoSuchCmdlineArg { return false, newUserErrorf("--rewrapper-path must be specified if --rewrapper-cfg is") } return false, nil } if cfgErr == errNoSuchCmdlineArg { return false, newUserErrorf("--rewrapper-cfg must be specified if --rewrapper-path is") } // It's unclear that we should have a similar fallback to gomacc if --rewrapper-path doesn't // exist, so don't until it's obviously necessary. builder.wrapPath(rewrapperPath, "-cfg", rewrapperCfg) return true, nil } func processRemoteBuildFlags(builder *commandBuilder) (remoteBuildUsed bool, err error) { rewrapperUsed, err := processRewrapperCcFlags(builder) if err != nil { return rewrapperUsed, err } inheritGomaFromEnv := !rewrapperUsed gomaUsed, err := processGomaCccFlags(builder, inheritGomaFromEnv) remoteBuildUsed = gomaUsed || rewrapperUsed if err != nil { return remoteBuildUsed, err } if gomaUsed && rewrapperUsed { return true, newUserErrorf("rewrapper and gomacc are mutually exclusive") } return remoteBuildUsed, nil }