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 }