github.com/distbuild/reclient@v0.0.0-20240401075343-3de72e395564/internal/pkg/inputprocessor/clangparser/gen_clang_flags/main.go (about) 1 // Copyright 2023 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Binary gen_clang_flags generates clang_flags.go from Options.td json output. 16 // 17 // Usage: 18 // 19 // $ llvm-tblgen \ 20 // -I../llvm/include \ 21 // -I../clang/include/clang/Driver \ 22 // ../clang/include/clang/Driver/Options.td \ 23 // | gen_clang_flags -o clang_flags.go 24 package main 25 26 import ( 27 "bytes" 28 "encoding/json" 29 "flag" 30 "fmt" 31 "go/format" 32 "log" 33 "os" 34 "sort" 35 "text/template" 36 ) 37 38 var ( 39 verbose = flag.Bool("v", false, "verbose") 40 input = flag.String("input", "", "Options.td json path") 41 output = flag.String("o", "clang_flags.go", "output file") 42 ) 43 44 // 45 // Options.td json dump format: 46 // 47 // <key>: { 48 // "Name": <string>, # name of flag. 49 // "Prefixes": [<string>, ..], # prefix of flag. "-", "--", .. 50 // "Kind": {.., "def":<string>, ..} # kind def 51 // "NumArgs": <number>, # number of args for MultiArgs 52 // "Flags": [{.., "def":<string>, ..}, ..], # options 53 // ..., 54 // } 55 // 56 // 57 // kind: see llvm/include/llvm/Option/OptParser.td 58 // // An option group. 59 // def KIND_GROUP : OptionKind<"Group">; 60 // // The input option kind. 61 // def KIND_INPUT : OptionKind<"Input", 1, 1>; 62 // // The unknown option kind. 63 // def KIND_UNKNOWN : OptionKind<"Unknown", 2, 1>; 64 // // A flag with no values. 65 // def KIND_FLAG : OptionKind<"Flag">; 66 // // An option which prefixes its (single) value. 67 // def KIND_JOINED : OptionKind<"Joined", 1>; 68 // // An option which is followed by its value. 69 // def KIND_SEPARATE : OptionKind<"Separate">; 70 // // An option followed by its values, which are separated by commas. 71 // def KIND_COMMAJOINED : OptionKind<"CommaJoined">; 72 // // An option which is which takes multiple (separate) arguments. 73 // def KIND_MULTIARG : OptionKind<"MultiArg">; 74 // // An option which is either joined to its (non-empty) value, or followed by its 75 // // value. 76 // def KIND_JOINED_OR_SEPARATE : OptionKind<"JoinedOrSeparate">; 77 // // An option which is both joined to its (first) value, and followed by its 78 // // (second) value. 79 // def KIND_JOINED_AND_SEPARATE : OptionKind<"JoinedAndSeparate">; 80 // // An option which consumes all remaining arguments if there are any. 81 // def KIND_REMAINING_ARGS : OptionKind<"RemainingArgs">; 82 // // An option which consumes an optional joined argument and any other remaining 83 // // arguments. 84 // def KIND_REMAINING_ARGS_JOINED : OptionKind<"RemainingArgsJoined">; 85 // 86 // flags: see clang/include/clang/Driver/Options.td 87 // // DriverOption - The option is a "driver" option, and should not be forwarded 88 // // to other tools. 89 // def DriverOption : OptionFlag; 90 // 91 // // LinkerInput - The option is a linker input. 92 // def LinkerInput : OptionFlag; 93 // 94 // // NoArgumentUnused - Don't report argument unused warnings for this option; this 95 // // is useful for options like -static or -dynamic which a user may always end up 96 // // passing, even if the platform defaults to (or only supports) that option. 97 // def NoArgumentUnused : OptionFlag; 98 // 99 // // Unsupported - The option is unsupported, and the driver will reject command 100 // // lines that use it. 101 // def Unsupported : OptionFlag; 102 // 103 // // Ignored - The option is unsupported, and the driver will silently ignore it. 104 // def Ignored : OptionFlag; 105 // 106 // // CoreOption - This is considered a "core" Clang option, available in both 107 // // clang and clang-cl modes. 108 // def CoreOption : OptionFlag; 109 // 110 // // CLOption - This is a cl.exe compatibility option. Options with this flag 111 // // are made available when the driver is running in CL compatibility mode. 112 // def CLOption : OptionFlag; 113 // 114 // // CC1Option - This option should be accepted by clang -cc1. 115 // def CC1Option : OptionFlag; 116 // 117 // // CC1AsOption - This option should be accepted by clang -cc1as. 118 // def CC1AsOption : OptionFlag; 119 // 120 // // NoDriverOption - This option should not be accepted by the driver. 121 // def NoDriverOption : OptionFlag; 122 123 var ( 124 tmpl = template.Must(template.New("").Parse(` 125 package clangparser 126 127 import "github.com/bazelbuild/reclient/internal/pkg/inputprocessor/args" 128 129 // DO NOT EDIT: this is autogenerated by 130 // {{.Generator}} 131 132 var ( 133 // ClangOptions is clang's flag options. 134 ClangOptions = map[string]int{ 135 {{range $k, $v := .Clang.Options -}} 136 {{printf "%q" $k}}: {{$v}}, 137 {{end -}} 138 } 139 140 // ClangPrefixes is clang's prefix flag options. 141 ClangPrefixes = []args.PrefixOption{ 142 {{range .Clang.Prefixes -}} 143 {Prefix: {{printf "%q" .Prefix}}, NumArgs: {{.NumArgs}}}, 144 {{end -}} 145 } 146 147 // ClangNormalizedFlags is clang's normalized flags. 148 ClangNormalizedFlags = map[string]string{ 149 {{range $k, $v := .Clang.NormalizedFlags -}} 150 {{printf "%q" $k}}: {{printf "%q" $v}}, 151 {{end -}} 152 } 153 154 // ClangCLOptions is clang's flag options. 155 ClangCLOptions = map[string]int{ 156 {{range $k, $v := .ClangCL.Options -}} 157 {{printf "%q" $k}}: {{$v}}, 158 {{end -}} 159 } 160 161 // ClangPrefixes is clang's prefix flag options. 162 ClangCLPrefixes = []args.PrefixOption{ 163 {{range .ClangCL.Prefixes -}} 164 {Prefix: {{printf "%q" .Prefix}}, NumArgs: {{.NumArgs}}}, 165 {{end -}} 166 } 167 168 // ClangNormalizedFlags is clang's normalized flags. 169 ClangCLNormalizedFlags = map[string]string{ 170 {{range $k, $v := .ClangCL.NormalizedFlags -}} 171 {{printf "%q" $k}}: {{printf "%q" $v}}, 172 {{end -}} 173 } 174 175 ) 176 `)) 177 ) 178 179 func main() { 180 flag.Parse() 181 if *input == "" { 182 log.Fatalf("need to specify --input /path/to/clang_options.json") 183 } 184 content, err := os.ReadFile(*input) 185 if err != nil { 186 log.Fatalf("read %s: %v", *input, err) 187 } 188 m := make(map[string]interface{}) 189 err = json.Unmarshal(content, &m) 190 if err != nil { 191 log.Fatalf("unmarshal json %s: %v", *input, err) 192 } 193 options, err := parseOptions(m) 194 if err != nil { 195 log.Fatalf("parse error %s: %v", *input, err) 196 } 197 var buf bytes.Buffer 198 err = tmpl.Execute(&buf, options) 199 if err != nil { 200 log.Fatalf("template error %v", err) 201 } 202 source, err := format.Source(buf.Bytes()) 203 if err != nil { 204 log.Fatalf("fmt error: %v\n%s", err, buf.Bytes()) 205 } 206 err = os.WriteFile(*output, source, 0644) 207 if err != nil { 208 log.Fatalf("write %s: %v", *output, err) 209 } 210 } 211 212 type prefixOption struct { 213 Prefix string 214 NumArgs int 215 } 216 217 // OptionDefs holds options definition of the command. 218 type OptionDefs struct { 219 // Options is for 220 // KIND_FLAG (value=0) 221 // KIND_SEPARATE (value=1) 222 // KIND_MULTIARG (value=NumArgs) 223 // KIND_JOINED_OR_SEPARATE (value=1) 224 // KIND_REMAINING_ARGS (value < 0) 225 Options map[string]int 226 227 // Prefixes is for 228 // KIND_COMMAJOINED, KIND_JOINED, KIND_JOINED_OR_SEPARATE (NumArgs = 0) 229 // KIND_JOINED_AND_SEPARETE (NumArgs = 1) 230 // KIND_REMAINING_ARGS_JOINED (NumArgs < 1). 231 Prefixes []prefixOption 232 233 // NormalizedFlags is a map to flag to a normalized flag 234 // for flag that have several flag prefixes. e.g. "/" and "-". 235 NormalizedFlags map[string]string 236 } 237 238 // Options holds clang and clang-cl options definition. 239 type Options struct { 240 // Generator is command line of generator. 241 Generator []string 242 243 // Clang includes flags except CLOption, NoDriverOption. 244 Clang OptionDefs 245 246 // ClangCL includes CLOption and CoreOption. 247 ClangCL OptionDefs 248 } 249 250 func parseOptions(m map[string]interface{}) (Options, error) { 251 options := Options{ 252 Generator: os.Args, 253 Clang: OptionDefs{ 254 Options: make(map[string]int), 255 NormalizedFlags: make(map[string]string), 256 }, 257 ClangCL: OptionDefs{ 258 Options: make(map[string]int), 259 NormalizedFlags: make(map[string]string), 260 }, 261 } 262 for key, val := range m { 263 v, ok := val.(map[string]interface{}) 264 if !ok { 265 if *verbose { 266 log.Printf("key %q is unexpected type: %T", key, val) 267 } 268 continue 269 } 270 if !isOptionClass(v) { 271 if *verbose { 272 log.Printf("key %s is not Option class: %v", key, v) 273 } 274 continue 275 } 276 o, err := parseOpt(v) 277 if err != nil { 278 return options, fmt.Errorf("invalid option for key %s: %v", key, err) 279 } 280 if *verbose { 281 log.Printf("key %s option %v", key, o) 282 } 283 if len(o.visibility) > 0 && !contains(o.visibility, "DefaultVis") && !contains(o.visibility, "CLOption") { 284 if *verbose { 285 log.Printf("key %s skip ", key) 286 } 287 continue 288 } 289 switch { 290 case contains(o.visibility, "DefaultVis"): 291 err = setOption(&options.Clang, o) 292 if err != nil { 293 return options, fmt.Errorf("set %v in clang: %v", o, err) 294 } 295 err = setOption(&options.ClangCL, o) 296 if err != nil { 297 return options, fmt.Errorf("set %v in clang-cl: %v", o, err) 298 } 299 case contains(o.visibility, "CLOption"): 300 err = setOption(&options.ClangCL, o) 301 if err != nil { 302 return options, fmt.Errorf("set %v in clang-cl: %v", o, err) 303 } 304 default: 305 err = setOption(&options.Clang, o) 306 if err != nil { 307 return options, fmt.Errorf("set %v in clang: %v", o, err) 308 } 309 } 310 } 311 sort.Slice(options.Clang.Prefixes, func(i, j int) bool { 312 return options.Clang.Prefixes[i].Prefix > options.Clang.Prefixes[j].Prefix 313 }) 314 sort.Slice(options.ClangCL.Prefixes, func(i, j int) bool { 315 return options.ClangCL.Prefixes[i].Prefix > options.ClangCL.Prefixes[j].Prefix 316 }) 317 return options, nil 318 } 319 320 func setOption(od *OptionDefs, o opt) error { 321 for _, prefix := range o.prefixes { 322 flag := prefix + o.name 323 if *verbose { 324 log.Printf("set %q %s (%d)", flag, o.kind, o.numArgs) 325 } 326 switch o.kind { 327 case "KIND_COMMAJOINED": 328 od.Prefixes = append(od.Prefixes, prefixOption{flag, 0}) 329 case "KIND_FLAG": 330 od.Options[flag] = 0 331 case "KIND_JOINED": 332 od.Prefixes = append(od.Prefixes, prefixOption{flag, 0}) 333 case "KIND_JOINED_AND_SEPARATE": 334 od.Prefixes = append(od.Prefixes, prefixOption{flag, 1}) 335 case "KIND_JOINED_OR_SEPARATE": 336 od.Options[flag] = 1 337 od.Prefixes = append(od.Prefixes, prefixOption{flag, 0}) 338 case "KIND_MULTIARG": 339 od.Options[flag] = o.numArgs 340 case "KIND_SEPARATE": 341 od.Options[flag] = 1 342 case "KIND_REMAINING_ARGS": 343 od.Options[flag] = -1 344 case "KIND_REMAINING_ARGS_JOINED": 345 od.Prefixes = append(od.Prefixes, prefixOption{flag, -1}) 346 default: 347 return fmt.Errorf("unknown kind %q in %v", o.kind, o) 348 } 349 } 350 if len(o.prefixes) > 1 { 351 // ["/", "-"] => normalized to "-" 352 normalizedFlag := o.prefixes[len(o.prefixes)-1] + o.name 353 for _, prefix := range o.prefixes[:len(o.prefixes)-1] { 354 flag := prefix + o.name 355 od.NormalizedFlags[flag] = normalizedFlag 356 } 357 } 358 return nil 359 } 360 361 // helpers 362 363 type opt struct { 364 kind string 365 name string 366 prefixes []string 367 numArgs int 368 visibility []string 369 } 370 371 func parseOpt(m map[string]interface{}) (opt, error) { 372 var o opt 373 var ok bool 374 o.kind, ok = getDef(m, "Kind") 375 if !ok { 376 return o, fmt.Errorf("`Kind` not found in %v", m) 377 } 378 o.name, ok = getString(m, "Name") 379 if !ok { 380 return o, fmt.Errorf("`Name` not found in %v", m) 381 } 382 o.prefixes, ok = getStrings(m, "Prefixes") 383 if !ok { 384 return o, fmt.Errorf("`Prefixes` not found in %v", m) 385 } 386 o.numArgs, _ = getInt(m, "NumArgs") 387 switch o.kind { 388 case "KIND_JOINED_AND_SEPARATE", "KIND_SEPARATE": 389 o.numArgs = 1 390 case "KIND_REMAINING_ARGS", "KIND_REMAINING_ARGS_JOIND": 391 o.numArgs = -1 392 } 393 o.visibility, ok = getDefs(m, "Visibility") 394 if !ok { 395 return o, fmt.Errorf("`Visibility` not found in %v", m) 396 } 397 return o, nil 398 } 399 400 func isOptionClass(m map[string]interface{}) bool { 401 superclasses, ok := getStrings(m, "!superclasses") 402 if !ok { 403 return false 404 } 405 if !contains(superclasses, "Option") { 406 return false 407 } 408 return true 409 } 410 411 func getString(m map[string]interface{}, key string) (string, bool) { 412 v, ok := m[key].(string) 413 return v, ok 414 } 415 416 func getInt(m map[string]interface{}, key string) (int, bool) { 417 v, ok := m[key].(float64) 418 return int(v), ok 419 } 420 421 func getStrings(m map[string]interface{}, key string) ([]string, bool) { 422 v, ok := m[key].([]interface{}) 423 if !ok { 424 return nil, false 425 } 426 var ret []string 427 for _, e := range v { 428 s, ok := e.(string) 429 if !ok { 430 return nil, false 431 } 432 ret = append(ret, s) 433 } 434 return ret, true 435 } 436 437 func getDef(m map[string]interface{}, key string) (string, bool) { 438 v, ok := m[key].(map[string]interface{}) 439 if !ok { 440 return "", false 441 } 442 def, ok := v["def"].(string) 443 if !ok { 444 return "", false 445 } 446 return def, true 447 } 448 449 func getDefs(m map[string]interface{}, key string) ([]string, bool) { 450 vs, ok := m[key].([]interface{}) 451 if !ok { 452 return nil, false 453 } 454 var defs []string 455 for _, v := range vs { 456 vm, ok := v.(map[string]interface{}) 457 if !ok { 458 return nil, false 459 } 460 def, ok := vm["def"].(string) 461 if !ok { 462 return nil, false 463 } 464 defs = append(defs, def) 465 } 466 return defs, true 467 } 468 469 func contains(list []string, s string) bool { 470 for _, v := range list { 471 if v == s { 472 return true 473 } 474 } 475 return false 476 }