2 goredo -- djb's redo implementation on pure Go
3 Copyright (C) 2020-2021 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) {
87 } else if *flagJobs > 0 {
88 jobs = uint64(*flagJobs)
89 } else if jobsEnv != "" {
90 jobs, err = strconv.ParseUint(jobsEnv, 10, 64)
92 log.Fatalln("can not parse", EnvJobs, err)
99 JSR, JSW, err = os.Pipe()
103 trace(CJS, "initial fill with %d", jobs)
104 jsTokens[BMakeGoodToken] = int(jobs)
105 for ; jobs > 0; jobs-- {
106 jsReleaseNoLock(BMakeGoodToken)
111 jsTokens = make(map[byte]int)
113 makeType := os.Getenv(EnvMake)
114 var makeArgRe *regexp.Regexp
117 makeArgRe = GMakeJSArgRe
118 MakeJSArg = GMakeJSArg
120 makeArgRe = BMakeJSArgRe
121 MakeJSArg = BMakeJSArg
125 MakeFlagsName = EnvJSFd
126 makeArgRe = DMakeJSArgRe
127 MakeJSArg = DMakeJSArg
129 log.Fatalln("unknown", EnvMake, "type")
132 MakeFlags = os.Getenv(MakeFlagsName)
133 jobsEnv := os.Getenv(EnvJobs)
135 // jobserver disabled, infinite jobs
139 // we are not running under make
144 match := makeArgRe.FindStringSubmatch(MakeFlags)
146 // MAKEFLAGS does not contain anything related to jobserver
150 MakeFlags = match[1] + " " + match[4]
154 if err := recover(); err != nil {
158 JSR = mustParseFd(match[2], "JSR")
159 JSW = mustParseFd(match[3], "JSW")
162 if token := os.Getenv(EnvJSToken); token != "" {
163 jsTokenInt, err := strconv.ParseUint(token, 10, 8)
165 log.Fatalln("invalid", EnvJSToken, "format:", err)
167 jsToken = byte(jsTokenInt)
169 jsRelease("ifchange entered", jsToken)
173 func jsReleaseNoLock(token byte) {
174 if n, err := JSW.Write([]byte{token}); err != nil || n != 1 {
175 log.Fatalln("can not write JSW:", err)
179 func jsRelease(ctx string, token byte) {
183 trace(CJS, "release from %s", ctx)
186 jsReleaseNoLock(token)
190 func jsReleaseAll() {
192 for token, i := range jsTokens {
194 jsReleaseNoLock(token)
200 func jsAcquire(ctx string) byte {
202 return BMakeGoodToken
204 trace(CJS, "acquire for %s", ctx)
206 if n, err := JSR.Read(token); err != nil || n != 1 {
207 log.Fatalln("can not read JSR:", err)
212 trace(CJS, "acquired for %s", ctx)