2 goredo -- djb's redo implementation on pure Go
3 Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
33 EnvMakeFlags = "MAKEFLAGS"
35 EnvJSFd = "REDO_JS_FD"
37 EnvJSToken = "REDO_JS_TOKEN"
41 MakeTypeBmake = "bmake"
42 MakeTypeGmake = "gmake"
46 // bmake (NetBSD make)
47 BMakeGoodToken = byte('+')
48 BMakeJSArg = "-j 1 -J "
49 BMakeJSArgRe = regexp.MustCompile(`(.*)\s*-J (\d+),(\d+)\s*(.*)`)
52 GMakeJSArg = "--jobserver-auth="
53 GMakeJSArgRe = regexp.MustCompile(`(.*)\s*--jobserver-auth=(\d+),(\d+)\s*(.*)`)
57 DMakeJSArgRe = regexp.MustCompile(`(.*)\s*(\d+),(\d+)\s*(.*)`)
59 MakeFlagsName = EnvMakeFlags
66 jsToken byte // got via EnvJSToken
75 if !(cmdName == CmdNameRedo || cmdName == CmdNameRedoIfchange) {
78 flagJobs = flag.Int("j", -1,
79 fmt.Sprintf("number of parallel jobs (0=inf, <0=1) (%s)", EnvJobs))
82 func jsStart(jobsEnv string) {
89 jobs = uint64(*flagJobs)
91 jobs, err = strconv.ParseUint(jobsEnv, 10, 64)
93 log.Fatalln("can not parse", EnvJobs, err)
100 JSR, JSW, err = os.Pipe()
104 tracef(CJS, "initial fill with %d", jobs)
105 jsTokens[BMakeGoodToken] = int(jobs)
106 for ; jobs > 0; jobs-- {
107 jsReleaseNoLock(BMakeGoodToken)
112 jsTokens = make(map[byte]int)
114 makeType := os.Getenv(EnvMake)
115 var makeArgRe *regexp.Regexp
118 makeArgRe = GMakeJSArgRe
119 MakeJSArg = GMakeJSArg
121 makeArgRe = BMakeJSArgRe
122 MakeJSArg = BMakeJSArg
126 MakeFlagsName = EnvJSFd
127 makeArgRe = DMakeJSArgRe
128 MakeJSArg = DMakeJSArg
130 log.Fatalln("unknown", EnvMake, "type")
133 MakeFlags = os.Getenv(MakeFlagsName)
134 jobsEnv := os.Getenv(EnvJobs)
136 // jobserver disabled, infinite jobs
140 // we are not running under make
145 match := makeArgRe.FindStringSubmatch(MakeFlags)
147 // MAKEFLAGS does not contain anything related to jobserver
151 MakeFlags = match[1] + " " + match[4]
155 if err := recover(); err != nil {
159 JSR = mustParseFd(match[2], "JSR")
160 JSW = mustParseFd(match[3], "JSW")
163 if token := os.Getenv(EnvJSToken); token != "" {
164 jsTokenInt, err := strconv.ParseUint(token, 10, 8)
166 log.Fatalln("invalid", EnvJSToken, "format:", err)
168 jsToken = byte(jsTokenInt)
170 jsRelease("ifchange entered", jsToken)
174 func jsReleaseNoLock(token byte) {
175 if n, err := JSW.Write([]byte{token}); err != nil || n != 1 {
176 log.Fatalln("can not write JSW:", err)
180 func jsRelease(ctx string, token byte) {
184 tracef(CJS, "release from %s", ctx)
187 jsReleaseNoLock(token)
191 func jsReleaseAll() {
193 for token, i := range jsTokens {
195 jsReleaseNoLock(token)
201 func jsAcquire(ctx string) byte {
203 return BMakeGoodToken
205 tracef(CJS, "acquire for %s", ctx)
207 if n, err := JSR.Read(token); err != nil || n != 1 {
208 log.Fatalln("can not read JSR:", err)
213 tracef(CJS, "acquired for %s", ctx)