github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/builder/musl.go (about) 1 package builder 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "path/filepath" 8 "regexp" 9 "strings" 10 11 "github.com/tinygo-org/tinygo/compileopts" 12 "github.com/tinygo-org/tinygo/goenv" 13 ) 14 15 var Musl = Library{ 16 name: "musl", 17 makeHeaders: func(target, includeDir string) error { 18 bits := filepath.Join(includeDir, "bits") 19 err := os.Mkdir(bits, 0777) 20 if err != nil { 21 return err 22 } 23 24 arch := compileopts.MuslArchitecture(target) 25 muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "musl") 26 27 // Create the file alltypes.h. 28 f, err := os.Create(filepath.Join(bits, "alltypes.h")) 29 if err != nil { 30 return err 31 } 32 infiles := []string{ 33 filepath.Join(muslDir, "arch", arch, "bits", "alltypes.h.in"), 34 filepath.Join(muslDir, "include", "alltypes.h.in"), 35 } 36 for _, infile := range infiles { 37 data, err := os.ReadFile(infile) 38 if err != nil { 39 return err 40 } 41 lines := strings.Split(string(data), "\n") 42 for _, line := range lines { 43 if strings.HasPrefix(line, "TYPEDEF ") { 44 matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line) 45 value := matches[1] 46 name := matches[2] 47 line = fmt.Sprintf("#if defined(__NEED_%s) && !defined(__DEFINED_%s)\ntypedef %s %s;\n#define __DEFINED_%s\n#endif\n", name, name, value, name, name) 48 } 49 if strings.HasPrefix(line, "STRUCT ") { 50 matches := regexp.MustCompile(`STRUCT * ([^ ]*) (.*);`).FindStringSubmatch(line) 51 name := matches[1] 52 value := matches[2] 53 line = fmt.Sprintf("#if defined(__NEED_struct_%s) && !defined(__DEFINED_struct_%s)\nstruct %s %s;\n#define __DEFINED_struct_%s\n#endif\n", name, name, name, value, name) 54 } 55 f.WriteString(line + "\n") 56 } 57 } 58 f.Close() 59 60 // Create the file syscall.h. 61 f, err = os.Create(filepath.Join(bits, "syscall.h")) 62 if err != nil { 63 return err 64 } 65 data, err := os.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in")) 66 if err != nil { 67 return err 68 } 69 _, err = f.Write(bytes.ReplaceAll(data, []byte("__NR_"), []byte("SYS_"))) 70 if err != nil { 71 return err 72 } 73 f.Close() 74 75 return nil 76 }, 77 cflags: func(target, headerPath string) []string { 78 arch := compileopts.MuslArchitecture(target) 79 muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl") 80 cflags := []string{ 81 "-std=c99", // same as in musl 82 "-D_XOPEN_SOURCE=700", // same as in musl 83 // Musl triggers some warnings and we don't want to show any 84 // warnings while compiling (only errors or silence), so disable 85 // specific warnings that are triggered in musl. 86 "-Werror", 87 "-Wno-logical-op-parentheses", 88 "-Wno-bitwise-op-parentheses", 89 "-Wno-shift-op-parentheses", 90 "-Wno-ignored-attributes", 91 "-Wno-string-plus-int", 92 "-Wno-ignored-pragmas", 93 "-Wno-tautological-constant-out-of-range-compare", 94 "-Wno-deprecated-non-prototype", 95 "-Qunused-arguments", 96 // Select include dirs. Don't include standard library includes 97 // (that would introduce host dependencies and other complications), 98 // but do include all the include directories expected by musl. 99 "-nostdlibinc", 100 "-I" + muslDir + "/arch/" + arch, 101 "-I" + muslDir + "/arch/generic", 102 "-I" + muslDir + "/src/include", 103 "-I" + muslDir + "/src/internal", 104 "-I" + headerPath, 105 "-I" + muslDir + "/include", 106 "-fno-stack-protector", 107 } 108 return cflags 109 }, 110 sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") }, 111 librarySources: func(target string) ([]string, error) { 112 arch := compileopts.MuslArchitecture(target) 113 globs := []string{ 114 "env/*.c", 115 "errno/*.c", 116 "exit/*.c", 117 "internal/defsysinfo.c", 118 "internal/libc.c", 119 "internal/syscall_ret.c", 120 "internal/vdso.c", 121 "legacy/*.c", 122 "linux/*.c", 123 "malloc/*.c", 124 "malloc/mallocng/*.c", 125 "mman/*.c", 126 "math/*.c", 127 "signal/*.c", 128 "stdio/*.c", 129 "string/*.c", 130 "thread/" + arch + "/*.s", 131 "thread/*.c", 132 "time/*.c", 133 "unistd/*.c", 134 } 135 if arch == "arm" { 136 // These files need to be added to the start for some reason. 137 globs = append([]string{"thread/arm/*.c"}, globs...) 138 } 139 140 var sources []string 141 seenSources := map[string]struct{}{} 142 basepath := goenv.Get("TINYGOROOT") + "/lib/musl/src/" 143 for _, pattern := range globs { 144 matches, err := filepath.Glob(basepath + pattern) 145 if err != nil { 146 // From the documentation: 147 // > Glob ignores file system errors such as I/O errors reading 148 // > directories. The only possible returned error is 149 // > ErrBadPattern, when pattern is malformed. 150 // So the only possible error is when the (statically defined) 151 // pattern is wrong. In other words, a programming bug. 152 return nil, fmt.Errorf("musl: could not glob source dirs: %w", err) 153 } 154 if len(matches) == 0 { 155 return nil, fmt.Errorf("musl: did not find any files for pattern %#v", pattern) 156 } 157 for _, match := range matches { 158 relpath, err := filepath.Rel(basepath, match) 159 if err != nil { 160 // Not sure if this is even possible. 161 return nil, err 162 } 163 // Make sure architecture specific files override generic files. 164 id := strings.ReplaceAll(relpath, "/"+arch+"/", "/") 165 if _, ok := seenSources[id]; ok { 166 // Already seen this file, skipping this (generic) file. 167 continue 168 } 169 seenSources[id] = struct{}{} 170 sources = append(sources, relpath) 171 } 172 } 173 return sources, nil 174 }, 175 crt1Source: "../crt/crt1.c", // lib/musl/crt/crt1.c 176 }