Skip to content

Commit

Permalink
add script/vendir
Browse files Browse the repository at this point in the history
  • Loading branch information
xhd2015 committed Oct 22, 2024
1 parent 1d8b33c commit 51d5717
Show file tree
Hide file tree
Showing 16 changed files with 689 additions and 16 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ cover*.out

/tmp

debug-compile.json
debug-compile.json

/script/vendir/example/src/vendor
/script/vendir/example/internal/third_party_vendir
4 changes: 2 additions & 2 deletions cmd/xgo/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import "fmt"
// VERSION is manully updated when needed a new tag

Check warning on line 6 in cmd/xgo/version.go

View workflow job for this annotation

GitHub Actions / check-build

"manully" should be "manually".
// see also runtime/core/version.go
const VERSION = "1.0.49"
const REVISION = "bfe5f14d7bfaf52b5dfecb95aaaa717e0ff4d3c9+1"
const NUMBER = 310
const REVISION = "1d8b33c2ca5fafec9439a1ec7adcdc591db705b9+1"
const NUMBER = 311

// the matching runtime/core's version
// manually updated
Expand Down
19 changes: 19 additions & 0 deletions script/vendir/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Vendir
vendir is an utility helps to introduce third party vendor libraries without introducing dependencies in the go.mod.

This way, a library itself can separate its exported APIs from its internal dependencies.

# Usage
```sh
go run github.com/xhd2015/xgo/script/vendir@latest create ./internal/third/src ./internal/third/vendir
```
This command will copy all dependencies `./internal/third/src/vendor` to `./internal/third/vendir`, and rewrite all imports to that created internal package.

Prior to running this command, you should create a go.mod, add dependency and run `go mod vendor` inside `./internal/third/src`.

# How it works?
In general, the source directory should contain a go.mod and a vendor directory describing all it's dependencies.

The target directory will be created by copying all dependencies from source vendor directory and rewrite all import paths(except stdlib) to internal paths.

Check [./example](./example) for demonstration.
20 changes: 20 additions & 0 deletions script/vendir/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Verify vendir create
```sh
# cd to current directory if not yet
cd ./script/vendir/example

# update dependencies
# you may need go1.22
(cd src && go mod vendor)

# remove target vendor
rm -rf ./internal/third_party_vendir

# update vendor
go run github.com/xhd2015/xgo/script/vendir create ./src ./internal/third_party_vendir

# check result
go run ./test
# output:
# github.com/xhd2015/xgo/script/vendir/example/test
```
15 changes: 15 additions & 0 deletions script/vendir/example/src/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module github.com/xhd2015/xgo/script/vendir/testdata/src

go 1.22.0

toolchain go1.22.2

require (
github.com/xhd2015/less-gen v0.0.2
golang.org/x/tools v0.25.0
)

require (
golang.org/x/mod v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
)
8 changes: 8 additions & 0 deletions script/vendir/example/src/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
github.com/xhd2015/less-gen v0.0.2 h1:4AqksVjQoC12oqm7OPtH0zaPPwoYCPfcZgBrJFJujWE=
github.com/xhd2015/less-gen v0.0.2/go.mod h1:14sYoXpQmBGJPM5sSoxOXocW1b7WeNmCfqOSBqHI4Js=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
6 changes: 6 additions & 0 deletions script/vendir/example/src/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package manifest

import (
_ "github.com/xhd2015/less-gen/go/project" // has ref
_ "golang.org/x/tools/cover" // no ref
)
23 changes: 23 additions & 0 deletions script/vendir/example/test/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"fmt"
"os"

"github.com/xhd2015/xgo/script/vendir/example/internal/third_party_vendir/github.com/xhd2015/less-gen/go/load"
"github.com/xhd2015/xgo/script/vendir/example/internal/third_party_vendir/github.com/xhd2015/less-gen/go/project"
)

func main() {
project, err := project.Load([]string{"./test"}, &load.LoadOptions{})
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
pkg, err := project.GetOnlyEntryPackage()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
fmt.Println(pkg.GoPkg().PkgPath)
}
242 changes: 242 additions & 0 deletions script/vendir/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
package main

