blob: 7a5a765185f8732b17476278dc3769defacbcb12 [file] [log] [blame]
// Copyright 2019 The ChromiumOS Authors
// 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
}