builder: refactor clang include headers
Set -resource-dir in a central place instead of passing the header path around everywhere and adding it using the `-I` flag. I believe this is closer to how Clang is intended to be used. This change was inspired by my attempt to add a Nix flake file to TinyGo.
Этот коммит содержится в:
		
							родитель
							
								
									c2f1965e03
								
							
						
					
					
						коммит
						d801d0cd53
					
				
					 14 изменённых файлов: 116 добавлений и 148 удалений
				
			
		|  | @ -211,7 +211,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe | ||||||
| 	defer machine.Dispose() | 	defer machine.Dispose() | ||||||
| 
 | 
 | ||||||
| 	// Load entire program AST into memory. | 	// Load entire program AST into memory. | ||||||
| 	lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{ | 	lprogram, err := loader.Load(config, pkgName, types.Config{ | ||||||
| 		Sizes: compiler.Sizes(machine), | 		Sizes: compiler.Sizes(machine), | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | @ -662,7 +662,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe | ||||||
| 		job := &compileJob{ | 		job := &compileJob{ | ||||||
| 			description: "compile extra file " + path, | 			description: "compile extra file " + path, | ||||||
| 			run: func(job *compileJob) error { | 			run: func(job *compileJob) error { | ||||||
| 				result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.Options.PrintCommands) | 				result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(false), config.Options.PrintCommands) | ||||||
| 				job.result = result | 				job.result = result | ||||||
| 				return err | 				return err | ||||||
| 			}, | 			}, | ||||||
|  |  | ||||||
|  | @ -8,7 +8,6 @@ import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/tinygo-org/tinygo/compileopts" | 	"github.com/tinygo-org/tinygo/compileopts" | ||||||
| 	"github.com/tinygo-org/tinygo/goenv" |  | ||||||
| 	"tinygo.org/x/go-llvm" | 	"tinygo.org/x/go-llvm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -74,7 +73,6 @@ func TestClangAttributes(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| func testClangAttributes(t *testing.T, options *compileopts.Options) { | func testClangAttributes(t *testing.T, options *compileopts.Options) { | ||||||
| 	testDir := t.TempDir() | 	testDir := t.TempDir() | ||||||
| 	clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) |  | ||||||
| 
 | 
 | ||||||
| 	ctx := llvm.NewContext() | 	ctx := llvm.NewContext() | ||||||
| 	defer ctx.Dispose() | 	defer ctx.Dispose() | ||||||
|  | @ -86,7 +84,6 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) { | ||||||
| 	config := compileopts.Config{ | 	config := compileopts.Config{ | ||||||
| 		Options: options, | 		Options: options, | ||||||
| 		Target:  target, | 		Target:  target, | ||||||
| 		ClangHeaders: clangHeaderPath, |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Create a very simple C input file. | 	// Create a very simple C input file. | ||||||
|  | @ -98,7 +95,7 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) { | ||||||
| 
 | 
 | ||||||
| 	// Compile this file using Clang. | 	// Compile this file using Clang. | ||||||
| 	outpath := filepath.Join(testDir, "test.bc") | 	outpath := filepath.Join(testDir, "test.bc") | ||||||
| 	flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags()...) | 	flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags(false)...) | ||||||
| 	if config.GOOS() == "darwin" { | 	if config.GOOS() == "darwin" { | ||||||
| 		// Silence some warnings that happen when testing GOOS=darwin on | 		// Silence some warnings that happen when testing GOOS=darwin on | ||||||
| 		// something other than MacOS. | 		// something other than MacOS. | ||||||
|  |  | ||||||
|  | @ -33,13 +33,10 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) { | ||||||
| 		return nil, fmt.Errorf("requires go version 1.18 through 1.21, got go%d.%d", major, minor) | 		return nil, fmt.Errorf("requires go version 1.18 through 1.21, got go%d.%d", major, minor) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) |  | ||||||
| 
 |  | ||||||
| 	return &compileopts.Config{ | 	return &compileopts.Config{ | ||||||
| 		Options:        options, | 		Options:        options, | ||||||
| 		Target:         spec, | 		Target:         spec, | ||||||
| 		GoMinorVersion: minor, | 		GoMinorVersion: minor, | ||||||
| 		ClangHeaders:   clangHeaderPath, |  | ||||||
| 		TestConfig:     options.TestConfig, | 		TestConfig:     options.TestConfig, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										105
									
								
								builder/env.go
									
										
									
									
									
								
							
							
						
						
									
										105
									
								
								builder/env.go
									
										
									
									
									
								
							|  | @ -1,105 +0,0 @@ | ||||||
| package builder |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"io/fs" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"os" |  | ||||||
| 	"os/exec" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"sort" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"tinygo.org/x/go-llvm" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // getClangHeaderPath returns the path to the built-in Clang headers. It tries |  | ||||||
| // multiple locations, which should make it find the directory when installed in |  | ||||||
| // various ways. |  | ||||||
| func getClangHeaderPath(TINYGOROOT string) string { |  | ||||||
| 	// Check whether we're running from the source directory. |  | ||||||
| 	path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers") |  | ||||||
| 	if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) { |  | ||||||
| 		return path |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Check whether we're running from the installation directory. |  | ||||||
| 	path = filepath.Join(TINYGOROOT, "lib", "clang", "include") |  | ||||||
| 	if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) { |  | ||||||
| 		return path |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// It looks like we are built with a system-installed LLVM. Do a last |  | ||||||
| 	// attempt: try to use Clang headers relative to the clang binary. |  | ||||||
| 	llvmMajor := strings.Split(llvm.Version, ".")[0] |  | ||||||
| 	for _, cmdName := range commands["clang"] { |  | ||||||
| 		binpath, err := exec.LookPath(cmdName) |  | ||||||
| 		if err == nil { |  | ||||||
| 			// This should be the command that will also be used by |  | ||||||
| 			// execCommand. To avoid inconsistencies, make sure we use the |  | ||||||
| 			// headers relative to this command. |  | ||||||
| 			binpath, err = filepath.EvalSymlinks(binpath) |  | ||||||
| 			if err != nil { |  | ||||||
| 				// Unexpected. |  | ||||||
| 				return "" |  | ||||||
| 			} |  | ||||||
| 			// Example executable: |  | ||||||
| 			//     /usr/lib/llvm-9/bin/clang |  | ||||||
| 			// Example include path: |  | ||||||
| 			//     /usr/lib/llvm-9/lib64/clang/9.0.1/include/ |  | ||||||
| 			llvmRoot := filepath.Dir(filepath.Dir(binpath)) |  | ||||||
| 			clangVersionRoot := filepath.Join(llvmRoot, "lib64", "clang") |  | ||||||
| 			dirs64, err64 := ioutil.ReadDir(clangVersionRoot) |  | ||||||
| 			// Example include path: |  | ||||||
| 			//     /usr/lib/llvm-9/lib/clang/9.0.1/include/ |  | ||||||
| 			clangVersionRoot = filepath.Join(llvmRoot, "lib", "clang") |  | ||||||
| 			dirs32, err32 := ioutil.ReadDir(clangVersionRoot) |  | ||||||
| 			if err64 != nil && err32 != nil { |  | ||||||
| 				// Unexpected. |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			dirnames := make([]string, len(dirs64)+len(dirs32)) |  | ||||||
| 			dirCount := 0 |  | ||||||
| 			for _, d := range dirs32 { |  | ||||||
| 				name := d.Name() |  | ||||||
| 				if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") { |  | ||||||
| 					dirnames[dirCount] = filepath.Join(llvmRoot, "lib", "clang", name) |  | ||||||
| 					dirCount++ |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			for _, d := range dirs64 { |  | ||||||
| 				name := d.Name() |  | ||||||
| 				if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") { |  | ||||||
| 					dirnames[dirCount] = filepath.Join(llvmRoot, "lib64", "clang", name) |  | ||||||
| 					dirCount++ |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			sort.Strings(dirnames) |  | ||||||
| 			// Check for the highest version first. |  | ||||||
| 			for i := dirCount - 1; i >= 0; i-- { |  | ||||||
| 				path := filepath.Join(dirnames[i], "include") |  | ||||||
| 				_, err := os.Stat(filepath.Join(path, "stdint.h")) |  | ||||||
| 				if err == nil { |  | ||||||
| 					return path |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// On Arch Linux, the clang executable is stored in /usr/bin rather than being symlinked from there. |  | ||||||
| 	// Search directly in /usr/lib for clang. |  | ||||||
| 	if matches, err := filepath.Glob("/usr/lib/clang/" + llvmMajor + ".*.*"); err == nil { |  | ||||||
| 		// Check for the highest version first. |  | ||||||
| 		sort.Strings(matches) |  | ||||||
| 		for i := len(matches) - 1; i >= 0; i-- { |  | ||||||
| 			path := filepath.Join(matches[i], "include") |  | ||||||
| 			_, err := os.Stat(filepath.Join(path, "stdint.h")) |  | ||||||
| 			if err == nil { |  | ||||||
| 				return path |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Could not find it. |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  | @ -143,6 +143,10 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ | ||||||
| 	// reproducible. Otherwise the temporary directory is stored in the archive | 	// reproducible. Otherwise the temporary directory is stored in the archive | ||||||
| 	// itself, which varies each run. | 	// itself, which varies each run. | ||||||
| 	args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir) | 	args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir) | ||||||
|  | 	resourceDir := goenv.ClangResourceDir(false) | ||||||
|  | 	if resourceDir != "" { | ||||||
|  | 		args = append(args, "-resource-dir="+resourceDir) | ||||||
|  | 	} | ||||||
| 	cpu := config.CPU() | 	cpu := config.CPU() | ||||||
| 	if cpu != "" { | 	if cpu != "" { | ||||||
| 		// X86 has deprecated the -mcpu flag, so we need to use -march instead. | 		// X86 has deprecated the -mcpu flag, so we need to use -march instead. | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| package builder | package builder | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
| 
 | 
 | ||||||
|  | @ -12,11 +11,6 @@ import ( | ||||||
| func runCCompiler(flags ...string) error { | func runCCompiler(flags ...string) error { | ||||||
| 	if hasBuiltinTools { | 	if hasBuiltinTools { | ||||||
| 		// Compile this with the internal Clang compiler. | 		// Compile this with the internal Clang compiler. | ||||||
| 		headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) |  | ||||||
| 		if headerPath == "" { |  | ||||||
| 			return errors.New("could not locate Clang headers") |  | ||||||
| 		} |  | ||||||
| 		flags = append(flags, "-I"+headerPath) |  | ||||||
| 		cmd := exec.Command(os.Args[0], append([]string{"clang"}, flags...)...) | 		cmd := exec.Command(os.Args[0], append([]string{"clang"}, flags...)...) | ||||||
| 		cmd.Stdout = os.Stdout | 		cmd.Stdout = os.Stdout | ||||||
| 		cmd.Stderr = os.Stderr | 		cmd.Stderr = os.Stderr | ||||||
|  |  | ||||||
|  | @ -165,7 +165,7 @@ func GoBytes(ptr unsafe.Pointer, length C.int) []byte { | ||||||
| // functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file | // functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file | ||||||
| // hashes of the accessed C header files. If there is one or more error, it | // hashes of the accessed C header files. If there is one or more error, it | ||||||
| // returns these in the []error slice but still modifies the AST. | // returns these in the []error slice but still modifies the AST. | ||||||
| func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) { | func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string) (*ast.File, []string, []string, []string, map[string][]byte, []error) { | ||||||
| 	p := &cgoPackage{ | 	p := &cgoPackage{ | ||||||
| 		currentDir:      dir, | 		currentDir:      dir, | ||||||
| 		importPath:      importPath, | 		importPath:      importPath, | ||||||
|  | @ -292,9 +292,6 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl | ||||||
| 	// have better alternatives anyway. | 	// have better alternatives anyway. | ||||||
| 	cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...) | 	cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...) | ||||||
| 	cflagsForCGo = append(cflagsForCGo, p.cflags...) | 	cflagsForCGo = append(cflagsForCGo, p.cflags...) | ||||||
| 	if clangHeaders != "" { |  | ||||||
| 		cflagsForCGo = append(cflagsForCGo, "-isystem", clangHeaders) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	// Retrieve types such as C.int, C.longlong, etc from C. | 	// Retrieve types such as C.int, C.longlong, etc from C. | ||||||
| 	p.newCGoFile(nil, -1).readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) { | 	p.newCGoFile(nil, -1).readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) { | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ func TestCGo(t *testing.T) { | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// Process the AST with CGo. | 			// Process the AST with CGo. | ||||||
| 			cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "") | 			cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags) | ||||||
| 
 | 
 | ||||||
| 			// Check the AST for type errors. | 			// Check the AST for type errors. | ||||||
| 			var typecheckErrors []error | 			var typecheckErrors []error | ||||||
|  |  | ||||||
|  | @ -19,7 +19,6 @@ type Config struct { | ||||||
| 	Options        *Options | 	Options        *Options | ||||||
| 	Target         *TargetSpec | 	Target         *TargetSpec | ||||||
| 	GoMinorVersion int | 	GoMinorVersion int | ||||||
| 	ClangHeaders   string // Clang built-in header include path |  | ||||||
| 	TestConfig     TestConfig | 	TestConfig     TestConfig | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -259,11 +258,19 @@ func (c *Config) DefaultBinaryExtension() string { | ||||||
| 
 | 
 | ||||||
| // CFlags returns the flags to pass to the C compiler. This is necessary for CGo | // CFlags returns the flags to pass to the C compiler. This is necessary for CGo | ||||||
| // preprocessing. | // preprocessing. | ||||||
| func (c *Config) CFlags() []string { | func (c *Config) CFlags(libclang bool) []string { | ||||||
| 	var cflags []string | 	var cflags []string | ||||||
| 	for _, flag := range c.Target.CFlags { | 	for _, flag := range c.Target.CFlags { | ||||||
| 		cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT"))) | 		cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT"))) | ||||||
| 	} | 	} | ||||||
|  | 	resourceDir := goenv.ClangResourceDir(libclang) | ||||||
|  | 	if resourceDir != "" { | ||||||
|  | 		// The resoure directory contains the built-in clang headers like | ||||||
|  | 		// stdbool.h, stdint.h, float.h, etc. | ||||||
|  | 		// It is left empty if we're using an external compiler (that already | ||||||
|  | 		// knows these headers). | ||||||
|  | 		cflags = append(cflags, "-resource-dir="+resourceDir) | ||||||
|  | 	} | ||||||
| 	switch c.Target.Libc { | 	switch c.Target.Libc { | ||||||
| 	case "darwin-libSystem": | 	case "darwin-libSystem": | ||||||
| 		root := goenv.Get("TINYGOROOT") | 		root := goenv.Get("TINYGOROOT") | ||||||
|  | @ -275,8 +282,8 @@ func (c *Config) CFlags() []string { | ||||||
| 		picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc") | 		picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc") | ||||||
| 		path, _ := c.LibcPath("picolibc") | 		path, _ := c.LibcPath("picolibc") | ||||||
| 		cflags = append(cflags, | 		cflags = append(cflags, | ||||||
| 			"--sysroot="+path, | 			"-nostdlibinc", | ||||||
| 			"-isystem", filepath.Join(path, "include"), // necessary for Xtensa | 			"-isystem", filepath.Join(path, "include"), | ||||||
| 			"-isystem", filepath.Join(picolibcDir, "include"), | 			"-isystem", filepath.Join(picolibcDir, "include"), | ||||||
| 			"-isystem", filepath.Join(picolibcDir, "tinystdio"), | 			"-isystem", filepath.Join(picolibcDir, "tinystdio"), | ||||||
| 		) | 		) | ||||||
|  |  | ||||||
|  | @ -243,7 +243,7 @@ func testCompilePackage(t *testing.T, options *compileopts.Options, file string) | ||||||
| 	defer machine.Dispose() | 	defer machine.Dispose() | ||||||
| 
 | 
 | ||||||
| 	// Load entire program AST into memory. | 	// Load entire program AST into memory. | ||||||
| 	lprogram, err := loader.Load(config, "./testdata/"+file, config.ClangHeaders, types.Config{ | 	lprogram, err := loader.Load(config, "./testdata/"+file, types.Config{ | ||||||
| 		Sizes: Sizes(machine), | 		Sizes: Sizes(machine), | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  |  | ||||||
|  | @ -14,6 +14,8 @@ import ( | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
|  | 
 | ||||||
|  | 	"tinygo.org/x/go-llvm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Keys is a slice of all available environment variable keys. | // Keys is a slice of all available environment variable keys. | ||||||
|  | @ -33,6 +35,9 @@ func init() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Set to true if we're linking statically against LLVM. | ||||||
|  | var hasBuiltinTools = false | ||||||
|  | 
 | ||||||
| // TINYGOROOT is the path to the final location for checking tinygo files. If | // TINYGOROOT is the path to the final location for checking tinygo files. If | ||||||
| // unset (by a -X ldflag), then sourceDir() will fallback to the original build | // unset (by a -X ldflag), then sourceDir() will fallback to the original build | ||||||
| // directory. | // directory. | ||||||
|  | @ -284,3 +289,70 @@ func isSourceDir(root string) bool { | ||||||
| 	_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go")) | 	_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go")) | ||||||
| 	return err == nil | 	return err == nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // ClangResourceDir returns the clang resource dir if available. This is the | ||||||
|  | // -resource-dir flag. If it isn't available, an empty string is returned and | ||||||
|  | // -resource-dir should be left unset. | ||||||
|  | // The libclang flag must be set if the resource dir is read for use by | ||||||
|  | // libclang. | ||||||
|  | // In that case, the resource dir is always returned (even when linking | ||||||
|  | // dynamically against LLVM) because libclang always needs this directory. | ||||||
|  | func ClangResourceDir(libclang bool) string { | ||||||
|  | 	if !hasBuiltinTools && !libclang { | ||||||
|  | 		// Using external tools, so the resource dir doesn't need to be | ||||||
|  | 		// specified. Clang knows where to find it. | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Check whether we're running from a TinyGo release directory. | ||||||
|  | 	// This is the case for release binaries on GitHub. | ||||||
|  | 	root := Get("TINYGOROOT") | ||||||
|  | 	releaseHeaderDir := filepath.Join(root, "lib", "clang") | ||||||
|  | 	if _, err := os.Stat(releaseHeaderDir); !errors.Is(err, fs.ErrNotExist) { | ||||||
|  | 		return releaseHeaderDir | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if hasBuiltinTools { | ||||||
|  | 		// We are statically linked to LLVM. | ||||||
|  | 		// Check whether we're running from the source directory. | ||||||
|  | 		// This typically happens when TinyGo was built using `make` as part of | ||||||
|  | 		// development. | ||||||
|  | 		llvmMajor := strings.Split(llvm.Version, ".")[0] | ||||||
|  | 		buildResourceDir := filepath.Join(root, "llvm-build", "lib", "clang", llvmMajor) | ||||||
|  | 		if _, err := os.Stat(buildResourceDir); !errors.Is(err, fs.ErrNotExist) { | ||||||
|  | 			return buildResourceDir | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		// We use external tools, either when installed using `go install` or | ||||||
|  | 		// when packaged in a Linux distribution (Linux distros typically prefer | ||||||
|  | 		// dynamic linking). | ||||||
|  | 		// Try to detect the system clang resources directory. | ||||||
|  | 		resourceDir := findSystemClangResources(root) | ||||||
|  | 		if resourceDir != "" { | ||||||
|  | 			return resourceDir | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Resource directory not found. | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Find the Clang resource dir on this particular system. | ||||||
|  | // Return the empty string when they aren't found. | ||||||
|  | func findSystemClangResources(TINYGOROOT string) string { | ||||||
|  | 	llvmMajor := strings.Split(llvm.Version, ".")[0] | ||||||
|  | 
 | ||||||
|  | 	switch runtime.GOOS { | ||||||
|  | 	case "linux", "android": | ||||||
|  | 		// Header files are typically stored in /usr/lib/clang/<version>/include. | ||||||
|  | 		// Tested on Fedora 39, Debian 12, and Arch Linux. | ||||||
|  | 		path := filepath.Join("/usr/lib/clang", llvmMajor) | ||||||
|  | 		_, err := os.Stat(filepath.Join(path, "include", "stdint.h")) | ||||||
|  | 		if err == nil { | ||||||
|  | 			return path | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Could not find it. | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										7
									
								
								goenv/tools-builtin.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										7
									
								
								goenv/tools-builtin.go
									
										
									
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | //go:build byollvm | ||||||
|  | 
 | ||||||
|  | package goenv | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	hasBuiltinTools = true | ||||||
|  | } | ||||||
|  | @ -30,7 +30,6 @@ import ( | ||||||
| // Program holds all packages and some metadata about the program as a whole. | // Program holds all packages and some metadata about the program as a whole. | ||||||
| type Program struct { | type Program struct { | ||||||
| 	config      *compileopts.Config | 	config      *compileopts.Config | ||||||
| 	clangHeaders string |  | ||||||
| 	typeChecker types.Config | 	typeChecker types.Config | ||||||
| 	goroot      string // synthetic GOROOT | 	goroot      string // synthetic GOROOT | ||||||
| 	workingDir  string | 	workingDir  string | ||||||
|  | @ -103,7 +102,7 @@ type EmbedFile struct { | ||||||
| // Load loads the given package with all dependencies (including the runtime | // Load loads the given package with all dependencies (including the runtime | ||||||
| // package). Call .Parse() afterwards to parse all Go files (including CGo | // package). Call .Parse() afterwards to parse all Go files (including CGo | ||||||
| // processing, if necessary). | // processing, if necessary). | ||||||
| func Load(config *compileopts.Config, inputPkg string, clangHeaders string, typeChecker types.Config) (*Program, error) { | func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config) (*Program, error) { | ||||||
| 	goroot, err := GetCachedGoroot(config) | 	goroot, err := GetCachedGoroot(config) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|  | @ -119,7 +118,6 @@ func Load(config *compileopts.Config, inputPkg string, clangHeaders string, type | ||||||
| 	} | 	} | ||||||
| 	p := &Program{ | 	p := &Program{ | ||||||
| 		config:      config, | 		config:      config, | ||||||
| 		clangHeaders: clangHeaders, |  | ||||||
| 		typeChecker: typeChecker, | 		typeChecker: typeChecker, | ||||||
| 		goroot:      goroot, | 		goroot:      goroot, | ||||||
| 		workingDir:  wd, | 		workingDir:  wd, | ||||||
|  | @ -438,9 +436,9 @@ func (p *Package) parseFiles() ([]*ast.File, error) { | ||||||
| 	// to call cgo.Process in that case as it will only cause issues. | 	// to call cgo.Process in that case as it will only cause issues. | ||||||
| 	if len(p.CgoFiles) != 0 && len(files) != 0 { | 	if len(p.CgoFiles) != 0 && len(files) != 0 { | ||||||
| 		var initialCFlags []string | 		var initialCFlags []string | ||||||
| 		initialCFlags = append(initialCFlags, p.program.config.CFlags()...) | 		initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...) | ||||||
| 		initialCFlags = append(initialCFlags, "-I"+p.Dir) | 		initialCFlags = append(initialCFlags, "-I"+p.Dir) | ||||||
| 		generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags, p.program.clangHeaders) | 		generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags) | ||||||
| 		p.CFlags = append(initialCFlags, cflags...) | 		p.CFlags = append(initialCFlags, cflags...) | ||||||
| 		p.CGoHeaders = headerCode | 		p.CGoHeaders = headerCode | ||||||
| 		for path, hash := range accessedFiles { | 		for path, hash := range accessedFiles { | ||||||
|  |  | ||||||
|  | @ -145,7 +145,7 @@ func compileGoFileForTesting(t *testing.T, filename string) llvm.Module { | ||||||
| 	defer machine.Dispose() | 	defer machine.Dispose() | ||||||
| 
 | 
 | ||||||
| 	// Load entire program AST into memory. | 	// Load entire program AST into memory. | ||||||
| 	lprogram, err := loader.Load(config, filename, config.ClangHeaders, types.Config{ | 	lprogram, err := loader.Load(config, filename, types.Config{ | ||||||
| 		Sizes: compiler.Sizes(machine), | 		Sizes: compiler.Sizes(machine), | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  |  | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem