modernc.org/cc@v1.0.1/v2/headers/generator.go (about)

     1  // Copyright 2017 The CC Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build ignore
     6  
     7  package main
     8  
     9  import (
    10  	"bytes"
    11  	"flag"
    12  	"fmt"
    13  	"go/format"
    14  	"go/token"
    15  	"io/ioutil"
    16  	"os"
    17  	"path/filepath"
    18  	"runtime"
    19  	"sort"
    20  	"strings"
    21  
    22  	"modernc.org/cc/v2"
    23  	"modernc.org/ir"
    24  	"modernc.org/sortutil"
    25  	"modernc.org/strutil"
    26  	"modernc.org/xc"
    27  )
    28  
    29  var (
    30  	goOS   = runtime.GOOS
    31  	goArch = runtime.GOARCH
    32  	osArch = fmt.Sprintf("%s_%s", goOS, goArch)
    33  
    34  	crt  string
    35  	dict = xc.Dict
    36  
    37  	need = map[string][]string{ // "goos_goarch" or "goos" or "all": list of headers
    38  		"all": {
    39  			"alloca.h",
    40  			"assert.h",
    41  			"ctype.h",
    42  			"errno.h",
    43  			"fcntl.h",
    44  			"float.h",
    45  			"inttypes.h",
    46  			"limits.h",
    47  			"locale.h",
    48  			"malloc.h",
    49  			"math.h",
    50  			"setjmp.h",
    51  			"stdarg.h",
    52  			"stdbool.h",
    53  			"stddef.h",
    54  			"stdint.h",
    55  			"stdio.h",
    56  			"stdlib.h",
    57  			"string.h",
    58  			"strings.h",
    59  			"sys/stat.h",
    60  			"sys/time.h",
    61  			"sys/wait.h",
    62  			"time.h",
    63  			"unistd.h",
    64  			"wchar.h",
    65  			"zlib.h",
    66  		},
    67  		"linux": {
    68  			"arpa/inet.h",
    69  			"dirent.h",
    70  			"dlfcn.h",
    71  			"execinfo.h",
    72  			"fts.h",
    73  			"getopt.h",
    74  			"grp.h",
    75  			"memory.h",
    76  			"netdb.h",
    77  			"pthread.h",
    78  			"pwd.h",
    79  			"sched.h",
    80  			"signal.h",
    81  			"signal.h",
    82  			"sys/file.h",
    83  			"sys/ioctl.h",
    84  			"sys/mman.h",
    85  			"sys/param.h",
    86  			"sys/resource.h",
    87  			"sys/select.h",
    88  			"sys/socket.h",
    89  			"sys/statfs.h",
    90  			"sys/times.h",
    91  			"sys/types.h",
    92  			"sys/uio.h",
    93  			"sys/un.h",
    94  			"sys/utsname.h",
    95  			"termios.h",
    96  			"utime.h",
    97  		},
    98  		"windows": {
    99  			"io.h",
   100  			"process.h",
   101  			"windows.h",
   102  		},
   103  	}
   104  )
   105  
   106  func main() {
   107  	oCRT := flag.String("crt", "", "CRT import path to update")
   108  	flag.Parse()
   109  	if s := *oCRT; s != "" {
   110  		var err error
   111  		if crt, err = findRepo(s); err != nil {
   112  			panic(err)
   113  		}
   114  	}
   115  	sys := predefined()
   116  	var want []string
   117  	for k, v := range need {
   118  		switch {
   119  		case
   120  			k == "all",
   121  			k == goOS,
   122  			k == osArch:
   123  
   124  			want = append(want, v...)
   125  		}
   126  	}
   127  	want = want[:sortutil.Dedupe(sort.StringSlice(want))]
   128  	var got []string
   129  	tweaks := &cc.Tweaks{
   130  		EnableAnonymousStructFields: true,
   131  		EnableEmptyStructs:          true,
   132  		TrackIncludes: func(s string) {
   133  			if strings.HasSuffix(s, "builtin.h") || strings.HasSuffix(s, "predefined.h") {
   134  				return
   135  			}
   136  
   137  			got = append(got, s)
   138  		},
   139  	}
   140  	for _, v := range want {
   141  		tu, err := cc.Translate(
   142  			tweaks,
   143  			append([]string{"@"}, sys...),
   144  			sys,
   145  			cc.NewStringSource("main.c", fmt.Sprintf(`
   146  #include "./%s/builtin.h"
   147  
   148  // Output of gcc features.c && ./a.out in modernc.org/cc/v2/headers on linux_amd64.
   149  #define _POSIX_SOURCE 1
   150  #define _POSIX_C_SOURCE 200809
   151  #define _DEFAULT_SOURCE 1
   152  
   153  
   154  #include "%v"
   155  `, osArch, v)),
   156  		)
   157  		if err != nil {
   158  			panic(cc.ErrString(err))
   159  		}
   160  
   161  		if crt != "" {
   162  			defs(v, tu)
   163  		}
   164  	}
   165  	got = got[:sortutil.Dedupe(sort.StringSlice(got))]
   166  	for _, v := range got {
   167  		b, err := ioutil.ReadFile(v)
   168  		if err != nil {
   169  			panic(cc.ErrString(err))
   170  		}
   171  
   172  		f := filepath.Join(osArch, v)
   173  		mkdir(filepath.Dir(f))
   174  		if err := ioutil.WriteFile(f, b, 0644); err != nil {
   175  			panic(cc.ErrString(err))
   176  		}
   177  	}
   178  }
   179  
   180  func predefined() []string {
   181  	predefined, inc, sys, err := cc.HostConfig("-std=c99")
   182  	if err != nil {
   183  		panic(cc.ErrString(err))
   184  	}
   185  
   186  	if len(inc) != 0 {
   187  		panic(inc)
   188  	}
   189  
   190  	a := strings.Split(predefined, "\n")
   191  	w := 0
   192  	for _, v := range a {
   193  		v = strings.TrimSpace(v)
   194  		if s := strings.ToLower(v); strings.Contains(s, "gcc") || strings.Contains(s, "gnu") {
   195  			continue
   196  		}
   197  
   198  		a[w] = v
   199  		w++
   200  	}
   201  	a = a[:w]
   202  	osArch = fmt.Sprintf("%s_%s", goOS, goArch)
   203  	mkdir(osArch)
   204  	b := bytes.NewBufferString(`// Code generated by $ go generate - DO NOT EDIT.
   205  
   206  // +build ignore
   207  	
   208  `)
   209  	for _, v := range a {
   210  		fmt.Fprintln(b, v)
   211  	}
   212  
   213  	if err := ioutil.WriteFile(filepath.Join(osArch, "predefined.h"), b.Bytes(), 0644); err != nil {
   214  		panic(cc.ErrString(err))
   215  	}
   216  
   217  	b.Reset()
   218  	m := map[string]struct{}{}
   219  	w = 0
   220  	for _, v := range sys {
   221  		v := filepath.Clean(v)
   222  		if _, ok := m[v]; !ok {
   223  			fmt.Fprintf(b, "%s\n", v)
   224  			sys[w] = v
   225  			w++
   226  		}
   227  		m[v] = struct{}{}
   228  	}
   229  	sys = sys[:w]
   230  	if err := ioutil.WriteFile(filepath.Join(osArch, "paths"), b.Bytes(), 0664); err != nil {
   231  		panic(cc.ErrString(err))
   232  	}
   233  
   234  	return sys
   235  }
   236  
   237  func mkdir(p string) {
   238  	if _, err := os.Stat(p); err != nil {
   239  		if !os.IsNotExist(err) {
   240  			panic(cc.ErrString(err))
   241  		}
   242  
   243  		if err := os.MkdirAll(p, 0775); err != nil {
   244  			panic(fmt.Errorf("%q: %v", p, err))
   245  		}
   246  	}
   247  }
   248  
   249  func findRepo(s string) (string, error) {
   250  	s = filepath.FromSlash(s)
   251  	for _, v := range strings.Split(strutil.Gopath(), string(os.PathListSeparator)) {
   252  		p := filepath.Join(v, "src", s)
   253  		fi, err := os.Lstat(p)
   254  		if err != nil {
   255  			continue
   256  		}
   257  
   258  		if fi.IsDir() {
   259  			wd, err := os.Getwd()
   260  			if err != nil {
   261  				return "", err
   262  			}
   263  
   264  			if p, err = filepath.Rel(wd, p); err != nil {
   265  				return "", err
   266  			}
   267  
   268  			if p, err = filepath.Abs(p); err != nil {
   269  				return "", err
   270  			}
   271  
   272  			return p, nil
   273  		}
   274  	}
   275  	return "", fmt.Errorf("%q: cannot find repository", s)
   276  }
   277  
   278  func defs(include string, tu *cc.TranslationUnit) {
   279  	model, err := cc.NewModel()
   280  	if err != nil {
   281  		panic(err)
   282  	}
   283  
   284  	var a []string
   285  	for _, v := range tu.Macros {
   286  		if v.IsFnLike {
   287  			continue
   288  		}
   289  
   290  		from := tu.FileSet.Position(v.DefTok.Pos()).Filename
   291  		if from == "" || strings.Contains(from, "predefined.h") || strings.Contains(from, "builtin.h") {
   292  			continue
   293  		}
   294  
   295  		nm := string(dict.S(v.DefTok.Val))
   296  		if strings.HasPrefix(nm, "__") {
   297  			continue
   298  		}
   299  
   300  		if strings.HasPrefix(nm, "_") && len(nm) > 1 {
   301  			if c := nm[1]; c >= 'A' && c <= 'Z' {
   302  				continue
   303  			}
   304  		}
   305  
   306  		a = append(a, nm)
   307  	}
   308  	sort.Strings(a)
   309  	fn := include[:len(include)-2] // sans .h
   310  	dir := filepath.Join(crt, filepath.FromSlash(fn))
   311  	fn = filepath.Join(dir, fmt.Sprintf("const_%s_%s.go", runtime.GOOS, runtime.GOARCH))
   312  	pn := filepath.Base(dir)
   313  	if token.Lookup(pn).IsKeyword() {
   314  		pn += "_"
   315  	}
   316  	buf := bytes.NewBufferString(fmt.Sprintf(`// Code generated by $ go generate - DO NOT EDIT.
   317  
   318  package %s
   319  
   320  const (`, pn))
   321  	for _, nm := range a {
   322  		v := tu.Macros[dict.SID(nm)]
   323  		op, err := v.Eval(model, tu.Macros)
   324  		if err != nil {
   325  			continue
   326  		}
   327  
   328  		buf.WriteByte('\n')
   329  		switch x := op.Value.(type) {
   330  		case *ir.Float32Value:
   331  			fmt.Fprintf(buf, "X%s = %v", nm, x.Value)
   332  		case *ir.Float64Value:
   333  			fmt.Fprintf(buf, "X%s = %v", nm, x.Value)
   334  		case *ir.Int64Value:
   335  			switch {
   336  			case op.Type.IsUnsigned():
   337  				fmt.Fprintf(buf, "X%s = %v", nm, uint64(cc.ConvertInt64(x.Value, op.Type, model)))
   338  			default:
   339  				fmt.Fprintf(buf, "X%s = %v", nm, x.Value)
   340  			}
   341  		case *ir.StringValue:
   342  			fmt.Fprintf(buf, "X%s = %q", nm, dict.S(int(x.StringID)))
   343  		default:
   344  			panic(fmt.Errorf("%T", x))
   345  		}
   346  	}
   347  	a = a[:0]
   348  	for _, v := range tu.FileScope.Idents {
   349  		switch x := v.(type) {
   350  		case *cc.EnumerationConstant:
   351  			nm := string(dict.S(x.Token.Val))
   352  			if strings.HasPrefix(nm, "__") {
   353  				continue
   354  			}
   355  
   356  			a = append(a, string(dict.S(x.Token.Val)))
   357  		}
   358  	}
   359  	sort.Strings(a)
   360  	if len(a) != 0 {
   361  		buf.WriteByte('\n')
   362  	}
   363  	for _, nm := range a {
   364  		op := tu.FileScope.Idents[dict.SID(nm)].(*cc.EnumerationConstant).Operand
   365  		x := op.Value.(*ir.Int64Value)
   366  		buf.WriteByte('\n')
   367  		switch {
   368  		case op.Type.IsUnsigned():
   369  			fmt.Fprintf(buf, "C%s = %v", nm, uint64(cc.ConvertInt64(x.Value, op.Type, model)))
   370  		default:
   371  			fmt.Fprintf(buf, "C%s = %v", nm, x.Value)
   372  		}
   373  	}
   374  	buf.WriteString("\n)\n")
   375  	b, err := format.Source(buf.Bytes())
   376  	if err != nil {
   377  		fmt.Printf("%s\n", buf.Bytes())
   378  		panic(err)
   379  	}
   380  	mkdir(dir)
   381  	if err := ioutil.WriteFile(fn, b, 0644); err != nil {
   382  		panic(err)
   383  	}
   384  }