Skip to content

Commit

Permalink
Import minimal version of gazel, a BUILD file generator
Browse files Browse the repository at this point in the history
  • Loading branch information
yugui committed Aug 3, 2016
1 parent a994fbd commit 1253f28
Show file tree
Hide file tree
Showing 20 changed files with 917 additions and 0 deletions.
11 changes: 11 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,14 @@ new_git_repository(
commit = "23def4e6c14b4da8ac2ed8007337bc5eb5007998",
remote = "https://github.com/golang/glog.git",
)

git_repository(
name = "io_bazel_buildifier",
commit = "0ca1d7991357ae7a7555589af88930d82cf07c0a",
remote = "https://github.com/bazelbuild/buildifier.git",
)

local_repository(
name = "io_bazel_rules_go",
path = ".",
)
10 changes: 10 additions & 0 deletions go/tools/gazel/gazel/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary")

go_binary(
name = "gazel",
srcs = ["main.go"],
deps = [
"@io_bazel_buildifier//core:go_default_library",
"//go/tools/gazel/generator:go_default_library",
],
)
86 changes: 86 additions & 0 deletions go/tools/gazel/gazel/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/* Copyright 2016 The Bazel Authors. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Command gazel is a BUILD file generator for Go projects.
// See "gazel --help" for more details.
package main

import (
"flag"
"fmt"
"log"
"os"

bzl "github.com/bazelbuild/buildifier/core"
"github.com/bazelbuild/rules_go/go/tools/gazel/generator"
)

var (
repoRoot = flag.String("repo_root", "", "path to a root directory of a repository")
)

func run(dirs []string) error {
g, err := generator.New(*repoRoot)
if err != nil {
return err
}

for _, d := range dirs {
files, err := g.Generate(d)
if err != nil {
return err
}
for _, f := range files {
if _, err := os.Stdout.Write(bzl.Format(f)); err != nil {
return err
}
}
}
return nil
}

func usage() {
fmt.Fprintln(os.Stderr, `usage: gazel [flags...] [package-dirs...]
Gazel is a BUILD file generator for Go projects.
Currently its primary usage is to generate BUILD files for external dependencies
in a go_vendor repository rule.
You can still use Gazel for other purposes, but its interface can change without
notice.
It takes a list of paths to Go package directories.
It recursively traverses its subpackages.
All the directories must be under the directory specified in -repo_root.
FLAGS:
`)
flag.PrintDefaults()
}

func main() {
flag.Usage = usage
flag.Parse()

if *repoRoot == "" {
if flag.NArg() != 1 {
log.Fatal("-repo_root is required")
}
*repoRoot = flag.Arg(0)
}
if err := run(flag.Args()); err != nil {
log.Fatal(err)
}
}
21 changes: 21 additions & 0 deletions go/tools/gazel/generator/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = ["generator.go"],
visibility = ["//visibility:public"],
deps = [
"@io_bazel_buildifier//core:go_default_library",
"//go/tools/gazel/packages:go_default_library",
"//go/tools/gazel/rules:go_default_library",
],
)

go_test(
name = "go_default_test",
srcs = ["generator_test.go"],
library = ":go_default_library",
deps = [
"//go/tools/gazel/testdata:go_default_library",
],
)
104 changes: 104 additions & 0 deletions go/tools/gazel/generator/generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/* Copyright 2016 The Bazel Authors. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package generator provides core functionality of
// BUILD file generation in gazel.
package generator

import (
"fmt"
"go/build"
"path/filepath"
"strings"

bzl "github.com/bazelbuild/buildifier/core"
"github.com/bazelbuild/rules_go/go/tools/gazel/packages"
"github.com/bazelbuild/rules_go/go/tools/gazel/rules"
)

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

// New returns a new Generator which is responsible for a Go repository.
//
// "repoRoot" is a path to the root directory of the repository.
func New(repoRoot string) (*Generator, error) {
bctx := build.Default
// Ignore source files in $GOROOT and $GOPATH
bctx.GOROOT = ""
bctx.GOPATH = ""

repoRoot, err := filepath.Abs(repoRoot)
if err != nil {
return nil, err
}
return &Generator{
repoRoot: filepath.Clean(repoRoot),
bctx: bctx,
g: rules.NewGenerator(),
}, nil
}

// Generate generates a BUILD file for each Go package found under
// the given directory.
// The directory must be the repository root directory the caller
// passed to New, or its subdirectory.
func (g *Generator) Generate(dir string) ([]*bzl.File, error) {
dir, err := filepath.Abs(dir)
if err != nil {
return nil, err
}
dir = filepath.Clean(dir)
if !isDescendingDir(dir, g.repoRoot) {
return nil, fmt.Errorf("dir %s is not under the repository root %s", dir, g.repoRoot)
}

var files []*bzl.File
err = packages.Walk(g.bctx, dir, func(pkg *build.Package) error {
rel, err := filepath.Rel(g.repoRoot, pkg.Dir)
if err != nil {
return err
}
if rel == "." {
rel = ""
}

rs, err := g.g.Generate(filepath.ToSlash(rel), pkg)
if err != nil {
return err
}
file := &bzl.File{Path: filepath.Join(rel, "BUILD")}
for _, r := range rs {
file.Stmt = append(file.Stmt, r.Call)
}
files = append(files, file)
return nil
})
if err != nil {
return nil, err
}
return files, nil
}

func isDescendingDir(dir, root string) bool {
if dir == root {
return true
}
return strings.HasPrefix(dir, fmt.Sprintf("%s%c", root, filepath.Separator))
}
105 changes: 105 additions & 0 deletions go/tools/gazel/generator/generator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* Copyright 2016 The Bazel Authors. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package generator

import (
"fmt"
"go/build"
"path/filepath"
"reflect"
"sort"
"strings"
"testing"

bzl "github.com/bazelbuild/buildifier/core"
"github.com/bazelbuild/rules_go/go/tools/gazel/testdata"
)

func TestGenerator(t *testing.T) {
stub := stubRuleGen{
fixtures: map[string][]*bzl.Rule{
"lib": {
{
Call: &bzl.CallExpr{
X: &bzl.LiteralExpr{Token: "go_library"},
},
},
},
"bin": {
{
Call: &bzl.CallExpr{
X: &bzl.LiteralExpr{Token: "go_binary"},
},
},
},
},
}

repo := filepath.Join(testdata.Dir(), "repo")
g, err := New(repo)
if err != nil {
t.Errorf("New(%q) failed with %v; want success", repo, err)
return
}
g.g = stub

got, err := g.Generate(repo)
if err != nil {
t.Errorf("g.Generate(%q) failed with %v; want success", repo, err)
}
sort.Sort(fileSlice(got))

want := []*bzl.File{
{
Path: "lib/BUILD",
Stmt: []bzl.Expr{stub.fixtures["lib"][0].Call},
},
{
Path: "bin/BUILD",
Stmt: []bzl.Expr{stub.fixtures["bin"][0].Call},
},
}
sort.Sort(fileSlice(want))

if !reflect.DeepEqual(got, want) {
t.Errorf("g.Generate(%q) = %v; want %v", repo, prettyFiles(got), prettyFiles(want))
}
}

type prettyFiles []*bzl.File

func (p prettyFiles) String() string {
var items []string
for _, f := range p {
items = append(items, fmt.Sprintf("{Path: %q, Stmt: %q", f.Path, string(bzl.Format(f))))
}
return fmt.Sprintf("[%s]", strings.Join(items, ","))
}

type fileSlice []*bzl.File

func (p fileSlice) Less(i, j int) bool { return strings.Compare(p[i].Path, p[j].Path) < 0 }
func (p fileSlice) Len() int { return len(p) }
func (p fileSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

// stubRuleGen is a test stub implementation of rules.Generator
type stubRuleGen struct {
fixtures map[string][]*bzl.Rule
}

func (s stubRuleGen) Generate(rel string, pkg *build.Package) ([]*bzl.Rule, error) {
return s.fixtures[rel], nil
}
16 changes: 16 additions & 0 deletions go/tools/gazel/packages/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
load("//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = [
"doc.go",
"walk.go",
],
visibility = ["//visibility:public"],
)

go_test(
name = "go_default_xtest",
srcs = ["walk_test.go"],
deps = [":go_default_library"],
)
17 changes: 17 additions & 0 deletions go/tools/gazel/packages/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* Copyright 2016 The Bazel Authors. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package packages provides Go package traversal in a Bazel repository.
package packages
Loading

0 comments on commit 1253f28

Please sign in to comment.