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  }