Skip to content

Commit

Permalink
reduce goroot file copy
Browse files Browse the repository at this point in the history
  • Loading branch information
xhd2015 committed Apr 5, 2024
1 parent 03d82b3 commit 51c75f7
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 100 deletions.
18 changes: 6 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,12 @@ There are other options,see [doc/INSTALLATION.md](./doc/INSTALLATION.md).
There is no specific limitation on OS and Architecture.

**All OS and Architectures** are supported by `xgo` as long as they are supported by `go`.

OS:
- MacOS
- Linux
- Windows (+WSL)
- ...

Architecture:
- x86
- x86_64(amd64)
- arm64
- ...
| | x86_64 | ARM64 | Any Other Arch... |
|---------|-----------|-----------|-----------|
| Linux | Y | Y | Y|
| Windows | Y | Y | Y|
| macOS | Y | Y | Y|
| Any Other OS... | Y | Y | Y|

# Quick Start
Let's write a unit test with `xgo`:
Expand Down
19 changes: 7 additions & 12 deletions README_zh_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

允许对`go`的函数进行拦截, 并提供Mock和Trace等工具帮助开发者编写测试和快速调试。

`xgo`作为一个预处理器工作在`go run`,`go build`,和`go test`之上(查看[blog](https://blog.xhd2015.xyz/posts/xgo-monkey-patching-in-go-using-toolexec/))。
`xgo`作为一个预处理器工作在`go run`,`go build`,和`go test`之上(查看[blog](https://blog.xhd2015.xyz/zh/posts/xgo-monkey-patching-in-go-using-toolexec/))。

`xgo`对源代码和IR(中间码)进行预处理之后, 再调用`go`进行后续的编译工作。通过这种方式, `xgo`实现了一些在`go`中缺乏的能力。

Expand Down Expand Up @@ -39,17 +39,12 @@ xgo version

对OS和Arch没有限制, `xgo`支持所有`go`支持的OS和Arch。

OS:
- macOS
- Linux
- Windows (+WSL)
- ...

Arch:
- x86
- x86_64(amd64)
- arm64
- ...
| | x86_64 | ARM64 | 任何其他架构... |
|---------|-----------|-----------|-----------|
| Linux | Y | Y | Y|
| Windows | Y | Y | Y|
| macOS | Y | Y | Y|
| 任何其他OS... | Y | Y | Y|

# 快速开始
我们基于`xgo`编写一个单元测试:
Expand Down
3 changes: 2 additions & 1 deletion cmd/xgo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ func handleBuild(cmd string, args []string) error {
}
buildCacheDir := filepath.Join(instrumentDir, "build-cache"+buildCacheSuffix)
revisionFile := filepath.Join(instrumentDir, "xgo-revision.txt")
fullSyncRecord := filepath.Join(instrumentDir, "full-sync-record.txt")

var realXgoSrc string
if isDevelopment {
Expand Down Expand Up @@ -294,7 +295,7 @@ func handleBuild(cmd string, args []string) error {
}
if isDevelopment || resetInstrument || revisionChanged {
logDebug("sync goroot %s -> %s", goroot, instrumentGoroot)
err = syncGoroot(goroot, instrumentGoroot, revisionChanged)
err = syncGoroot(goroot, instrumentGoroot, fullSyncRecord)
if err != nil {
return err
}
Expand Down
128 changes: 98 additions & 30 deletions cmd/xgo/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,34 @@ import (
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/xhd2015/xgo/support/filecopy"
"github.com/xhd2015/xgo/support/goinfo"
"github.com/xhd2015/xgo/support/osinfo"
)

// the _FilePath declared at toplevel
// serves as an item list where
// the runtime and compiler maybe
// affected.
// NOTE: do not remove files, always add files,
// these old files may exists in older version
// so can be cleared by newer xgo
type _FilePath []string

func (c _FilePath) Join(s ...string) string {
return filepath.Join(filepath.Join(s...), filepath.Join(c...))
}

var affectedFiles []_FilePath

func init() {
affectedFiles = append(affectedFiles, compilerFiles...)
affectedFiles = append(affectedFiles, runtimeFiles...)
affectedFiles = append(affectedFiles, reflectFiles...)
}

// assume go 1.20
// the patch should be idempotent
// the origGoroot is used to generate runtime defs, see https://github.com/xhd2015/xgo/issues/4#issuecomment-2017880791
Expand Down Expand Up @@ -44,14 +66,6 @@ func patchRuntimeAndCompiler(origGoroot string, goroot string, xgoSrc string, go
return nil
}

func getInternalPatch(goroot string, subDirs ...string) string {
dir := filepath.Join(goroot, "src", "cmd", "compile", "internal", "xgo_rewrite_internal", "patch")
if len(subDirs) > 0 {
dir = filepath.Join(dir, filepath.Join(subDirs...))
}
return dir
}

func replaceBuildIgnore(content []byte) ([]byte, error) {
const buildIgnore = "//go:build ignore"

Expand Down Expand Up @@ -101,50 +115,104 @@ func readOrEmpty(file string) (string, error) {
}

// NOTE: flagA never cause goroot to reset
func syncGoroot(goroot string, dstDir string, forceCopy bool) error {
func syncGoroot(goroot string, instrumentGoroot string, fullSyncRecordFile string) error {
// check if src goroot has src/runtime
srcRuntimeDir := filepath.Join(goroot, "src", "runtime")
err := assertDir(srcRuntimeDir)
if err != nil {
return err
}
if !forceCopy {
srcGoBin := filepath.Join(goroot, "bin", "go")
dstGoBin := filepath.Join(dstDir, "bin", "go")
var goBinaryChanged bool = true
srcGoBin := filepath.Join(goroot, "bin", "go")
dstGoBin := filepath.Join(instrumentGoroot, "bin", "go")

srcFile, err := os.Stat(srcGoBin)
if err != nil {
return nil
}
if srcFile.IsDir() {
return fmt.Errorf("bad goroot: %s", goroot)
}

dstFile, statErr := os.Stat(dstGoBin)
if statErr != nil {
if !os.IsNotExist(statErr) {
return statErr
}
}

srcFile, err := os.Stat(srcGoBin)
if dstFile != nil && !dstFile.IsDir() && dstFile.Size() == srcFile.Size() {
goBinaryChanged = false
}

var doPartialCopy bool
if !goBinaryChanged && statNoErr(fullSyncRecordFile) {
// full sync record does not yet exist
doPartialCopy = true
}
if doPartialCopy {
// do partial copy
err := partialCopy(goroot, instrumentGoroot)
if err != nil {
return nil
return err
}
if srcFile.IsDir() {
return fmt.Errorf("bad goroot: %s", goroot)
} else {
rmErr := os.Remove(fullSyncRecordFile)
if rmErr != nil {
if !errors.Is(rmErr, os.ErrNotExist) {
return rmErr
}
}

dstFile, statErr := os.Stat(dstGoBin)
if statErr != nil {
if !os.IsNotExist(statErr) {
return statErr
}
// need copy, delete target dst dir first
// TODO: use git worktree add if .git exists
err = filecopy.NewOptions().
Concurrent(10).
CopyReplaceDir(goroot, instrumentGoroot)
if err != nil {
return err
}

if dstFile != nil && !dstFile.IsDir() && dstFile.Size() == srcFile.Size() {
// already copied
return nil
// record this full sync
copyTime := time.Now().Format("2006-01-02T15:04:05Z07:00")
err = os.WriteFile(fullSyncRecordFile, []byte(copyTime), 0755)
if err != nil {
return err
}
}
// change binary executable
return nil
}

// need copy, delete target dst dir first
// TODO: use git worktree add if .git exists
err = filecopy.NewOptions().
Concurrent(10).
CopyReplaceDir(goroot, dstDir)
func partialCopy(goroot string, instrumentGoroot string) error {
err := os.RemoveAll(xgoRewriteInternal.Join(instrumentGoroot))
if err != nil {
return err
}
// change binary executable
for _, affectedFile := range affectedFiles {
srcFile := affectedFile.Join(goroot)
dstFile := affectedFile.Join(instrumentGoroot)

err := filecopy.CopyFileAll(srcFile, dstFile)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return err
}
// delete dstFile
err := os.RemoveAll(dstFile)
if err != nil {
return err
}
continue
}
}
return nil
}

func statNoErr(f string) bool {
_, err := os.Stat(f)
return err == nil
}
func buildInstrumentTool(goroot string, xgoSrc string, compilerBin string, compilerBuildIDFile string, execToolBin string, xgoBin string, debugPkg string, logCompile bool, noSetup bool, debugWithDlv bool) (compilerChanged bool, toolExecFlag string, err error) {
var execToolCmd []string
if !noSetup {
Expand Down
Loading

0 comments on commit 51c75f7

Please sign in to comment.