// Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause package main import ( "errors" "fmt" "io" "io/fs" "os" "path/filepath" ) // makeGoroot constructs a GOROOT-like file structure in outPath, // which consists of toolchainRoot except for the `go` binary, which // points to gocross. // // It's useful for integrating with tooling that expects to be handed // a GOROOT, like the Goland IDE or depaware. func makeGoroot(toolchainRoot, outPath string) error { self, err := os.Executable() if err != nil { return fmt.Errorf("getting gocross's path: %v", err) } os.RemoveAll(outPath) if err := os.MkdirAll(filepath.Join(outPath, "bin"), 0750); err != nil { return fmt.Errorf("making %q: %v", outPath, err) } if err := os.Symlink(self, filepath.Join(outPath, "bin/go")); err != nil { return fmt.Errorf("linking gocross into outpath: %v", err) } if err := linkFarm(toolchainRoot, outPath); err != nil { return fmt.Errorf("creating GOROOT link farm: %v", err) } if err := linkFarm(filepath.Join(toolchainRoot, "bin"), filepath.Join(outPath, "bin")); err != nil { return fmt.Errorf("creating GOROOT/bin link farm: %v", err) } return nil } func copyFile(src, dst string) error { s, err := os.Open(src) if err != nil { return fmt.Errorf("opening %q: %v", src, err) } defer s.Close() d, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE, 0755) if err != nil { return fmt.Errorf("opening %q: %v", dst, err) } if _, err := io.Copy(d, s); err != nil { d.Close() return fmt.Errorf("copying %q to %q: %v", src, dst, err) } if err := d.Close(); err != nil { return fmt.Errorf("closing %q: %v", dst, err) } return nil } // linkFarm symlinks every entry in srcDir into outDir, unless that // directory entry already exists. func linkFarm(srcDir, outDir string) error { ents, err := os.ReadDir(srcDir) if err != nil { return fmt.Errorf("reading %q: %v", srcDir, err) } for _, ent := range ents { dst := filepath.Join(outDir, ent.Name()) _, err := os.Lstat(dst) if errors.Is(err, fs.ErrNotExist) { if err := os.Symlink(filepath.Join(srcDir, ent.Name()), dst); err != nil { return fmt.Errorf("symlinking %q to %q: %v", ent.Name(), outDir, err) } } else if err != nil { return fmt.Errorf("stat-ing %q: %v", dst, err) } } return nil }