Skip to content

Commit

Permalink
[WIP] Support "flat" style of of BUILD file organization
Browse files Browse the repository at this point in the history
It supports the pattern 2 discussed in
#16 (comment)
  • Loading branch information
yugui committed Aug 29, 2016
1 parent fcb9af0 commit 5ebffb2
Show file tree
Hide file tree
Showing 12 changed files with 404 additions and 75 deletions.
33 changes: 25 additions & 8 deletions go/tools/gazelle/gazelle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,23 @@ var (
goPrefix = flag.String("go_prefix", "", "go_prefix of the target workspace")
repoRoot = flag.String("repo_root", "", "path to a directory which corresponds to go_prefix")
mode = flag.String("mode", "print", "print, fix or diff")
style = flag.String("style", "structured", "structued for flat")
)

var modeFromName = map[string]func(*bzl.File) error{
"print": printFile,
"fix": fixFile,
"diff": diffFile,
}
var (
modeFromName = map[string]func(*bzl.File) error{
"print": printFile,
"fix": fixFile,
"diff": diffFile,
}
styleFromName = map[string]generator.Style{
"structured": generator.StructuredStyle,
"flat": generator.FlatStyle,
}
)

func run(dirs []string, emit func(*bzl.File) error) error {
g, err := generator.New(*repoRoot, *goPrefix)
func run(dirs []string, s generator.Style, emit func(*bzl.File) error) error {
g, err := generator.New(*repoRoot, *goPrefix, s)
if err != nil {
return err
}
Expand Down Expand Up @@ -80,6 +87,12 @@ In print mode, gazelle prints reconciled BUILD files to stdout.
In fix mode, gazelle creates BUILD files or updates existing ones.
In diff mode, gazelle shows diff.
There are two output styles of gazelle.
In structured style, gazelle maps a Go package into a Bazel package and creates
a BUILD file for each.
IN flat style, gazelle creates only one BUILD file into the repository root,
which covers all the Go packages under the repository.
FLAGS:
`)
flag.PrintDefaults()
Expand All @@ -106,8 +119,12 @@ func main() {
if emit == nil {
log.Fatalf("unrecognized mode %s", *mode)
}
s, ok := styleFromName[*style]
if !ok {
log.Fatalf("unrecognized style %s", *style)
}

if err := run(flag.Args(), emit); err != nil {
if err := run(flag.Args(), s, emit); err != nil {
log.Fatal(err)
}
}
7 changes: 6 additions & 1 deletion go/tools/gazelle/generator/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = ["generator.go"],
srcs = [
"file_builder.go",
"flat_file_builder.go",
"generator.go",
"structured_file_builder.go",
],
visibility = ["//visibility:public"],
deps = [
"@io_bazel_buildifier//core:go_default_library",
Expand Down
47 changes: 47 additions & 0 deletions go/tools/gazelle/generator/file_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package generator

import (
bzl "github.com/bazelbuild/buildifier/core"
)

const (
// goRulesBzl is the label of the Skylark file which provides Go rules
goRulesBzl = "@io_bazel_rules_go//go:def.bzl"
)

type fileBuilder interface {
addRules(rel string, rules []*bzl.Rule)
files() []*bzl.File
}

func generateLoad(f *bzl.File) bzl.Expr {
var list []string
for _, kind := range []string{
"go_prefix",
"go_library",
"go_binary",
"go_test",
// TODO(yugui): Support cgo_library
} {
if len(f.Rules(kind)) > 0 {
list = append(list, kind)
}
}
if len(list) == 0 {
return nil
}
return loadExpr(goRulesBzl, list...)
}

func loadExpr(ruleFile string, rules ...string) bzl.Expr {
var list []bzl.Expr
for _, r := range append([]string{ruleFile}, rules...) {
list = append(list, &bzl.StringExpr{Value: r})
}

return &bzl.CallExpr{
X: &bzl.LiteralExpr{Token: "load"},
List: list,
ForceCompact: true,
}
}
23 changes: 23 additions & 0 deletions go/tools/gazelle/generator/flat_file_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package generator

import (
bzl "github.com/bazelbuild/buildifier/core"
)

type flatFileBuilder struct {
f bzl.File
}

func (b *flatFileBuilder) addRules(rel string, rules []*bzl.Rule) {
for _, r := range rules {
b.f.Stmt = append(b.f.Stmt, r.Call)
}
}

func (b *flatFileBuilder) files() []*bzl.File {
b.f.Path = "BUILD"
if load := generateLoad(&b.f); load != nil {
b.f.Stmt = append([]bzl.Expr{load}, b.f.Stmt...)
}
return []*bzl.File{&b.f}
}
79 changes: 25 additions & 54 deletions go/tools/gazelle/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,32 @@ import (
"github.com/bazelbuild/rules_go/go/tools/gazelle/rules"
)

// A Style describes a strategy of organizing BUILD files
type Style rules.Style

const (
// goRulesBzl is the label of the Skylark file which provides Go rules
goRulesBzl = "@io_bazel_rules_go//go:def.bzl"
// StructuredStyle means that individual Go packages will have their own
// Bazel packages.
StructuredStyle = Style(rules.StructuredStyle)
// FlatStyle means that the given repoRoot has only one BUILD file for all
// the subpacakges.
FlatStyle = Style(rules.FlatStyle)
)

// Generator generates BUILD files for a Go repository.
type Generator struct {
repoRoot string
bctx build.Context
g rules.Generator
s Style
}

// New returns a new Generator which is responsible for a Go repository.
//
// "repoRoot" is a path to the root directory of the repository.
// "goPrefix" is the go_prefix corresponding to the repository root directory.
// See also https://github.com/bazelbuild/rules_go#go_prefix.
func New(repoRoot, goPrefix string) (*Generator, error) {
func New(repoRoot, goPrefix string, s Style) (*Generator, error) {
bctx := build.Default
// Ignore source files in $GOROOT and $GOPATH
bctx.GOROOT = ""
Expand All @@ -58,7 +66,8 @@ func New(repoRoot, goPrefix string) (*Generator, error) {
return &Generator{
repoRoot: filepath.Clean(repoRoot),
bctx: bctx,
g: rules.NewGenerator(goPrefix),
g: rules.NewGenerator(goPrefix, rules.Style(s)),
s: s,
}, nil
}

Expand All @@ -76,7 +85,7 @@ func (g *Generator) Generate(dir string) ([]*bzl.File, error) {
return nil, fmt.Errorf("dir %s is not under the repository root %s", dir, g.repoRoot)
}

var files []*bzl.File
b := builderForStyle(g.s)
err = packages.Walk(g.bctx, dir, func(pkg *build.Package) error {
rel, err := filepath.Rel(g.repoRoot, pkg.Dir)
if err != nil {
Expand All @@ -86,65 +95,27 @@ func (g *Generator) Generate(dir string) ([]*bzl.File, error) {
rel = ""
}

file, err := g.generateOne(rel, pkg)
rs, err := g.g.Generate(filepath.ToSlash(rel), pkg)
if err != nil {
return err
}

files = append(files, file)
b.addRules(rel, rs)
return nil
})
if err != nil {
return nil, err
}
return files, nil
}

func (g *Generator) generateOne(rel string, pkg *build.Package) (*bzl.File, error) {
rs, err := g.g.Generate(filepath.ToSlash(rel), pkg)
if err != nil {
return nil, err
}

file := &bzl.File{Path: filepath.Join(rel, "BUILD")}
for _, r := range rs {
file.Stmt = append(file.Stmt, r.Call)
}
if load := g.generateLoad(file); load != nil {
file.Stmt = append([]bzl.Expr{load}, file.Stmt...)
}
return file, nil
return b.files(), nil
}

func (g *Generator) generateLoad(f *bzl.File) bzl.Expr {
var list []string
for _, kind := range []string{
"go_prefix",
"go_library",
"go_binary",
"go_test",
// TODO(yugui): Support cgo_library
} {
if len(f.Rules(kind)) > 0 {
list = append(list, kind)
}
}
if len(list) == 0 {
return nil
}
return loadExpr(goRulesBzl, list...)
}

func loadExpr(ruleFile string, rules ...string) bzl.Expr {
var list []bzl.Expr
for _, r := range append([]string{ruleFile}, rules...) {
list = append(list, &bzl.StringExpr{Value: r})
}

return &bzl.CallExpr{
X: &bzl.LiteralExpr{Token: "load"},
List: list,
ForceCompact: true,
func builderForStyle(s Style) fileBuilder {
switch s {
case StructuredStyle:
return new(structuredFileBuilder)
case FlatStyle:
return new(flatFileBuilder)
default:
panic(fmt.Sprintf("unrecognized style: %d", s))
}
}

Expand Down
2 changes: 1 addition & 1 deletion go/tools/gazelle/generator/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestGenerator(t *testing.T) {
}

repo := filepath.Join(testdata.Dir(), "repo")
g, err := New(repo, "example.com/repo")
g, err := New(repo, "example.com/repo", StructuredStyle)
if err != nil {
t.Errorf(`New(%q, "example.com/repo") failed with %v; want success`, repo, err)
return
Expand Down
27 changes: 27 additions & 0 deletions go/tools/gazelle/generator/structured_file_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package generator

import (
"path/filepath"

bzl "github.com/bazelbuild/buildifier/core"
)

type structuredFileBuilder struct {
fs []*bzl.File
}

func (b *structuredFileBuilder) addRules(rel string, rules []*bzl.Rule) {
f := &bzl.File{Path: filepath.Join(rel, "BUILD")}
for _, r := range rules {
f.Stmt = append(f.Stmt, r.Call)
}
if load := generateLoad(f); load != nil {
f.Stmt = append([]bzl.Expr{load}, f.Stmt...)
}

b.fs = append(b.fs, f)
}

func (b *structuredFileBuilder) files() []*bzl.File {
return b.fs
}
2 changes: 2 additions & 0 deletions go/tools/gazelle/rules/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ go_library(
"generator.go",
"resolve.go",
"resolve_external.go",
"resolve_flat.go",
"resolve_structured.go",
],
visibility = ["//visibility:public"],
Expand All @@ -21,6 +22,7 @@ go_test(
name = "go_default_test",
srcs = [
"resolve_external_test.go",
"resolve_flat_test.go",
"resolve_structured_test.go",
"resolve_test.go",
],
Expand Down

0 comments on commit 5ebffb2

Please sign in to comment.