Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reduce goroot file copy #37

Merged
merged 1 commit into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

**English | [简体中文](./README_zh_cn.md)**

Enable function Trap for `go`, and provide tools like Mock and Trace to help go developers write unit test and debug both easier and faster.
Enable function Trap in `go`, provide tools like Mock and Trace to help go developers write unit test and debug both easier and faster.

`xgo` works as a preprocessor for `go run`,`go build`, and `go test`(see our [blog](https://blog.xhd2015.xyz/posts/xgo-monkey-patching-in-go-using-toolexec/)).

Expand Down 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
Loading