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  }