import (
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"

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

const help = `
vendir helps to create third party vendor dependency without
introducing changes to go.mod.
Usage:
vendir create [OPTIONS] <dir> <target_vendor_dir>
vendir rewrite-file [OPTIONS] <file> <target_vendor_dir>
vendir rewrite-path <path> <target_vendor_dir>
vendir help
Arguments:
<dir> should be either:
- the root dir containing a go.mod and a vendor dir, or
- the vendor directory
Options:
--help show help message
Example:
$ go run github.com/xhd2015/xgo/script/vendir create ./x/src ./x/third_party_vendir
`

// usage:
//
// go run ./script/vendir create ./script/vendir/testdata/src ./script/vendir/testdata/third_party_vendir
func main() {
err := handle(os.Args[1:])
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}

func handle(args []string) error {
if len(args) == 0 {
return fmt.Errorf("requires cmd")
}
cmd := args[0]
args = args[1:]
switch cmd {
case "create":
return createVendor(args)
case "rewrite-file":
return rewriteFile(args)
case "rewrite-path":
return rewritePath(args)
case "help":
fmt.Println(strings.TrimSpace(help))
return nil
default:
return fmt.Errorf("unrecognized cmd: %s, check help", cmd)
}
}

func createVendor(args []string) error {
var remainArgs []string
var verbose bool
n := len(args)
for i := 0; i < n; i++ {
if args[i] == "--rm" {
if i+1 >= n {
return fmt.Errorf("%v requires arg", args[i])
}
i++
continue
}
if args[i] == "-v" || args[i] == "--verbose" {
verbose = true
continue
}
if args[i] == "--help" {
fmt.Println(strings.TrimSpace(help))
return nil
}
if args[i] == "--" {
remainArgs = append(remainArgs, args[i+1:]...)
break
}
if strings.HasPrefix(args[i], "-") {
return fmt.Errorf("unrecognized flag: %v", args[i])
}
remainArgs = append(remainArgs, args[i])
}
if len(remainArgs) < 2 {
return fmt.Errorf("usage: vendir create <dir> <target_dir>")
}

dir := remainArgs[0]
targetVendorDir := remainArgs[1]

_, targetStatErr := os.Stat(targetVendorDir)
if !os.IsNotExist(targetStatErr) {
return fmt.Errorf("%s already exists, remove it before create", targetVendorDir)
}
goMod := filepath.Join(dir, "go.mod")
vendorDir := filepath.Join(dir, "vendor")
_, err := os.Stat(goMod)
if err != nil {
return err
}
_, err = os.Stat(vendorDir)
if err != nil {
return err
}

err = os.MkdirAll(targetVendorDir, 0755)
if err != nil {
return err
}

modPath, err := goinfo.ResolveModPath(targetVendorDir)
if err != nil {
return err
}
if verbose {
fmt.Fprintf(os.Stderr, "rewrite prefix: %s\n", modPath)
}
rw, err := initRewriter(modPath + "/")
if err != nil {
return err
}

err = filecopy.Copy(vendorDir, targetVendorDir)
if err != nil {
return err
}
// traverse all go files, and rewrite
return filepath.Walk(targetVendorDir, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if !strings.HasSuffix(path, ".go") {
return nil
}
newCode, err := rw.rewriteFile(path)
if err != nil {
return fmt.Errorf("%s: %w", path, err)
}
return os.WriteFile(path, []byte(newCode), info.Mode())
})
}

func rewriteFile(args []string) error {
var some string

var remainArgs []string
n := len(args)
for i := 0; i < n; i++ {
if args[i] == "--some" {
if i+1 >= n {
return fmt.Errorf("%v requires arg", args[i])
}
some = args[i+1]
i++
continue
}
if args[i] == "--help" {
fmt.Println(strings.TrimSpace(help))
return nil
}
if args[i] == "--" {
remainArgs = append(remainArgs, args[i+1:]...)
break
}
if strings.HasPrefix(args[i], "-") {
return fmt.Errorf("unrecognized flag: %v", args[i])
}
remainArgs = append(remainArgs, args[i])
}
if len(remainArgs) < 2 {
return fmt.Errorf("usage: vendir <file> <target_dir>")
}

file := remainArgs[0]
targetDir := remainArgs[1]

stat, err := os.Stat(file)
if err != nil {
return err
}

if stat.IsDir() {
return fmt.Errorf("%s is not a file", file)
}

modPath, err := goinfo.ResolveModPath(targetDir)
if err != nil {
return err
}
rw, err := initRewriter(modPath + "/")
if err != nil {
return err
}
// rewrite file
code, err := rw.rewriteFile(file)
if err != nil {
return err
}
fmt.Println(code)

_ = some

return nil
}

func rewritePath(args []string) error {
remainArgs := args
if len(remainArgs) < 2 {
return fmt.Errorf("usage: vendir rewrite-path <path> <target_dir>")
}

path := remainArgs[0]
targetDir := remainArgs[1]

modPath, err := goinfo.ResolveModPath(targetDir)
if err != nil {
return err
}
rw, err := initRewriter(modPath + "/")
if err != nil {
return err
}

fmt.Println(rw.rewritePath(path))
return nil
}
Loading

0 comments on commit 51d5717

Please sign in to comment.