Skip to content

Commit

Permalink
Experimental: read and write build files in alternate directories (#286)
Browse files Browse the repository at this point in the history
* The flags -experimental_{read,write}_build_files_dir may now be used
  to read and write build files to alternate directories, which may be
  outside of the repository root.
* When a build file is read from an alternate directory, the build
  file in the source directory is ignored.
* When a build file is written to an alternate directory, any existing
  build file in that directory is replaced. The build file in the
  source directory is not updated.
  • Loading branch information
jayconrod authored Aug 6, 2018
1 parent bfb9a4d commit 6a1b93c
Show file tree
Hide file tree
Showing 26 changed files with 315 additions and 136 deletions.
11 changes: 4 additions & 7 deletions cmd/gazelle/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,18 @@ import (
"io/ioutil"
"os"
"os/exec"

"github.com/bazelbuild/bazel-gazelle/internal/config"
bzl "github.com/bazelbuild/buildtools/build"
"path/filepath"
)

func diffFile(c *config.Config, file *bzl.File, path string) error {
oldContents, err := ioutil.ReadFile(file.Path)
func diffFile(path string, newContents []byte) error {
oldContents, err := ioutil.ReadFile(path)
if err != nil {
oldContents = nil
}
newContents := bzl.Format(file)
if bytes.Equal(oldContents, newContents) {
return nil
}
f, err := ioutil.TempFile("", c.DefaultBuildFileName())
f, err := ioutil.TempFile("", filepath.Base(path))
if err != nil {
return err
}
Expand Down
54 changes: 33 additions & 21 deletions cmd/gazelle/fix-update.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
Expand All @@ -31,19 +32,17 @@ import (
"github.com/bazelbuild/bazel-gazelle/internal/resolve"
"github.com/bazelbuild/bazel-gazelle/internal/rule"
"github.com/bazelbuild/bazel-gazelle/internal/walk"
bzl "github.com/bazelbuild/buildtools/build"
)

// updateConfig holds configuration information needed to run the fix and
// update commands. This includes everything in config.Config, but it also
// includes some additional fields that aren't relevant to other packages.
type updateConfig struct {
emit emitFunc
outDir, outSuffix string
repos []repos.Repo
emit emitFunc
repos []repos.Repo
}

type emitFunc func(*config.Config, *bzl.File, string) error
type emitFunc func(path string, data []byte) error

var modeFromName = map[string]emitFunc{
"print": printFile,
Expand All @@ -68,8 +67,6 @@ func (ucr *updateConfigurer) RegisterFlags(fs *flag.FlagSet, cmd string, c *conf
c.ShouldFix = cmd == "fix"

fs.StringVar(&ucr.mode, "mode", "fix", "print: prints all of the updated BUILD files\n\tfix: rewrites all of the BUILD files in place\n\tdiff: computes the rewrite but then just does a diff")
fs.StringVar(&uc.outDir, "experimental_out_dir", "", "write build files to an alternate directory tree")
fs.StringVar(&uc.outSuffix, "experimental_out_suffix", "", "extra suffix appended to build file names. Only used if -experimental_out_dir is also set.")
}

func (ucr *updateConfigurer) CheckFlags(fs *flag.FlagSet, c *config.Config) error {
Expand Down Expand Up @@ -198,7 +195,7 @@ func runFixUpdate(cmd command, args []string) error {

// Insert or merge rules into the build file.
if f == nil {
f = rule.EmptyFile(filepath.Join(dir, c.DefaultBuildFileName()))
f = rule.EmptyFile(filepath.Join(dir, c.DefaultBuildFileName()), rel)
for _, r := range gen {
r.Insert(f)
}
Expand Down Expand Up @@ -236,15 +233,9 @@ func runFixUpdate(cmd command, args []string) error {
// Emit merged files.
for _, v := range visits {
merger.FixLoads(v.file, loads)
v.file.Sync()
bzl.Rewrite(v.file.File, nil) // have buildifier 'format' our rules.

path := v.file.Path
if uc.outDir != "" {
stem := filepath.Base(v.file.Path) + uc.outSuffix
path = filepath.Join(uc.outDir, v.pkgRel, stem)
}
if err := uc.emit(c, v.file.File, path); err != nil {
content := v.file.Format()
outputPath := findOutputPath(c, v.file)
if err := uc.emit(outputPath, content); err != nil {
log.Print(err)
}
}
Expand All @@ -269,7 +260,7 @@ func newFixUpdateConfiguration(cmd command, args []string, cexts []config.Config
if err := fs.Parse(args); err != nil {
if err == flag.ErrHelp {
fixUpdateUsage(fs)
os.Exit(0)
return nil, err
}
// flag already prints the error; don't print it again.
log.Fatal("Try -help for more information.")
Expand All @@ -283,7 +274,7 @@ func newFixUpdateConfiguration(cmd command, args []string, cexts []config.Config

uc := getUpdateConfig(c)
workspacePath := filepath.Join(c.RepoRoot, "WORKSPACE")
if workspace, err := rule.LoadFile(workspacePath); err != nil {
if workspace, err := rule.LoadFile(workspacePath, ""); err != nil {
if !os.IsNotExist(err) {
return nil, err
}
Expand Down Expand Up @@ -361,8 +352,7 @@ func fixWorkspace(c *config.Config, workspace *rule.File, loads []rule.LoadInfo)
if err := merger.CheckGazelleLoaded(workspace); err != nil {
return err
}
workspace.Sync()
return uc.emit(c, workspace.File, workspace.Path)
return uc.emit(workspace.Path, workspace.Format())
}

func findWorkspaceName(f *rule.File) string {
Expand All @@ -384,3 +374,25 @@ func isDescendingDir(dir, root string) bool {
}
return !strings.HasPrefix(rel, "..")
}

func findOutputPath(c *config.Config, f *rule.File) string {
if c.ReadBuildFilesDir == "" && c.WriteBuildFilesDir == "" {
return f.Path
}
baseDir := c.WriteBuildFilesDir
if c.WriteBuildFilesDir == "" {
baseDir = c.RepoRoot
}
outputDir := filepath.Join(baseDir, filepath.FromSlash(f.Pkg))
defaultOutputPath := filepath.Join(outputDir, c.DefaultBuildFileName())
files, err := ioutil.ReadDir(outputDir)
if err != nil {
// Ignore error. Directory probably doesn't exist.
return defaultOutputPath
}
outputPath := rule.MatchBuildFileName(outputDir, c.ValidBuildFileNames, files)
if outputPath == "" {
return defaultOutputPath
}
return outputPath
}
10 changes: 2 additions & 8 deletions cmd/gazelle/fix.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,11 @@ import (
"io/ioutil"
"os"
"path/filepath"

"github.com/bazelbuild/bazel-gazelle/internal/config"
bzl "github.com/bazelbuild/buildtools/build"
)

func fixFile(c *config.Config, file *bzl.File, path string) error {
func fixFile(path string, data []byte) error {
if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
return err
}
if err := ioutil.WriteFile(path, bzl.Format(file), 0666); err != nil {
return err
}
return nil
return ioutil.WriteFile(path, data, 0666)
}
160 changes: 156 additions & 4 deletions cmd/gazelle/fix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"

"github.com/bazelbuild/bazel-gazelle/internal/config"
bzl "github.com/bazelbuild/buildtools/build"
)

Expand Down Expand Up @@ -63,9 +63,7 @@ func TestFixFile(t *testing.T) {
},
},
}
c := &config.Config{}

if err := fixFile(c, stubFile, stubFile.Path); err != nil {
if err := fixFile(stubFile.Path, bzl.Format(stubFile)); err != nil {
t.Errorf("fixFile(%#v) failed with %v; want success", stubFile, err)
return
}
Expand Down Expand Up @@ -134,3 +132,157 @@ func TestUpdateFile(t *testing.T) {
t.Errorf("BUILD.bazel should not exist")
}
}

func TestReadWriteDir(t *testing.T) {
buildInFile := fileSpec{
path: "in/BUILD.in",
content: `
go_binary(
name = "hello",
pure = "on",
)
`,
}
buildSrcFile := fileSpec{
path: "src/BUILD.bazel",
content: `# src build file`,
}
oldFiles := []fileSpec{
buildInFile,
buildSrcFile,
{
path: "src/hello.go",
content: `
package main
func main() {}
`,
}, {
path: "out/BUILD",
content: `this should get replaced`,
},
}

for _, tc := range []struct {
desc string
args []string
want []fileSpec
}{
{
desc: "read",
args: []string{
"-repo_root={{dir}}/src",
"-experimental_read_build_files_dir={{dir}}/in",
"-build_file_name=BUILD.bazel,BUILD,BUILD.in",
"-go_prefix=example.com/repo",
"{{dir}}/src",
},
want: []fileSpec{
buildInFile,
{
path: "src/BUILD.bazel",
content: `
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_binary(
name = "hello",
embed = [":go_default_library"],
pure = "on",
visibility = ["//visibility:public"],
)
go_library(
name = "go_default_library",
srcs = ["hello.go"],
importpath = "example.com/repo",
visibility = ["//visibility:private"],
)
`,
},
},
}, {
desc: "write",
args: []string{
"-repo_root={{dir}}/src",
"-experimental_write_build_files_dir={{dir}}/out",
"-build_file_name=BUILD.bazel,BUILD,BUILD.in",
"-go_prefix=example.com/repo",
"{{dir}}/src",
},
want: []fileSpec{
buildInFile,
buildSrcFile,
{
path: "out/BUILD",
content: `
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
# src build file
go_library(
name = "go_default_library",
srcs = ["hello.go"],
importpath = "example.com/repo",
visibility = ["//visibility:private"],
)
go_binary(
name = "repo",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
`,
},
},
}, {
desc: "read_and_write",
args: []string{
"-repo_root={{dir}}/src",
"-experimental_read_build_files_dir={{dir}}/in",
"-experimental_write_build_files_dir={{dir}}/out",
"-build_file_name=BUILD.bazel,BUILD,BUILD.in",
"-go_prefix=example.com/repo",
"{{dir}}/src",
},
want: []fileSpec{
buildInFile,
{
path: "out/BUILD",
content: `
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_binary(
name = "hello",
embed = [":go_default_library"],
pure = "on",
visibility = ["//visibility:public"],
)
go_library(
name = "go_default_library",
srcs = ["hello.go"],
importpath = "example.com/repo",
visibility = ["//visibility:private"],
)
`,
},
},
},
} {
t.Run(tc.desc, func(t *testing.T) {
dir, err := createFiles(oldFiles)
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
replacer := strings.NewReplacer("{{dir}}", dir, "/", string(os.PathSeparator))
for i := range tc.args {
tc.args[i] = replacer.Replace(tc.args[i])
}
if err := run(tc.args); err != nil {
t.Error(err)
}
checkFiles(t, dir, tc.want)
})
}
}
6 changes: 4 additions & 2 deletions cmd/gazelle/gazelle.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.
package main

import (
"flag"
"fmt"
"log"
"os"
Expand Down Expand Up @@ -76,7 +77,7 @@ func run(args []string) error {
case fixCmd, updateCmd:
return runFixUpdate(cmd, args)
case helpCmd:
help()
return help()
case updateReposCmd:
return updateRepos(args)
default:
Expand All @@ -85,7 +86,7 @@ func run(args []string) error {
return nil
}

func help() {
func help() error {
fmt.Fprint(os.Stderr, `usage: gazelle <command> [args...]
Gazelle is a BUILD file generator for Go projects. It can create new BUILD files
Expand Down Expand Up @@ -115,4 +116,5 @@ Gazelle is under active delevopment, and its interface may change
without notice.
`)
return flag.ErrHelp
}
Loading

0 comments on commit 6a1b93c

Please sign in to comment.