fork of https://github.com/sourcegraph/zoekt
0

Configure Feed

Select the types of activity you want to include in your feed.

ctags: allow binary to be anything with validation (#652)

Previously we enforced the binary was called universal-ctags. However,
we let users override the name of the binary, so if they override it we
should use it. To prevent footguns, we now validate the binary was built
with the interactive feature.

In the case of scip-ctags we use the old validation of just checking the
name.

Additionally we fix a bug that was introduced where if symbols are
optional we continue if parsing fails.

Test Plan: indexed with and without universal-ctags. Ctags on my mbp
points to something from xcode which wouldn't work

$ CTAGS_COMMAND=ctags go run ./cmd/zoekt-git-index -require_ctags .
2023/10/04 17:08:12 indexGitRepo(/Users/keegan/src/github.com/sourcegraph/zoekt, delta=false): build.NewBuilder: ctags.NewParserMap: ctags binary is not universal-ctags or is not compiled with +interactive feature: bin=ctags
exit status 1

$ CTAGS_COMMAND=universal-ctags go run ./cmd/zoekt-git-index -require_ctags .
2023/10/04 17:08:29 finished github.com%2Fsourcegraph%2Fzoekt_v16.00000.zoekt: 8657338 index bytes (overhead 2.9)

$ CTAGS_COMMAND=ctags go run ./cmd/zoekt-git-index .
2023/10/04 17:08:40 finished github.com%2Fsourcegraph%2Fzoekt_v16.00000.zoekt: 8538246 index bytes (overhead 2.9)

+41 -13
+4
build/ctags.go
··· 53 53 parser := parserMap[parserKind] 54 54 if parser == nil { 55 55 parser = parserMap[ctags.UniversalCTags] 56 + if parser == nil { 57 + // this happens if CTagsMustSucceed is not true and we didn't find universal-ctags 58 + continue 59 + } 56 60 } 57 61 58 62 es, err := parser.Parse(doc.Name, doc.Content)
+35 -11
ctags/json.go
··· 15 15 package ctags 16 16 17 17 import ( 18 + "bytes" 18 19 "fmt" 19 20 "log" 20 21 "os" 22 + "os/exec" 21 23 "strings" 22 24 "sync" 23 25 "time" ··· 119 121 120 122 // NewParser creates a parser that is implemented by the given 121 123 // universal-ctags binary. The parser is safe for concurrent use. 122 - func NewParser(bin string) (Parser, error) { 123 - if strings.Contains(bin, "universal-ctags") || strings.Contains(bin, "scip-ctags") { 124 - opts := goctags.Options{ 125 - Bin: bin, 124 + func NewParser(parserType CTagsParserType, bin string) (Parser, error) { 125 + if err := checkBinary(parserType, bin); err != nil { 126 + return nil, err 127 + } 128 + 129 + opts := goctags.Options{ 130 + Bin: bin, 131 + } 132 + if debug { 133 + opts.Info = log.New(os.Stderr, "CTAGS INF: ", log.LstdFlags) 134 + opts.Debug = log.New(os.Stderr, "CTAGS DBG: ", log.LstdFlags) 135 + } 136 + return &lockedParser{ 137 + opts: opts, 138 + }, nil 139 + } 140 + 141 + // checkBinary does checks on bin to ensure we can correctly use the binary 142 + // for symbols. It is more user friendly to fail early in this case. 143 + func checkBinary(typ CTagsParserType, bin string) error { 144 + switch typ { 145 + case UniversalCTags: 146 + helpOutput, err := exec.Command(bin, "--help").CombinedOutput() 147 + if err != nil { 148 + return fmt.Errorf("failed to check if %s is universal-ctags: %w\n--help output:\n%s", bin, err, string(helpOutput)) 126 149 } 127 - if debug { 128 - opts.Info = log.New(os.Stderr, "CTAGS INF: ", log.LstdFlags) 129 - opts.Debug = log.New(os.Stderr, "CTAGS DBG: ", log.LstdFlags) 150 + if !bytes.Contains(helpOutput, []byte("+interactive")) { 151 + return fmt.Errorf("ctags binary is not universal-ctags or is not compiled with +interactive feature: bin=%s", bin) 152 + } 153 + 154 + case ScipCTags: 155 + if !strings.Contains(bin, "scip-ctags") { 156 + return fmt.Errorf("only supports scip-ctags, not %s", bin) 130 157 } 131 - return &lockedParser{ 132 - opts: opts, 133 - }, nil 134 158 } 135 159 136 - return nil, fmt.Errorf("only supports universal-ctags, not %s", bin) 160 + return nil 137 161 }
+1 -1
ctags/json_test.go
··· 27 27 t.Skip(err) 28 28 } 29 29 30 - p, err := NewParser("universal-ctags") 30 + p, err := NewParser(UniversalCTags, "universal-ctags") 31 31 if err != nil { 32 32 t.Fatal("newProcess", err) 33 33 }
+1 -1
ctags/parser_map.go
··· 66 66 for _, parserType := range []CTagsParserType{UniversalCTags, ScipCTags} { 67 67 bin := bins[parserType] 68 68 if bin != "" { 69 - parser, err := NewParser(bin) 69 + parser, err := NewParser(parserType, bin) 70 70 71 71 if err != nil && cTagsMustSucceed { 72 72 return nil, fmt.Errorf("ctags.NewParserMap: %v", err)