package main import ( "embed" "errors" "flag" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "go/format" "os" "path" "regexp" "strings" ) //go:embed internal var patchFiles embed.FS func main() { repositoryUrl := flag.String("url", "https://github.com/golang/go.git", "golang repository url") repositoryRef := flag.String("ref", Version, "git reference") outputDirectory := flag.String("output", ".", "output directory") packagePath := flag.String("pkg", "", "package path") flag.Parse() tmpDir, err := os.MkdirTemp(os.TempDir(), "golang_repo") if err != nil { panic(err) } defer os.RemoveAll(tmpDir) _, err = git.PlainClone(tmpDir, false, &git.CloneOptions{ URL: *repositoryUrl, ReferenceName: plumbing.NewTagReferenceName(*repositoryRef), SingleBranch: true, Depth: 1, RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, ShallowSubmodules: true, Progress: os.Stderr, }) if errors.Is(err, git.NoMatchingRefSpecError{}) { //retry with branch name instead _, err = git.PlainClone(tmpDir, false, &git.CloneOptions{ URL: *repositoryUrl, ReferenceName: plumbing.NewBranchReferenceName(*repositoryRef), SingleBranch: true, Depth: 1, RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, ShallowSubmodules: true, Progress: os.Stderr, }) } if err != nil { panic(err) } err = os.MkdirAll(*outputDirectory, 0755) if err != nil { panic(err) } dirList, err := os.ReadDir(*outputDirectory) if err != nil { panic(err) } // Cleanup directories from source for _, d := range dirList { if d.IsDir() { err = os.RemoveAll(path.Join(*outputDirectory, d.Name())) if err != nil { panic(err) } } } type targets [2]string for _, t := range []targets{ {"src/cmd/asm/internal/arch", "asm/arch"}, {"src/cmd/internal/obj", "obj"}, {"src/cmd/internal/objabi", "objabi"}, {"src/cmd/internal/goobj", "goobj"}, {"src/cmd/internal/dwarf", "dwarf"}, {"src/cmd/internal/src", "src"}, {"src/cmd/internal/sys", "sys"}, {"src/internal/abi", "abi"}, {"src/internal/goarch", "goarch"}, {"src/internal/buildcfg", "buildcfg"}, } { srcPath := path.Join(tmpDir, t[0]) outPath := path.Join(*outputDirectory, t[1]) err = os.MkdirAll(path.Dir(outPath), 0755) if err != nil { panic(err) } //todo: os.CopyFS https://github.com/golang/go/issues/62484 err = os.Rename(srcPath, outPath) if err != nil { panic(err) } } for _, fileToDelete := range []string{ // removes dependency on goexperiment "buildcfg/exp.go", // removes dependency on internal/bisect "objabi/flag.go", // removes dependency on cmd/internal/bio "obj/objfile.go", "goobj/objfile.go", "goobj/funcinfo.go", } { err = os.Remove(path.Join(*outputDirectory, fileToDelete)) if err != nil { panic(err) } } var nestPatch func(dir string) error nestPatch = func(dir string) error { dirList, err := patchFiles.ReadDir(dir) if err != nil { return err } for _, p := range dirList { if p.IsDir() { err = nestPatch(path.Join(dir, p.Name())) if err != nil { return err } continue } fData, err := patchFiles.ReadFile(path.Join(dir, p.Name())) if err != nil { return err } err = os.WriteFile(path.Join(*outputDirectory, strings.TrimPrefix(dir, "internal/"), p.Name()), fData, 0655) if err != nil { return err } } return nil } err = nestPatch("internal") if err != nil { panic(err) } // Copy license file err = os.Rename(path.Join(tmpDir, "LICENSE"), path.Join(*outputDirectory, "LICENSE")) if err != nil { panic(err) } type replaceEntry struct { Regexp *regexp.Regexp Replacement []byte } replacer := []replaceEntry{ // fixup notsha256 {regexp.MustCompile("(\\s)\"cmd/internal/notsha256\""), []byte("$1\"crypto/sha256\"")}, {regexp.MustCompile("notsha256\\."), []byte("sha256.")}, // fixup lazyregexp {regexp.MustCompile("(\\s)\"cmd/internal/lazyregexp\""), []byte("$1\"regexp\"")}, {regexp.MustCompile("lazyregexp\\.New"), []byte("regexp.MustCompile")}, // fixup imports {regexp.MustCompile("(\\s)\"cmd/internal"), []byte("$1\"" + *packagePath)}, {regexp.MustCompile("(\\s)\"internal"), []byte("$1\"" + *packagePath)}, } var nestDir func(dir string, depth int) error nestDir = func(dir string, depth int) error { dirList, err := os.ReadDir(dir) if err != nil { return err } for _, d := range dirList { fPath := path.Join(dir, d.Name()) if d.IsDir() { // remove tests if d.Name() == "testdata" { err = os.RemoveAll(fPath) if err != nil { return err } continue } err := nestDir(fPath, depth+1) if err != nil { return err } continue } // do not replace on root if depth < 2 { continue } // remove tests if strings.HasSuffix(d.Name(), "_test.go") || strings.HasSuffix(d.Name(), "_test.s") { err := os.Remove(fPath) if err != nil { return err } continue } if strings.HasSuffix(d.Name(), ".go") { err := func() error { fData, err := os.ReadFile(fPath) if err != nil { return err } for _, r := range replacer { fData = r.Regexp.ReplaceAll(fData, r.Replacement) } // gofmt fData, err = format.Source(fData) if err != nil { return err } writer, err := os.Create(fPath) if err != nil { return err } defer writer.Close() _, err = writer.Write(fData) if err != nil { return err } return nil }() if err != nil { return err } } } return nil } err = nestDir(*outputDirectory, 1) if err != nil { panic(err) } }