X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=main.go;h=eb67c8069fa2534b645368e1504b8b6963bb7853;hb=HEAD;hp=a79d2b130dd9679b6006682553e54c245fe29b35;hpb=9d7519924e635be8f99cd0c7d12f36b06be6f5ef;p=goredo.git diff --git a/main.go b/main.go index a79d2b1..eb67c80 100644 --- a/main.go +++ b/main.go @@ -1,30 +1,29 @@ -/* -goredo -- djb's redo implementation on pure Go -Copyright (C) 2020-2023 Sergey Matveev - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ +// goredo -- djb's redo implementation on pure Go +// Copyright (C) 2020-2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . package main import ( "bufio" "bytes" - "crypto/rand" + "encoding/hex" "errors" "flag" "fmt" "io" + "io/fs" "log" "os" "os/signal" @@ -34,6 +33,7 @@ import ( "strconv" "syscall" + "github.com/google/uuid" "go.cypherpunks.ru/recfile" "golang.org/x/sys/unix" ) @@ -44,6 +44,7 @@ const ( CmdNameRedoAffects = "redo-affects" CmdNameRedoAlways = "redo-always" CmdNameRedoCleanup = "redo-cleanup" + CmdNameRedoDep2Rec = "redo-dep2rec" CmdNameRedoDepFix = "redo-depfix" CmdNameRedoDot = "redo-dot" CmdNameRedoIfchange = "redo-ifchange" @@ -59,7 +60,7 @@ const ( var ( Cwd string - BuildUUID string + BuildUUID uuid.UUID IsTopRedo bool // is it the top redo instance UmaskCur int ) @@ -92,12 +93,23 @@ func main() { var symlinks *bool cmdName := CmdName() - if cmdName == "goredo" { + if cmdName == CmdNameGoredo { symlinks = flag.Bool("symlinks", false, "create necessary symlinks in current directory") } flag.Usage = func() { usage(os.Args[0]) } - flag.Parse() + + BuildUUIDStr := os.Getenv(EnvBuildUUID) + IsTopRedo = BuildUUIDStr == "" + + var args []string + if IsTopRedo { + flag.Parse() + args = flag.Args() + } else { + args = os.Args[1:] + } + if *warranty { fmt.Println(Warranty) return @@ -113,6 +125,7 @@ func main() { CmdNameRedoAffects, CmdNameRedoAlways, CmdNameRedoCleanup, + CmdNameRedoDep2Rec, CmdNameRedoDepFix, CmdNameRedoDot, CmdNameRedoIfchange, @@ -185,7 +198,7 @@ func main() { LogLock = os.Getenv(EnvLogLock) == "1" LogJS = os.Getenv(EnvLogJS) == "1" if Debug || os.Getenv(EnvLogPid) == "1" { - MyPid = os.Getpid() + MyPID = os.Getpid() } var traced bool if flagTraceAll != nil && *flagTraceAll { @@ -277,27 +290,22 @@ func main() { fdDep = mustParseFd(v, EnvDepFd) } - tgts := make([]*Tgt, 0, len(flag.Args())) - for _, arg := range flag.Args() { + tgts := make([]*Tgt, 0, len(args)) + for _, arg := range args { tgts = append(tgts, NewTgt(arg)) } - BuildUUID = os.Getenv(EnvBuildUUID) tgtsWasEmpty := len(tgts) == 0 - if BuildUUID == "" { - IsTopRedo = true - raw := new([16]byte) - if _, err = io.ReadFull(rand.Reader, raw[:]); err != nil { - log.Fatal(err) - } - raw[6] = (raw[6] & 0x0F) | uint8(4<<4) // version 4 - BuildUUID = fmt.Sprintf( - "%x-%x-%x-%x-%x", - raw[0:4], raw[4:6], raw[6:8], raw[8:10], raw[10:], - ) + if BuildUUIDStr == "" { + BuildUUID = uuid.New() if tgtsWasEmpty { tgts = append(tgts, NewTgt("all")) } tracef(CDebug, "inode-trust: %s", InodeTrust) + } else { + BuildUUID, err = uuid.Parse(BuildUUIDStr) + if err != nil { + log.Fatal(err) + } } if cmdName == CmdNameRedo { @@ -320,12 +328,9 @@ func main() { ok := true err = nil - tracef( - CDebug, "[%s] run: %s %s cwd:%s dirprefix:%s", - BuildUUID, cmdName, tgts, Cwd, DirPrefix, - ) + tracef(CDebug, "[%s] run: %s %s cwd:%s dirprefix:%s", + BuildUUID, cmdName, tgts, Cwd, DirPrefix) -CmdSwitch: switch cmdName { case CmdNameRedo: for _, tgt := range tgts { @@ -343,20 +348,22 @@ CmdSwitch: if fdDep == nil { log.Fatalln("no", EnvDepFd) } + fdDepW := bufio.NewWriter(fdDep) for _, tgt := range tgts { - err = ifcreate(fdDep, tgt.RelTo(path.Join(Cwd, DirPrefix))) + err = ifcreate(fdDepW, fdDep.Name(), tgt.RelTo(path.Join(Cwd, DirPrefix))) if err != nil { break } } + err = fdDepW.Flush() case CmdNameRedoAlways: if fdDep == nil { log.Fatalln("no", EnvDepFd) } - err = always(fdDep) + err = always(fdDep, fdDep.Name()) case CmdNameRedoCleanup: for _, what := range tgts { - err = cleanupWalker(Cwd, what.t) + err = cleanupWalker(Cwd, path.Base(what.a)) if err != nil { break } @@ -367,7 +374,12 @@ CmdSwitch: if fdDep == nil { log.Fatalln("no", EnvDepFd) } - err = stamp(fdDep, os.Stdin) + var hsh Hash + hsh, err = fileHash(os.Stdin) + if err != nil { + break + } + err = stamp(fdDep, fdDep.Name(), hsh) case CmdNameRedoLog: if len(tgts) != 1 { log.Fatal("single target expected") @@ -377,52 +389,18 @@ CmdSwitch: if len(tgts) != 1 { log.Fatal("single target expected") } - var fdTmp *os.File - fdTmp, err = os.CreateTemp("", "whichdo") - if err != nil { - err = ErrLine(err) - break - } - err = ErrLine(os.Remove(fdTmp.Name())) + var dos []string + dos, err = whichdo(tgts[0]) if err != nil { - break - } - tgt := tgts[0] - var doFile string - var upLevels int - doFile, upLevels, err = findDo(fdTmp, tgt.h, tgt.t) - if err != nil { - err = ErrLine(err) - break - } - _, err = fdTmp.Seek(0, io.SeekStart) - if err != nil { - err = ErrLine(err) - break - } - r := recfile.NewReader(fdTmp) - for { - m, err := r.NextMap() - if err != nil { - if errors.Is(err, io.EOF) { - break - } - err = ErrLine(err) - break CmdSwitch - } - fmt.Println(cwdMustRel(tgt.h, m["Target"])) - } - if doFile == "" { - ok = false - } else { - p := make([]string, 0, upLevels+2) - p = append(p, tgt.h) - for i := 0; i < upLevels; i++ { - p = append(p, "..") + if errors.Is(err, fs.ErrNotExist) { + err = nil + ok = false + } else { + break } - p = append(p, doFile) - rel := mustRel(Cwd, path.Join(p...)) - fmt.Println(rel) + } + for _, do := range dos { + fmt.Println(do) } case CmdNameRedoTargets: raws := make([]string, 0, len(tgts)) @@ -455,7 +433,7 @@ CmdSwitch: } deps := make(map[string]map[string]*Tgt) for _, tgt := range tgtsKnown { - collectDeps(NewTgt(tgt), 0, deps, true, make(map[string]struct{})) + collectDeps(NewTgt(tgt), 0, deps, true) } seen := make(map[string]*Tgt) for _, tgt := range tgts { @@ -529,6 +507,8 @@ CmdSwitch: fmt.Println(src) } case CmdNameRedoDepFix: + IfchangeCache = nil + DepFixHashCache = make(map[string]Hash) err = depFix(Cwd) case CmdNameRedoInode: var inode *Inode @@ -546,6 +526,68 @@ CmdSwitch: break } } + case CmdNameRedoDep2Rec: + var data []byte + data, err = os.ReadFile(tgts[0].a) + if err != nil { + break + } + var build uuid.UUID + build, data, err = depHeadParse(data) + if err != nil { + break + } + w := bufio.NewWriter(os.Stdout) + err = recfileWrite(w, []recfile.Field{ + {Name: "Build", Value: build.String()}, + }...) + if err != nil { + break + } + var typ byte + var chunk []byte + var inode Inode + for len(data) > 0 { + typ, chunk, data, _ = chunkRead(data) + switch typ { + case DepTypeAlways: + err = recfileWrite(w, []recfile.Field{ + {Name: "Type", Value: "always"}, + }...) + case DepTypeStamp: + err = recfileWrite(w, []recfile.Field{ + {Name: "Type", Value: "stamp"}, + {Name: "Hash", Value: hex.EncodeToString(chunk)}, + }...) + case DepTypeIfcreate: + err = recfileWrite(w, []recfile.Field{ + {Name: "Type", Value: "ifcreate"}, + {Name: "Target", Value: string(chunk)}, + }...) + case DepTypeIfchange: + name := string(chunk[InodeLen+HashLen:]) + meta := chunk[:InodeLen+HashLen] + fields := []recfile.Field{ + {Name: "Type", Value: "ifchange"}, + {Name: "Target", Value: name}, + } + fields = append(fields, recfile.Field{ + Name: "Hash", Value: Hash(meta[InodeLen:]).String(), + }) + inode = Inode(meta[:][:InodeLen]) + fields = append(fields, inode.RecfileFields()...) + err = recfileWrite(w, fields...) + case DepTypeIfchangeNonex: + err = recfileWrite(w, []recfile.Field{ + {Name: "Type", Value: "ifchange"}, + {Name: "Target", Value: string(chunk)}, + }...) + } + if err != nil { + break + } + } + err = w.Flush() default: log.Fatalln("unknown command", cmdName) }