musl.go raw

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