xref: /aosp_15_r20/external/toolchain-utils/compiler_wrapper/remote_build_flags.go (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
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