Commit 1fee7b15 authored by Tim Hockin's avatar Tim Hockin Committed by Jeff Grafton

Vendor kazel

parent 768ebcd4
......@@ -8,6 +8,7 @@
"github.com/tools/godep",
"github.com/client9/misspell/cmd/misspell",
"github.com/bazelbuild/bazel-gazelle/cmd/gazelle",
"github.com/kubernetes/repo-infra/kazel",
"./..."
],
"Deps": [
......@@ -2100,6 +2101,10 @@
"Rev": "6807e777504f54ad073ecef66747de158294b639"
},
{
"ImportPath": "github.com/kubernetes/repo-infra/kazel",
"Rev": "2a736b4fba317cf3038e3cbd06899b544b875fae"
},
{
"ImportPath": "github.com/libopenstorage/openstorage/api",
"Rev": "093a0c3888753c2056e7373183693d670c6bba01"
},
......
......@@ -60,6 +60,7 @@ REQUIRED_BINS=(
"github.com/tools/godep"
"github.com/client9/misspell/cmd/misspell"
"github.com/bazelbuild/bazel-gazelle/cmd/gazelle"
"github.com/kubernetes/repo-infra/kazel"
"./..."
)
......
......@@ -33,10 +33,7 @@ PATH="${GOBIN}:${PATH}"
# Install tools we need, but only from vendor/...
go install ./vendor/github.com/bazelbuild/bazel-gazelle/cmd/gazelle
# The git commit sha1s here should match the values in $KUBE_ROOT/WORKSPACE.
kube::util::go_install_from_commit \
github.com/kubernetes/repo-infra/kazel \
97099dccc8807e9159dc28f374a8f0602cab07e1
go install ./vendor/github.com/kubernetes/repo-infra/kazel
touch "${KUBE_ROOT}/vendor/BUILD"
......
......@@ -17,6 +17,7 @@ openapi_library(
"pkg/kubelet/apis/kubeletconfig/v1beta1",
"pkg/proxy/apis/kubeproxyconfig/v1alpha1",
"pkg/version",
"vendor/github.com/kubernetes/repo-infra/kazel",
],
tags = ["automanaged"],
vendor_prefix = openapi_vendor_prefix,
......
......@@ -288,6 +288,7 @@ filegroup(
"//vendor/github.com/kr/pretty:all-srcs",
"//vendor/github.com/kr/pty:all-srcs",
"//vendor/github.com/kr/text:all-srcs",
"//vendor/github.com/kubernetes/repo-infra/kazel:all-srcs",
"//vendor/github.com/libopenstorage/openstorage/api:all-srcs",
"//vendor/github.com/libopenstorage/openstorage/pkg/parser:all-srcs",
"//vendor/github.com/libopenstorage/openstorage/pkg/units:all-srcs",
......
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "kazel",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = [
"config.go",
"diff.go",
"generator.go",
"kazel.go",
"sourcerer.go",
],
importpath = "github.com/kubernetes/repo-infra/kazel",
tags = ["automanaged"],
deps = [
"//vendor/github.com/bazelbuild/buildtools/build:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
kazel - a BUILD file generator for go and bazel
===============================================
Requirements:
#############
* Your project must be somewhat compatible with go tool because
kazel uses go tool to parse your import tree.
* You must have a **GOPATH** and **GOROOT** setup and your project must
be in the correct location in your **GOPATH**.
* Your ``./vendor`` directory may not contain ``BUILD`` files.
Usage:
######
1. Get kazel by running ``go get k8s.io/repo-infra/kazel``.
2. Create a ``.kazelcfg.json`` in the root of the repository. For the
kazel repository, the ``.kazelcfg.json`` would look like:
.. code-block:: json
{
"GoPrefix": "k8s.io/repo-infra",
"SrcDirs": [
"./kazel"
],
"SkippedPaths": [
".*foobar(baz)?.*$"
]
}
3. Run kazel:
.. code-block:: bash
$ kazel -root=$GOPATH/src/k8s.io/repo-infra
Defaults:
#########
* **SrcDirs** in ``.kazelcfg.json`` defaults to ``["./"]``
* ``-root`` option defaults to the current working directory
Automanagement:
###############
kazel reconciles rules that have the "**automanaged**" tag. If
you no longer want kazel to manage a rule, you can remove the
**automanaged** tag and kazel will no longer manage that rule.
kazel only manages srcs, deps, and library attributes of a
rule after initial creation so you can add and managed other
attributes like data and copts and kazel will respect your
changes.
kazel automatically formats all ``BUILD`` files in your repository
except for those matching **SkippedPaths**.
Adding "sources" rules:
#######################
If you set "**AddSourcesRules**": ``true`` in your ``.kazelcfg.json``,
kazel will create "**package-srcs**" and "**all-srcs**" rules in every
package.
The "**package-srcs**" rule is a glob matching all files in the
package recursively, but not any files owned by packages in
subdirectories.
The "**all-srcs**" rule includes both the "**package-srcs**" rule and
the "**all-srcs**" rules of all subpackages; i.e. **//:all-srcs** will
include all files in your repository.
The "**package-srcs**" rule defaults to private visibility,
since it is safer to depend on the "**all-srcs**" rule: if a
subpackage is added, the "**package-srcs**" rule will no longer
include those files.
You can remove the "**automanaged**" tag from the "**package-srcs**"
rule if you need to modify the glob (such as adding excludes).
It's recommended that you leave the "**all-srcs**" rule
automanaged.
Validating BUILD files in CI:
#############################
If you run kazel with ``--validate``, it will not update any ``BUILD`` files, but it
will exit nonzero if any ``BUILD`` files are out-of-date. You can add ``--print-diff``
to print out the changes needed.
/*
Copyright 2017 The Kubernetes Authors.
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 main
import (
"encoding/json"
"io/ioutil"
)
// Cfg defines the configuration options for kazel.
type Cfg struct {
GoPrefix string
// evaluated recursively, defaults to ["."]
SrcDirs []string
// regexps that match packages to skip
SkippedPaths []string
// whether to add "pkg-srcs" and "all-srcs" filegroups
// note that this operates on the entire tree (not just SrcsDirs) but skips anything matching SkippedPaths
AddSourcesRules bool
// whether to have multiple build files in vendor/ or just one.
VendorMultipleBuildFiles bool
// whether to manage kubernetes' pkg/generated/openapi.
K8sOpenAPIGen bool
// Whether to manage the upstream Go rules provided by bazelbuild/rules_go.
// If using gazelle, set this to false (or omit).
ManageGoRules bool
}
// ReadCfg reads and unmarshals the specified json file into a Cfg struct.
func ReadCfg(cfgPath string) (*Cfg, error) {
b, err := ioutil.ReadFile(cfgPath)
if err != nil {
return nil, err
}
var cfg Cfg
if err := json.Unmarshal(b, &cfg); err != nil {
return nil, err
}
defaultCfg(&cfg)
return &cfg, nil
}
func defaultCfg(c *Cfg) {
if len(c.SrcDirs) == 0 {
c.SrcDirs = []string{"."}
}
}
/*
Copyright 2017 The Kubernetes Authors.
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 main
import (
"io/ioutil"
"os"
"os/exec"
)
// Diff prints the unified diff of the two provided byte slices
// using the unix diff command.
func Diff(left, right []byte) error {
lf, err := ioutil.TempFile("/tmp", "actual-file-")
if err != nil {
return err
}
defer lf.Close()
defer os.Remove(lf.Name())
rf, err := ioutil.TempFile("/tmp", "expected-file-")
if err != nil {
return err
}
defer rf.Close()
defer os.Remove(rf.Name())
_, err = lf.Write(left)
if err != nil {
return err
}
lf.Close()
_, err = rf.Write(right)
if err != nil {
return err
}
rf.Close()
cmd := exec.Command("/usr/bin/diff", "-u", lf.Name(), rf.Name())
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
return nil
}
/*
Copyright 2017 The Kubernetes Authors.
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 main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
)
const (
openAPIGenTag = "// +k8s:openapi-gen"
staging = "staging/src/"
)
// walkGenerated updates the rule for kubernetes' OpenAPI generated file.
// This involves reading all go files in the source tree and looking for the
// "+k8s:openapi-gen" tag. If present, then that package must be supplied to
// the genrule.
func (v *Vendorer) walkGenerated() error {
if !v.cfg.K8sOpenAPIGen {
return nil
}
v.managedAttrs = append(v.managedAttrs, "openapi_targets", "vendor_targets")
paths, err := v.findOpenAPI(".")
if err != nil {
return err
}
return v.addGeneratedOpenAPIRule(paths)
}
// findOpenAPI searches for all packages under root that request OpenAPI. It
// returns the go import paths. It does not follow symlinks.
func (v *Vendorer) findOpenAPI(root string) ([]string, error) {
for _, r := range v.skippedPaths {
if r.MatchString(root) {
return nil, nil
}
}
finfos, err := ioutil.ReadDir(root)
if err != nil {
return nil, err
}
var res []string
var includeMe bool
for _, finfo := range finfos {
path := filepath.Join(root, finfo.Name())
if finfo.IsDir() && (finfo.Mode()&os.ModeSymlink == 0) {
children, err := v.findOpenAPI(path)
if err != nil {
return nil, err
}
res = append(res, children...)
} else if strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, "_test.go") {
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
if bytes.Contains(b, []byte(openAPIGenTag)) {
includeMe = true
}
}
}
if includeMe {
pkg, err := v.ctx.ImportDir(filepath.Join(v.root, root), 0)
if err != nil {
return nil, err
}
res = append(res, pkg.ImportPath)
}
return res, nil
}
// addGeneratedOpenAPIRule updates the pkg/generated/openapi go_default_library
// rule with the automanaged openapi_targets and vendor_targets.
func (v *Vendorer) addGeneratedOpenAPIRule(paths []string) error {
var openAPITargets []string
var vendorTargets []string
baseImport := v.cfg.GoPrefix + "/"
for _, p := range paths {
if !strings.HasPrefix(p, baseImport) {
return fmt.Errorf("openapi-gen path outside of %s: %s", v.cfg.GoPrefix, p)
}
np := p[len(baseImport):]
if strings.HasPrefix(np, staging) {
vendorTargets = append(vendorTargets, np[len(staging):])
} else {
openAPITargets = append(openAPITargets, np)
}
}
sort.Strings(openAPITargets)
sort.Strings(vendorTargets)
pkgPath := filepath.Join("pkg", "generated", "openapi")
// If we haven't walked this package yet, walk it so there is a go_library rule to modify
if len(v.newRules[pkgPath]) == 0 {
if err := v.updateSinglePkg(pkgPath); err != nil {
return err
}
}
for _, r := range v.newRules[pkgPath] {
if r.Name() == "go_default_library" {
r.SetAttr("openapi_targets", asExpr(openAPITargets))
r.SetAttr("vendor_targets", asExpr(vendorTargets))
break
}
}
return nil
}
/*
Copyright 2017 The Kubernetes Authors.
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 main
import (
"fmt"
"io/ioutil"
"path/filepath"
bzl "github.com/bazelbuild/buildtools/build"
)
const (
pkgSrcsTarget = "package-srcs"
allSrcsTarget = "all-srcs"
)
// walkSource walks the source tree recursively from pkgPath, adding
// any BUILD files to v.newRules to be formatted.
//
// If AddSourcesRules is enabled in the kazel config, then we additionally add
// package-sources and recursive all-srcs filegroups rules to every BUILD file.
//
// Returns the list of children all-srcs targets that should be added to the
// all-srcs rule of the enclosing package.
func (v *Vendorer) walkSource(pkgPath string) ([]string, error) {
// clean pkgPath since we access v.newRules directly
pkgPath = filepath.Clean(pkgPath)
for _, r := range v.skippedPaths {
if r.MatchString(pkgPath) {
return nil, nil
}
}
files, err := ioutil.ReadDir(pkgPath)
if err != nil {
return nil, err
}
// Find any children packages we need to include in an all-srcs rule.
var children []string
for _, f := range files {
if f.IsDir() {
c, err := v.walkSource(filepath.Join(pkgPath, f.Name()))
if err != nil {
return nil, err
}
children = append(children, c...)
}
}
// This path is a package either if we've added rules or if a BUILD file already exists.
_, hasRules := v.newRules[pkgPath]
isPkg := hasRules
if !isPkg {
isPkg, _ = findBuildFile(pkgPath)
}
if !isPkg {
// This directory isn't a package (doesn't contain a BUILD file),
// but there might be subdirectories that are packages,
// so pass that up to our parent.
return children, nil
}
// Enforce formatting the BUILD file, even if we're not adding srcs rules
if !hasRules {
v.addRules(pkgPath, nil)
}
if !v.cfg.AddSourcesRules {
return nil, nil
}
pkgSrcsExpr := &bzl.LiteralExpr{Token: `glob(["**"])`}
if pkgPath == "." {
pkgSrcsExpr = &bzl.LiteralExpr{Token: `glob(["**"], exclude=["bazel-*/**", ".git/**"])`}
}
v.addRules(pkgPath, []*bzl.Rule{
newRule(RuleTypeFileGroup,
func(_ ruleType) string { return pkgSrcsTarget },
map[string]bzl.Expr{
"srcs": pkgSrcsExpr,
"visibility": asExpr([]string{"//visibility:private"}),
}),
newRule(RuleTypeFileGroup,
func(_ ruleType) string { return allSrcsTarget },
map[string]bzl.Expr{
"srcs": asExpr(append(children, fmt.Sprintf(":%s", pkgSrcsTarget))),
// TODO: should this be more restricted?
"visibility": asExpr([]string{"//visibility:public"}),
}),
})
return []string{fmt.Sprintf("//%s:%s", pkgPath, allSrcsTarget)}, nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment