Skip to content

Commit

Permalink
feat: retrieve package dump JSON and generate swiftpkg.PackageInfo (#…
Browse files Browse the repository at this point in the history
…22)

- Implement `InitPackage()`, `DumpPackage()`, and `DescribePackage()`
in`swiftbin.SwiftBin`.
- Implement `swiftpkg.NewPackageInfo()` to retrieve the dump JSON and
parse into `swiftpkg.Manifest`.

Related to #8.
  • Loading branch information
cgrindel committed Nov 23, 2022
1 parent 3306684 commit 525f02c
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 24 deletions.
1 change: 1 addition & 0 deletions gazelle/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ go_library(
"//gazelle/internal/stringslices",
"//gazelle/internal/swift",
"//gazelle/internal/swiftbin",
"//gazelle/internal/swiftpkg",
"//gazelle/internal/wspace",
"@bazel_gazelle//config:go_default_library",
"@bazel_gazelle//label:go_default_library",
Expand Down
60 changes: 54 additions & 6 deletions gazelle/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/bazelbuild/bazel-gazelle/rule"
"github.com/cgrindel/swift_bazel/gazelle/internal/swift"
"github.com/cgrindel/swift_bazel/gazelle/internal/swiftbin"
"github.com/cgrindel/swift_bazel/gazelle/internal/swiftpkg"
"github.com/cgrindel/swift_bazel/gazelle/internal/wspace"
)

Expand All @@ -16,6 +17,12 @@ const swiftConfigName = "swift"
type swiftConfig struct {
moduleIndex *swift.ModuleIndex
swiftBinPath string
// If this is true and a Package.swift is found, then use it to generate Bazel build files.
genFromPkgManifest bool
}

func (sc *swiftConfig) swiftBin() *swiftbin.SwiftBin {
return swiftbin.NewSwiftBin(sc.swiftBinPath)
}

func getSwiftConfig(c *config.Config) *swiftConfig {
Expand All @@ -27,23 +34,59 @@ func (*swiftLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config)
sc := &swiftConfig{
moduleIndex: swift.NewModuleIndex(),
}

switch cmd {
case "fix", "update":
fs.BoolVar(
&sc.genFromPkgManifest,
"gen_from_pkg_manifest",
false,
"If set and a Package.swift file is found, then generate build files from manifest info.",
)

// case "update-repos":
}

// Store the config for later steps
c.Exts[swiftConfigName] = sc
}

func (sl *swiftLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
var err error
sc := getSwiftConfig(c)

// TODO(chuck): Add flag so that the client can tell use which Swift to use.
// GH021: Add flag so that the client can tell us which Swift to use.

// Find the Swift executable
sc.swiftBinPath, err = swiftbin.FindSwiftBinPath()
if err != nil {
if sc.swiftBinPath, err = swiftbin.FindSwiftBinPath(); err != nil {
return err
}
sb := sc.swiftBin()

// Look for http_archive declarations with Swift declarations.
wkspFilePath := wspace.FindWORKSPACEFile(c.RepoRoot)
shouldProcWkspFile := true
if sc.genFromPkgManifest {
if pkg, err := swiftpkg.NewPackageInfo(sb, c.RepoRoot); err != nil {
return err
} else if pkg != nil {
shouldProcWkspFile = false
if err = processSwiftPackage(sc, pkg); err != nil {
return err
}
}
}

if shouldProcWkspFile {
// Look for http_archive declarations with Swift declarations.
if err = findExternalDepsInWorkspace(sc.moduleIndex, c.RepoRoot); err != nil {
return err
}
}

return nil
}

func findExternalDepsInWorkspace(mi *swift.ModuleIndex, repoRoot string) error {
wkspFilePath := wspace.FindWORKSPACEFile(repoRoot)
wkspFile, err := rule.LoadWorkspaceFile(wkspFilePath, "")
if err != nil {
return fmt.Errorf("failed to load WORKSPACE file %v: %w", wkspFilePath, err)
Expand All @@ -57,8 +100,13 @@ func (sl *swiftLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
)
}
for _, archive := range archives {
sc.moduleIndex.AddModules(archive.Modules...)
mi.AddModules(archive.Modules...)
}
return nil
}

func processSwiftPackage(sc *swiftConfig, pkg *swiftpkg.PackageInfo) error {
// GH008: Generate a BUILD file with everything from the module OR store the info in the config
// and generate build files as we visit the appropriate directories.
return nil
}
31 changes: 22 additions & 9 deletions gazelle/internal/swiftbin/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
load("@cgrindel_bazel_starlib//bzlformat:defs.bzl", "bzlformat_pkg")
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

bzlformat_pkg(name = "bzlformat")

# MARK: - Integration Test

filegroup(
name = "all_files",
srcs = glob(["*"]),
visibility = ["//:__subpackages__"],

# MARK: - Golang

go_library(
name = "swiftbin",
Expand All @@ -11,12 +22,14 @@ go_library(
visibility = ["//gazelle:__subpackages__"],
)

bzlformat_pkg(name = "bzlformat")

# MARK: - Integration Test

filegroup(
name = "all_files",
srcs = glob(["*"]),
visibility = ["//:__subpackages__"],
go_test(
name = "swiftbin_test",
srcs = ["swift_bin_test.go"],
# This test runs Swift commands which will not succeed in the sandbox
local = True,
deps = [
":swiftbin",
"//gazelle/internal/jsonutils",
"@com_github_stretchr_testify//assert",
],
)
63 changes: 62 additions & 1 deletion gazelle/internal/swiftbin/swift_bin.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,65 @@
package swiftbin

type SwiftBin interface {
import (
"bytes"
"fmt"
"os/exec"
)

type Executor interface {
InitPackage(dir, name, pkgType string) error
DumpPackage(dir string) ([]byte, error)
DescribePackage(dir string) ([]byte, error)
}

type SwiftBin struct {
BinPath string
}

func NewSwiftBin(binPath string) *SwiftBin {
return &SwiftBin{BinPath: binPath}
}

func (sb *SwiftBin) InitPackage(dir, name, pkgType string) error {
args := []string{"package", "init", "--name", name, "--type", pkgType}
cmd := exec.Command(sb.BinPath, args...)
cmd.Dir = dir
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed executing `swift package init`, out\n%v: %w", string(out), err)
}
return nil
}

func (sb *SwiftBin) DumpPackage(dir string) ([]byte, error) {
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command(sb.BinPath, "package", "dump-package")
cmd.Dir = dir
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf(
"failed executing `swift package dump-package`, stderr\n%s: %w",
stderr.String(),
err,
)
}
return stdout.Bytes(), nil
}

func (sb *SwiftBin) DescribePackage(dir string) ([]byte, error) {
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command(sb.BinPath, "package", "describe", "--type", "json")
cmd.Dir = dir
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf(
"failed executing `swift package describe`, stderr\n%s: %w",
stderr.String(),
err,
)
}
return stdout.Bytes(), nil
}
50 changes: 50 additions & 0 deletions gazelle/internal/swiftbin/swift_bin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package swiftbin_test

import (
"encoding/json"
"os"
"testing"

"github.com/cgrindel/swift_bazel/gazelle/internal/jsonutils"
"github.com/cgrindel/swift_bazel/gazelle/internal/swiftbin"
"github.com/stretchr/testify/assert"
)

func TestSwiftBin(t *testing.T) {
t.Run("init package, dump, and describe", func(t *testing.T) {
// Create temp dir
dir, err := os.MkdirTemp("", "swiftbin")
assert.NoError(t, err)
defer os.RemoveAll(dir)

// Find Swift
binPath, err := swiftbin.FindSwiftBinPath()
assert.NoError(t, err)
sb := swiftbin.NewSwiftBin(binPath)

// Init a package
pkgName := "MyPackage"
err = sb.InitPackage(dir, pkgName, "library")
assert.NoError(t, err)

// Dump the package
out, err := sb.DumpPackage(dir)
assert.NoError(t, err)
var dumpMap map[string]any
err = json.Unmarshal(out, &dumpMap)
assert.NoError(t, err)
actualPkgName, err := jsonutils.StringAtKey(dumpMap, "name")
assert.NoError(t, err)
assert.Equal(t, pkgName, actualPkgName)

// Describe the package
out, err = sb.DescribePackage(dir)
assert.NoError(t, err)
var descMap map[string]any
err = json.Unmarshal(out, &descMap)
assert.NoError(t, err)
actualPkgName, err = jsonutils.StringAtKey(descMap, "name")
assert.NoError(t, err)
assert.Equal(t, pkgName, actualPkgName)
})
}
12 changes: 10 additions & 2 deletions gazelle/internal/swiftpkg/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@ go_library(
name = "swiftpkg",
srcs = [
"manifest.go",
"swift_package.go",
"package_info.go",
],
importpath = "github.com/cgrindel/swift_bazel/gazelle/internal/swiftpkg",
visibility = ["//gazelle:__subpackages__"],
deps = [
"//gazelle/internal/jsonutils",
"//gazelle/internal/swiftbin",
"@com_github_hashicorp_go_multierror//:go-multierror",
],
)

go_test(
name = "swiftpkg_test",
srcs = ["manifest_test.go"],
srcs = [
"manifest_test.go",
"package_info_test.go",
],
# The PackageInfo tests use SwiftBin to create an actual Swift package. To use Swift, the test
# needs to be executed outside of the sandbox.
local = True,
deps = [
":swiftpkg",
"//gazelle/internal/swiftbin",
"@com_github_stretchr_testify//assert",
],
)
Expand Down
29 changes: 29 additions & 0 deletions gazelle/internal/swiftpkg/package_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package swiftpkg

import (
"github.com/cgrindel/swift_bazel/gazelle/internal/swiftbin"
)

type PackageInfo struct {
// Package directory
Dir string

// The manifest information
Manifest *Manifest
}

func NewPackageInfo(sw swiftbin.Executor, dir string) (*PackageInfo, error) {
dump, err := sw.DumpPackage(dir)
if err != nil {
return nil, err
}
manifest, err := NewManifestFromJSON(dump)
if err != nil {
return nil, err
}

return &PackageInfo{
Dir: dir,
Manifest: manifest,
}, nil
}
34 changes: 34 additions & 0 deletions gazelle/internal/swiftpkg/package_info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package swiftpkg_test

import (
"os"
"testing"

"github.com/cgrindel/swift_bazel/gazelle/internal/swiftbin"
"github.com/cgrindel/swift_bazel/gazelle/internal/swiftpkg"
"github.com/stretchr/testify/assert"
)

func TestPackageInfo(t *testing.T) {
t.Run("create", func(t *testing.T) {
// Create temp dir
dir, err := os.MkdirTemp("", "swiftpkg")
assert.NoError(t, err)
defer os.RemoveAll(dir)

// Find Swift
binPath, err := swiftbin.FindSwiftBinPath()
assert.NoError(t, err)
sb := swiftbin.NewSwiftBin(binPath)

// Init a package
pkgName := "MyPackage"
err = sb.InitPackage(dir, pkgName, "library")
assert.NoError(t, err)

pi, err := swiftpkg.NewPackageInfo(sb, dir)
assert.NoError(t, err)
assert.Equal(t, dir, pi.Dir)
assert.Equal(t, pkgName, pi.Manifest.Name)
})
}
6 changes: 0 additions & 6 deletions gazelle/internal/swiftpkg/swift_package.go

This file was deleted.

0 comments on commit 525f02c

Please sign in to comment.