Commit 768ebcd4 authored by Tim Hockin's avatar Tim Hockin Committed by Jeff Grafton

Vendor gazelle

parent 67a44fbc
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
"github.com/jteeuwen/go-bindata/go-bindata", "github.com/jteeuwen/go-bindata/go-bindata",
"github.com/tools/godep", "github.com/tools/godep",
"github.com/client9/misspell/cmd/misspell", "github.com/client9/misspell/cmd/misspell",
"github.com/bazelbuild/bazel-gazelle/cmd/gazelle",
"./..." "./..."
], ],
"Deps": [ "Deps": [
...@@ -331,6 +332,71 @@ ...@@ -331,6 +332,71 @@
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e" "Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
}, },
{ {
"ImportPath": "github.com/bazelbuild/bazel-gazelle/cmd/gazelle",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/bazel-gazelle/internal/config",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/bazel-gazelle/internal/label",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/bazel-gazelle/internal/merger",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/bazel-gazelle/internal/packages",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/bazel-gazelle/internal/pathtools",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/bazel-gazelle/internal/repos",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/bazel-gazelle/internal/resolve",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/bazel-gazelle/internal/rules",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/bazel-gazelle/internal/version",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/bazel-gazelle/internal/wspace",
"Comment": "0.10.1",
"Rev": "ff4d67f395b3f4d1dc9810eeceb61585fea252cb"
},
{
"ImportPath": "github.com/bazelbuild/buildtools/build",
"Comment": "0.6.0-60-g1a9c38e",
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
},
{
"ImportPath": "github.com/bazelbuild/buildtools/tables",
"Comment": "0.6.0-60-g1a9c38e",
"Rev": "1a9c38e0df9397d033a1ca535596de5a7c1cf18f"
},
{
"ImportPath": "github.com/beorn7/perks/quantile", "ImportPath": "github.com/beorn7/perks/quantile",
"Rev": "3ac7bf7a47d159a033b107610db8a1b6575507a4" "Rev": "3ac7bf7a47d159a033b107610db8a1b6575507a4"
}, },
...@@ -2448,14 +2514,9 @@ ...@@ -2448,14 +2514,9 @@
"Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4" "Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
}, },
{ {
"ImportPath": "github.com/pelletier/go-buffruneio",
"Comment": "v0.1.0",
"Rev": "df1e16fde7fc330a0ca68167c23bf7ed6ac31d6d"
},
{
"ImportPath": "github.com/pelletier/go-toml", "ImportPath": "github.com/pelletier/go-toml",
"Comment": "v0.3.5-10-g0049ab3", "Comment": "v1.0.1",
"Rev": "0049ab3dc4c4c70a9eee23087437b69c0dde2130" "Rev": "16398bac157da96aa88f98a2df640c7f32af1da2"
}, },
{ {
"ImportPath": "github.com/peterbourgon/diskv", "ImportPath": "github.com/peterbourgon/diskv",
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -58,7 +58,8 @@ filegroup( ...@@ -58,7 +58,8 @@ filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [ srcs = [
":package-srcs", ":package-srcs",
"//api:all-srcs", "//api/openapi-spec:all-srcs",
"//api/swagger-spec:all-srcs",
"//build:all-srcs", "//build:all-srcs",
"//cluster:all-srcs", "//cluster:all-srcs",
"//cmd:all-srcs", "//cmd:all-srcs",
......
...@@ -59,6 +59,7 @@ REQUIRED_BINS=( ...@@ -59,6 +59,7 @@ REQUIRED_BINS=(
"github.com/jteeuwen/go-bindata/go-bindata" "github.com/jteeuwen/go-bindata/go-bindata"
"github.com/tools/godep" "github.com/tools/godep"
"github.com/client9/misspell/cmd/misspell" "github.com/client9/misspell/cmd/misspell"
"github.com/bazelbuild/bazel-gazelle/cmd/gazelle"
"./..." "./..."
) )
......
...@@ -26,13 +26,17 @@ kube::util::ensure-gnu-sed ...@@ -26,13 +26,17 @@ kube::util::ensure-gnu-sed
# TODO(spxtr): Remove this line once Bazel is the only way to build. # TODO(spxtr): Remove this line once Bazel is the only way to build.
rm -f "${KUBE_ROOT}/pkg/generated/openapi/zz_generated.openapi.go" rm -f "${KUBE_ROOT}/pkg/generated/openapi/zz_generated.openapi.go"
# Ensure that we find the binaries we build before anything else.
export GOBIN="${KUBE_OUTPUT_BINPATH}"
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. # The git commit sha1s here should match the values in $KUBE_ROOT/WORKSPACE.
kube::util::go_install_from_commit \ kube::util::go_install_from_commit \
github.com/kubernetes/repo-infra/kazel \ github.com/kubernetes/repo-infra/kazel \
97099dccc8807e9159dc28f374a8f0602cab07e1 97099dccc8807e9159dc28f374a8f0602cab07e1
kube::util::go_install_from_commit \
github.com/bazelbuild/bazel-gazelle/cmd/gazelle \
0.10.1
touch "${KUBE_ROOT}/vendor/BUILD" touch "${KUBE_ROOT}/vendor/BUILD"
......
...@@ -40,9 +40,9 @@ mkdir -p "${_tmp_kuberoot}/.." ...@@ -40,9 +40,9 @@ mkdir -p "${_tmp_kuberoot}/.."
cp -a "${KUBE_ROOT}" "${_tmp_kuberoot}/.." cp -a "${KUBE_ROOT}" "${_tmp_kuberoot}/.."
cd "${_tmp_kuberoot}" cd "${_tmp_kuberoot}"
GOPATH="${_tmp_gopath}" ./hack/update-bazel.sh GOPATH="${_tmp_gopath}" PATH="${_tmp_gopath}/bin:${PATH}" ./hack/update-bazel.sh
diff=$(diff -Naupr "${KUBE_ROOT}" "${_tmp_kuberoot}" || true) diff=$(diff -Naupr -x '_output' "${KUBE_ROOT}" "${_tmp_kuberoot}" || true)
if [[ -n "${diff}" ]]; then if [[ -n "${diff}" ]]; then
echo "${diff}" >&2 echo "${diff}" >&2
......
...@@ -64,6 +64,7 @@ _kubetmp="${_kubetmp}/kubernetes" ...@@ -64,6 +64,7 @@ _kubetmp="${_kubetmp}/kubernetes"
# Do all our work in the new GOPATH # Do all our work in the new GOPATH
export GOPATH="${_tmpdir}" export GOPATH="${_tmpdir}"
export PATH="${GOPATH}/bin:${PATH}"
pushd "${_kubetmp}" 2>&1 > /dev/null pushd "${_kubetmp}" 2>&1 > /dev/null
# Restore the Godeps into our temp directory # Restore the Godeps into our temp directory
......
...@@ -20,15 +20,12 @@ set -o pipefail ...@@ -20,15 +20,12 @@ set -o pipefail
export KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. export KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
source "${KUBE_ROOT}/hack/lib/init.sh" source "${KUBE_ROOT}/hack/lib/init.sh"
# Ensure that we find the binaries we build before anything else.
export GOBIN="${KUBE_OUTPUT_BINPATH}"
PATH="${GOBIN}:${PATH}"
# Install tools we need, but only from vendor/... # Install tools we need, but only from vendor/...
cd ${KUBE_ROOT}
go install ./vendor/github.com/client9/misspell/cmd/misspell go install ./vendor/github.com/client9/misspell/cmd/misspell
if ! which misspell >/dev/null 2>&1; then
echo "Can't find misspell - is your GOPATH 'bin' in your PATH?" >&2
echo " GOPATH: ${GOPATH}" >&2
echo " PATH: ${PATH}" >&2
exit 1
fi
# Spell checking # Spell checking
# All the skipping files are defined in hack/.spelling_failures # All the skipping files are defined in hack/.spelling_failures
......
...@@ -44,6 +44,19 @@ filegroup( ...@@ -44,6 +44,19 @@ filegroup(
"//vendor/github.com/aws/aws-sdk-go/service/elbv2:all-srcs", "//vendor/github.com/aws/aws-sdk-go/service/elbv2:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/service/kms:all-srcs", "//vendor/github.com/aws/aws-sdk-go/service/kms:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/service/sts:all-srcs", "//vendor/github.com/aws/aws-sdk-go/service/sts:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/cmd/gazelle:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/config:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/label:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/merger:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/packages:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/pathtools:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/repos:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/resolve:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/rules:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/version:all-srcs",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/wspace:all-srcs",
"//vendor/github.com/bazelbuild/buildtools/build:all-srcs",
"//vendor/github.com/bazelbuild/buildtools/tables:all-srcs",
"//vendor/github.com/beorn7/perks/quantile:all-srcs", "//vendor/github.com/beorn7/perks/quantile:all-srcs",
"//vendor/github.com/blang/semver:all-srcs", "//vendor/github.com/blang/semver:all-srcs",
"//vendor/github.com/chai2010/gettext-go/gettext:all-srcs", "//vendor/github.com/chai2010/gettext-go/gettext:all-srcs",
...@@ -305,7 +318,6 @@ filegroup( ...@@ -305,7 +318,6 @@ filegroup(
"//vendor/github.com/opencontainers/runtime-spec/specs-go:all-srcs", "//vendor/github.com/opencontainers/runtime-spec/specs-go:all-srcs",
"//vendor/github.com/opencontainers/selinux/go-selinux:all-srcs", "//vendor/github.com/opencontainers/selinux/go-selinux:all-srcs",
"//vendor/github.com/pborman/uuid:all-srcs", "//vendor/github.com/pborman/uuid:all-srcs",
"//vendor/github.com/pelletier/go-buffruneio:all-srcs",
"//vendor/github.com/pelletier/go-toml:all-srcs", "//vendor/github.com/pelletier/go-toml:all-srcs",
"//vendor/github.com/peterbourgon/diskv:all-srcs", "//vendor/github.com/peterbourgon/diskv:all-srcs",
"//vendor/github.com/pkg/errors:all-srcs", "//vendor/github.com/pkg/errors:all-srcs",
......
# This is the official list of authors for copyright purposes.
# Names should be added to this file as:
# Name or Organization <email address>
# The email address is not required for organizations.
Andy Hochhaus <hochhaus@users.noreply.github.com>
Antoine Pelisse <apelisse@gmail.com>
GinFungYJF <645116215@qq.com>
Google Inc.
Improbable Worlds Ltd
Jeff Hodges <jeff@somethingsimilar.com>
John Millikin <jmillikin@gmail.com>
Melinda Lu <melinda@vsco.co>
Peter McAlpine <peter@aoeu.ca>
RS <sayrer@gmail.com>
Rodrigo Queiro <overdrigzed@gmail.com>
Tom Payne <twpayne@gmail.com>
Yuki Yugui Sonoda <yugui@yugui.jp>
# People who have agreed to one of the CLAs and can contribute patches.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# https://developers.google.com/open-source/cla/individual
# https://developers.google.com/open-source/cla/corporate
#
# Names should be added to this file as:
# Name <email address>
Ainsley Escorce-Jones <ains@users.noreply.github.com>
Andy Hochhaus <hochhaus@users.noreply.github.com>
Antoine Pelisse <apelisse@gmail.com>
GinFungYJF <645116215@qq.com>
Ian Cottrell <ian.the.hat@gmail.com>
Jay Conrod <jayconrod@gmail.com>
Jeff Grafton <ixdy@users.noreply.github.com>
Jeff Hodges <jeff@somethingsimilar.com>
John Millikin <jmillikin@gmail.com>
Kristina <k.chodorow@gmail.com>
Melinda Lu <melinda@vsco.co>
Paul Bethe <pbethe@google.com>
Peter McAlpine <peter@aoeu.ca>
Rodrigo Queiro <overdrigzed@gmail.com>
RS <sayrer@gmail.com>
Stefan Sakalik <stefan@improbable.io>
Tom Payne <twpayne@gmail.com>
Yuki Yugui Sonoda <yugui@yugui.jp>
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_library(
name = "go_default_library",
srcs = [
"diff.go",
"fix.go",
"fix-update.go",
"flags.go",
"gazelle.go",
"print.go",
"update-repos.go",
"version.go",
],
importpath = "github.com/bazelbuild/bazel-gazelle/cmd/gazelle",
visibility = ["//visibility:private"],
deps = [
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/config:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/label:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/merger:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/packages:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/repos:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/resolve:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/rules:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/version:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/wspace:go_default_library",
"//vendor/github.com/bazelbuild/buildtools/build:go_default_library",
],
)
go_binary(
name = "gazelle",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
/* 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 main
import (
"bytes"
"io/ioutil"
"os"
"os/exec"
"github.com/bazelbuild/bazel-gazelle/internal/config"
bf "github.com/bazelbuild/buildtools/build"
)
func diffFile(c *config.Config, file *bf.File, path string) error {
oldContents, err := ioutil.ReadFile(file.Path)
if err != nil {
oldContents = nil
}
newContents := bf.Format(file)
if bytes.Equal(oldContents, newContents) {
return nil
}
f, err := ioutil.TempFile("", c.DefaultBuildFileName())
if err != nil {
return err
}
f.Close()
defer os.Remove(f.Name())
if err := ioutil.WriteFile(f.Name(), newContents, 0666); err != nil {
return err
}
cmd := exec.Command("diff", "-u", "--new-file", path, f.Name())
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if _, ok := err.(*exec.ExitError); ok {
// diff returns non-zero when files are different. This is not an error.
return nil
}
return err
}
/* 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 main
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/bazelbuild/bazel-gazelle/internal/config"
bf "github.com/bazelbuild/buildtools/build"
)
func fixFile(c *config.Config, file *bf.File, path string) error {
if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
return err
}
if err := ioutil.WriteFile(path, bf.Format(file), 0666); err != nil {
return err
}
return nil
}
// Copyright 2017 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 main
import "fmt"
// multiFlag allows repeated string flags to be collected into a slice
type multiFlag []string
func (m *multiFlag) String() string {
if len(*m) == 0 {
return ""
}
return fmt.Sprint(*m)
}
func (m *multiFlag) Set(v string) error {
(*m) = append(*m, v)
return nil
}
// explicitFlag is a string flag that tracks whether it was set.
type explicitFlag struct {
set bool
value string
}
func (f *explicitFlag) Set(value string) error {
f.set = true
f.value = value
return nil
}
func (f *explicitFlag) String() string {
if f == nil {
return ""
}
return f.value
}
/* 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 gazelle is a BUILD file generator for Go projects.
// See "gazelle --help" for more details.
package main
import (
"fmt"
"log"
"os"
)
type command int
const (
updateCmd command = iota
fixCmd
updateReposCmd
helpCmd
)
var commandFromName = map[string]command{
"fix": fixCmd,
"help": helpCmd,
"update": updateCmd,
"update-repos": updateReposCmd,
}
func main() {
log.SetPrefix("gazelle: ")
log.SetFlags(0) // don't print timestamps
if err := run(os.Args[1:]); err != nil {
log.Fatal(err)
}
}
func run(args []string) error {
cmd := updateCmd
if len(args) == 1 && (args[0] == "-h" || args[0] == "-help" || args[0] == "--help") {
cmd = helpCmd
} else if len(args) > 0 {
c, ok := commandFromName[args[0]]
if ok {
cmd = c
args = args[1:]
}
}
switch cmd {
case fixCmd, updateCmd:
return runFixUpdate(cmd, args)
case helpCmd:
help()
case updateReposCmd:
return updateRepos(args)
default:
log.Panicf("unknown command: %v", cmd)
}
return nil
}
func help() {
fmt.Fprint(os.Stderr, `usage: gazelle <command> [args...]
Gazelle is a BUILD file generator for Go projects. It can create new BUILD files
for a project that follows "go build" conventions, and it can update BUILD files
if they already exist. It can be invoked directly in a project workspace, or
it can be run on an external dependency during the build as part of the
go_repository rule.
Gazelle may be run with one of the commands below. If no command is given,
Gazelle defaults to "update".
update - Gazelle will create new BUILD files or update existing BUILD files
if needed.
fix - in addition to the changes made in update, Gazelle will make potentially
breaking changes. For example, it may delete obsolete rules or rename
existing rules.
update-repos - updates repository rules in the WORKSPACE file. Run with
-h for details.
help - show this message.
For usage information for a specific command, run the command with the -h flag.
For example:
gazelle update -h
Gazelle is under active delevopment, and its interface may change
without notice.
`)
}
/* 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 main
import (
"os"
"github.com/bazelbuild/bazel-gazelle/internal/config"
bf "github.com/bazelbuild/buildtools/build"
)
func printFile(c *config.Config, f *bf.File, _ string) error {
_, err := os.Stdout.Write(bf.Format(f))
return err
}
/* Copyright 2017 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 main
import (
"errors"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
"github.com/bazelbuild/bazel-gazelle/internal/merger"
"github.com/bazelbuild/bazel-gazelle/internal/repos"
"github.com/bazelbuild/bazel-gazelle/internal/wspace"
bf "github.com/bazelbuild/buildtools/build"
)
type updateReposFn func(c *updateReposConfiguration, oldFile *bf.File) (*bf.File, error)
type updateReposConfiguration struct {
fn updateReposFn
repoRoot string
lockFilename string
importPaths []string
}
func updateRepos(args []string) error {
c, err := newUpdateReposConfiguration(args)
if err != nil {
return err
}
workspacePath := filepath.Join(c.repoRoot, "WORKSPACE")
content, err := ioutil.ReadFile(workspacePath)
if err != nil {
return fmt.Errorf("error reading %q: %v", workspacePath, err)
}
oldFile, err := bf.Parse(workspacePath, content)
if err != nil {
return fmt.Errorf("error parsing %q: %v", workspacePath, err)
}
mergedFile, err := c.fn(c, oldFile)
if err != nil {
return err
}
mergedFile = merger.FixLoads(mergedFile)
if err := ioutil.WriteFile(mergedFile.Path, bf.Format(mergedFile), 0666); err != nil {
return fmt.Errorf("error writing %q: %v", mergedFile.Path, err)
}
return nil
}
func newUpdateReposConfiguration(args []string) (*updateReposConfiguration, error) {
c := new(updateReposConfiguration)
fs := flag.NewFlagSet("gazelle", flag.ContinueOnError)
// Flag will call this on any parse error. Don't print usage unless
// -h or -help were passed explicitly.
fs.Usage = func() {}
fromFileFlag := fs.String("from_file", "", "Gazelle will translate repositories listed in this file into repository rules in WORKSPACE. Currently only dep's Gopkg.lock is supported.")
repoRootFlag := fs.String("repo_root", "", "path to the root directory of the repository. If unspecified, this is assumed to be the directory containing WORKSPACE.")
if err := fs.Parse(args); err != nil {
if err == flag.ErrHelp {
updateReposUsage(fs)
os.Exit(0)
}
// flag already prints the error; don't print it again.
return nil, errors.New("Try -help for more information")
}
// Handle general flags that apply to all subcommands.
c.repoRoot = *repoRootFlag
if c.repoRoot == "" {
if repoRoot, err := wspace.Find("."); err != nil {
return nil, err
} else {
c.repoRoot = repoRoot
}
}
// Handle flags specific to each subcommand.
switch {
case *fromFileFlag != "":
if len(fs.Args()) != 0 {
return nil, fmt.Errorf("Got %d positional arguments with -from_file; wanted 0.\nTry -help for more information.", len(fs.Args()))
}
c.fn = importFromLockFile
c.lockFilename = *fromFileFlag
default:
if len(fs.Args()) == 0 {
return nil, fmt.Errorf("No repositories specified\nTry -help for more information.")
}
c.fn = updateImportPaths
c.importPaths = fs.Args()
}
return c, nil
}
func updateReposUsage(fs *flag.FlagSet) {
fmt.Fprintln(os.Stderr, `usage:
# Add/update repositories by import path
gazelle update-repos example.com/repo1 example.com/repo2
# Import repositories from lock file
gazelle update-repos -from_file=file
The update-repos command updates repository rules in the WORKSPACE file.
update-repos can add or update repositories explicitly by import path.
update-repos can also import repository rules from a vendoring tool's lock
file (currently only deps' Gopkg.lock is supported).
FLAGS:
`)
}
func updateImportPaths(c *updateReposConfiguration, oldFile *bf.File) (*bf.File, error) {
rs := repos.ListRepositories(oldFile)
rc := repos.NewRemoteCache(rs)
genRules := make([]bf.Expr, len(c.importPaths))
errs := make([]error, len(c.importPaths))
var wg sync.WaitGroup
wg.Add(len(c.importPaths))
for i, imp := range c.importPaths {
go func(i int) {
defer wg.Done()
repo, err := repos.UpdateRepo(rc, imp)
if err != nil {
errs[i] = err
return
}
repo.Remote = "" // don't set these explicitly
repo.VCS = ""
rule := repos.GenerateRule(repo)
genRules[i] = rule
}(i)
}
wg.Wait()
for _, err := range errs {
if err != nil {
return nil, err
}
}
mergedFile, _ := merger.MergeFile(genRules, nil, oldFile, merger.RepoAttrs)
return mergedFile, nil
}
func importFromLockFile(c *updateReposConfiguration, oldFile *bf.File) (*bf.File, error) {
genRules, err := repos.ImportRepoRules(c.lockFilename)
if err != nil {
return nil, err
}
mergedFile, _ := merger.MergeFile(genRules, nil, oldFile, merger.RepoAttrs)
return mergedFile, nil
}
/* Copyright 2018 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 main
import (
"io/ioutil"
"log"
"path/filepath"
"regexp"
"github.com/bazelbuild/bazel-gazelle/internal/config"
"github.com/bazelbuild/bazel-gazelle/internal/repos"
"github.com/bazelbuild/bazel-gazelle/internal/version"
)
var minimumRulesGoVersion = version.Version{0, 9, 0}
// checkRulesGoVersion checks whether a compatible version of rules_go is
// being used in the workspace. A message will be logged if an incompatible
// version is found.
//
// Note that we can't always determine the version of rules_go in use. Also,
// if we find an incompatible version, we shouldn't bail out since the
// incompatibility may not matter in the current workspace.
func checkRulesGoVersion(repoRoot string) {
const message = `Gazelle may not be compatible with this version of rules_go.
Update io_bazel_rules_go to a newer version in your WORKSPACE file.`
rulesGoPath, err := repos.FindExternalRepo(repoRoot, config.RulesGoRepoName)
if err != nil {
return
}
defBzlPath := filepath.Join(rulesGoPath, "go", "def.bzl")
defBzlContent, err := ioutil.ReadFile(defBzlPath)
if err != nil {
return
}
versionRe := regexp.MustCompile(`(?m)^RULES_GO_VERSION = ['"]([0-9.]*)['"]`)
match := versionRe.FindSubmatch(defBzlContent)
if match == nil {
log.Printf("RULES_GO_VERSION not found in @%s//go:def.bzl.\n%s", config.RulesGoRepoName, message)
return
}
vstr := string(match[1])
v, err := version.ParseVersion(vstr)
if err != nil {
log.Printf("RULES_GO_VERSION %q could not be parsed in @%s//go:def.bzl.\n%s", vstr, config.RulesGoRepoName, message)
}
if v.Compare(minimumRulesGoVersion) < 0 {
log.Printf("Found RULES_GO_VERSION %s. Minimum compatible version is %s.\n%s", v, minimumRulesGoVersion, message)
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"config.go",
"constants.go",
"directives.go",
"platform.go",
],
importpath = "github.com/bazelbuild/bazel-gazelle/internal/config",
visibility = ["//vendor/github.com/bazelbuild/bazel-gazelle:__subpackages__"],
deps = ["//vendor/github.com/bazelbuild/buildtools/build: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"],
)
/* Copyright 2017 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 config
import (
"fmt"
"go/build"
"strings"
)
// Config holds information about how Gazelle should run. This is mostly
// based on command-line arguments.
type Config struct {
// Dirs is a list of absolute paths to directories where Gazelle should run.
Dirs []string
// RepoRoot is the absolute, canonical path to the root directory of the
// repository with all symlinks resolved.
RepoRoot string
// ValidBuildFileNames is a list of base names that are considered valid
// build files. Some repositories may have files named "BUILD" that are not
// used by Bazel and should be ignored. Must contain at least one string.
ValidBuildFileNames []string
// GenericTags is a set of build constraints that are true on all platforms.
// It should not be nil.
GenericTags BuildTags
// GoPrefix is the portion of the import path for the root of this repository.
// This is used to map imports to labels within the repository.
GoPrefix string
// GoPrefixRel is the slash-separated path to the directory where GoPrefix
// was set, relative to the repository root. "" for the repository root.
GoPrefixRel string
// ShouldFix determines whether Gazelle attempts to remove and replace
// usage of deprecated rules.
ShouldFix bool
// DepMode determines how imports outside of GoPrefix are resolved.
DepMode DependencyMode
// ProtoMode determines how rules are generated for protos.
ProtoMode ProtoMode
// ProtoModeExplicit indicates whether the proto mode was set explicitly.
ProtoModeExplicit bool
}
var DefaultValidBuildFileNames = []string{"BUILD.bazel", "BUILD"}
func (c *Config) IsValidBuildFileName(name string) bool {
for _, n := range c.ValidBuildFileNames {
if name == n {
return true
}
}
return false
}
func (c *Config) DefaultBuildFileName() string {
return c.ValidBuildFileNames[0]
}
// BuildTags is a set of build constraints.
type BuildTags map[string]bool
// SetBuildTags sets GenericTags by parsing as a comma separated list. An
// error will be returned for tags that wouldn't be recognized by "go build".
// PreprocessTags should be called after this.
func (c *Config) SetBuildTags(tags string) error {
c.GenericTags = make(BuildTags)
if tags == "" {
return nil
}
for _, t := range strings.Split(tags, ",") {
if strings.HasPrefix(t, "!") {
return fmt.Errorf("build tags can't be negated: %s", t)
}
c.GenericTags[t] = true
}
return nil
}
// PreprocessTags adds some tags which are on by default before they are
// used to match files.
func (c *Config) PreprocessTags() {
if c.GenericTags == nil {
c.GenericTags = make(BuildTags)
}
c.GenericTags["gc"] = true
}
// CheckPrefix checks that a string may be used as a prefix. We forbid local
// (relative) imports and those beginning with "/". We allow the empty string,
// but generated rules must not have an empty importpath.
func CheckPrefix(prefix string) error {
if strings.HasPrefix(prefix, "/") || build.IsLocalImport(prefix) {
return fmt.Errorf("invalid prefix: %q", prefix)
}
return nil
}
// DependencyMode determines how imports of packages outside of the prefix
// are resolved.
type DependencyMode int
const (
// ExternalMode indicates imports should be resolved to external dependencies
// (declared in WORKSPACE).
ExternalMode DependencyMode = iota
// VendorMode indicates imports should be resolved to libraries in the
// vendor directory.
VendorMode
)
// DependencyModeFromString converts a string from the command line
// to a DependencyMode. Valid strings are "external", "vendor". An error will
// be returned for an invalid string.
func DependencyModeFromString(s string) (DependencyMode, error) {
switch s {
case "external":
return ExternalMode, nil
case "vendored":
return VendorMode, nil
default:
return 0, fmt.Errorf("unrecognized dependency mode: %q", s)
}
}
// ProtoMode determines how proto rules are generated.
type ProtoMode int
const (
// DefaultProtoMode generates proto_library and new grpc_proto_library rules.
// .pb.go files are excluded when there is a .proto file with a similar name.
DefaultProtoMode ProtoMode = iota
// DisableProtoMode ignores .proto files. .pb.go files are treated
// as normal sources.
DisableProtoMode
// LegacyProtoMode generates filegroups for .proto files if .pb.go files
// are present in the same directory.
LegacyProtoMode
)
func ProtoModeFromString(s string) (ProtoMode, error) {
switch s {
case "default":
return DefaultProtoMode, nil
case "disable":
return DisableProtoMode, nil
case "legacy":
return LegacyProtoMode, nil
default:
return 0, fmt.Errorf("unrecognized proto mode: %q", s)
}
}
/* Copyright 2017 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 config
const (
// RulesGoRepoName is the canonical name of the rules_go repository. It must
// match the workspace name in WORKSPACE.
RulesGoRepoName = "io_bazel_rules_go"
// DefaultLibName is the name of the default go_library rule in a Go
// package directory. It must be consistent to DEFAULT_LIB in go/private/common.bf.
DefaultLibName = "go_default_library"
// DefaultTestName is a name of an internal test corresponding to
// DefaultLibName. It does not need to be consistent to something but it
// just needs to be unique in the Bazel package
DefaultTestName = "go_default_test"
// DefaultXTestName is a name of an external test corresponding to
// DefaultLibName.
DefaultXTestName = "go_default_xtest"
// DefaultProtosName is the name of a filegroup created
// whenever the library contains .pb.go files
DefaultProtosName = "go_default_library_protos"
// DefaultCgoLibName is the name of the default cgo_library rule in a Go package directory.
DefaultCgoLibName = "cgo_default_library"
// GrpcCompilerLabel is the label for the gRPC compiler plugin, used in the
// "compilers" attribute of go_proto_library rules.
GrpcCompilerLabel = "@io_bazel_rules_go//proto:go_grpc"
// WellKnownTypesProtoRepo is the repository containing proto_library rules
// for the Well Known Types.
WellKnownTypesProtoRepo = "com_google_protobuf"
// WellKnownTypesGoProtoRepo is the repository containing go_library rules
// for the Well Known Types.
WellKnownTypesGoProtoRepo = "com_github_golang_protobuf"
// WellKnownTypesGoPrefix is the import path for the Go repository containing
// pre-generated code for the Well Known Types.
WellKnownTypesGoPrefix = "github.com/golang/protobuf"
// GazelleImportsKey is an internal attribute that lists imported packages
// on generated rules. It is replaced with "deps" during import resolution.
GazelleImportsKey = "_gazelle_imports"
)
// Language is the name of a programming langauge that Gazelle knows about.
// This is used to specify import paths.
type Language int
const (
// GoLang marks Go targets.
GoLang Language = iota
// ProtoLang marks protocol buffer targets.
ProtoLang
)
func (l Language) String() string {
switch l {
case GoLang:
return "go"
case ProtoLang:
return "proto"
default:
return "unknown"
}
}
/* Copyright 2017 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 config
import (
"log"
"path"
"regexp"
"strings"
bf "github.com/bazelbuild/buildtools/build"
)
// Directive is a key-value pair extracted from a top-level comment in
// a build file. Directives have the following format:
//
// # gazelle:key value
//
// Keys may not contain spaces. Values may be empty and may contain spaces,
// but surrounding space is trimmed.
type Directive struct {
Key, Value string
}
// Top-level directives apply to the whole package or build file. They must
// appear before the first statement.
var knownTopLevelDirectives = map[string]bool{
"build_file_name": true,
"build_tags": true,
"exclude": true,
"prefix": true,
"ignore": true,
"proto": true,
}
// TODO(jayconrod): annotation directives will apply to an individual rule.
// They must appear in the block of comments above that rule.
// ParseDirectives scans f for Gazelle directives. The full list of directives
// is returned. Errors are reported for unrecognized directives and directives
// out of place (after the first statement).
func ParseDirectives(f *bf.File) []Directive {
var directives []Directive
parseComment := func(com bf.Comment) {
match := directiveRe.FindStringSubmatch(com.Token)
if match == nil {
return
}
key, value := match[1], match[2]
if _, ok := knownTopLevelDirectives[key]; !ok {
log.Printf("%s:%d: unknown directive: %s", f.Path, com.Start.Line, com.Token)
return
}
directives = append(directives, Directive{key, value})
}
for _, s := range f.Stmt {
coms := s.Comment()
for _, com := range coms.Before {
parseComment(com)
}
for _, com := range coms.After {
parseComment(com)
}
}
return directives
}
var directiveRe = regexp.MustCompile(`^#\s*gazelle:(\w+)\s*(.*?)\s*$`)
// ApplyDirectives applies directives that modify the configuration to a copy of
// c, which is returned. If there are no configuration directives, c is returned
// unmodified.
func ApplyDirectives(c *Config, directives []Directive, rel string) *Config {
modified := *c
didModify := false
for _, d := range directives {
switch d.Key {
case "build_tags":
if err := modified.SetBuildTags(d.Value); err != nil {
log.Print(err)
modified.GenericTags = c.GenericTags
} else {
modified.PreprocessTags()
didModify = true
}
case "build_file_name":
modified.ValidBuildFileNames = strings.Split(d.Value, ",")
didModify = true
case "prefix":
if err := CheckPrefix(d.Value); err != nil {
log.Print(err)
continue
}
modified.GoPrefix = d.Value
modified.GoPrefixRel = rel
didModify = true
case "proto":
protoMode, err := ProtoModeFromString(d.Value)
if err != nil {
log.Print(err)
continue
}
modified.ProtoMode = protoMode
modified.ProtoModeExplicit = true
didModify = true
}
}
if !didModify {
return c
}
return &modified
}
// InferProtoMode sets Config.ProtoMode, based on the contents of f. If the
// proto mode is already set to something other than the default, or if the mode
// is set explicitly in directives, this function does not change it. If the
// legacy go_proto_library.bzl is loaded, or if this is the Well Known Types
// repository, legacy mode is used. If go_proto_library is loaded from another
// file, proto rule generation is disabled.
func InferProtoMode(c *Config, rel string, f *bf.File, directives []Directive) *Config {
if c.ProtoMode != DefaultProtoMode || c.ProtoModeExplicit {
return c
}
for _, d := range directives {
if d.Key == "proto" {
return c
}
}
if c.GoPrefix == WellKnownTypesGoPrefix {
// Use legacy mode in this repo. We don't need proto_library or
// go_proto_library, since we get that from @com_google_protobuf.
// Legacy rules still refer to .proto files in here, which need are
// exposed by filegroup. go_library rules from .pb.go files will be
// generated, which are depended upon by the new rules.
modified := *c
modified.ProtoMode = LegacyProtoMode
return &modified
}
if path.Base(rel) == "vendor" {
modified := *c
modified.ProtoMode = DisableProtoMode
return &modified
}
if f == nil {
return c
}
mode := DefaultProtoMode
for _, stmt := range f.Stmt {
c, ok := stmt.(*bf.CallExpr)
if !ok {
continue
}
x, ok := c.X.(*bf.LiteralExpr)
if !ok || x.Token != "load" || len(c.List) == 0 {
continue
}
name, ok := c.List[0].(*bf.StringExpr)
if !ok {
continue
}
if name.Value == "@io_bazel_rules_go//proto:def.bzl" {
break
}
if name.Value == "@io_bazel_rules_go//proto:go_proto_library.bzl" {
mode = LegacyProtoMode
break
}
for _, arg := range c.List[1:] {
if sym, ok := arg.(*bf.StringExpr); ok && sym.Value == "go_proto_library" {
mode = DisableProtoMode
break
}
kwarg, ok := arg.(*bf.BinaryExpr)
if !ok || kwarg.Op != "=" {
continue
}
if key, ok := kwarg.X.(*bf.LiteralExpr); ok && key.Token == "go_proto_library" {
mode = DisableProtoMode
break
}
}
}
if mode == DefaultProtoMode || c.ProtoMode == mode || c.ShouldFix && mode == LegacyProtoMode {
return c
}
modified := *c
modified.ProtoMode = mode
return &modified
}
/* Copyright 2017 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 config
import (
"sort"
)
// Platform represents a GOOS/GOARCH pair. When Platform is used to describe
// sources, dependencies, or flags, either OS or Arch may be empty.
type Platform struct {
OS, Arch string
}
// String returns OS, Arch, or "OS_Arch" if both are set. This must match
// the names of config_setting rules in @io_bazel_rules_go//go/platform.
func (p Platform) String() string {
switch {
case p.OS != "" && p.Arch != "":
return p.OS + "_" + p.Arch
case p.OS != "":
return p.OS
case p.Arch != "":
return p.Arch
default:
return ""
}
}
// KnownPlatforms is the set of target platforms that Go supports. Gazelle
// will generate multi-platform build files using these tags. rules_go and
// Bazel may not actually support all of these.
var KnownPlatforms = []Platform{
{"android", "386"},
{"android", "amd64"},
{"android", "arm"},
{"android", "arm64"},
{"darwin", "386"},
{"darwin", "amd64"},
{"darwin", "arm"},
{"darwin", "arm64"},
{"dragonfly", "amd64"},
{"freebsd", "386"},
{"freebsd", "amd64"},
{"freebsd", "arm"},
{"linux", "386"},
{"linux", "amd64"},
{"linux", "arm"},
{"linux", "arm64"},
{"linux", "mips"},
{"linux", "mips64"},
{"linux", "mips64le"},
{"linux", "mipsle"},
{"linux", "ppc64"},
{"linux", "ppc64le"},
{"linux", "s390x"},
{"nacl", "386"},
{"nacl", "amd64p32"},
{"nacl", "arm"},
{"netbsd", "386"},
{"netbsd", "amd64"},
{"netbsd", "arm"},
{"openbsd", "386"},
{"openbsd", "amd64"},
{"openbsd", "arm"},
{"plan9", "386"},
{"plan9", "amd64"},
{"plan9", "arm"},
{"solaris", "amd64"},
{"windows", "386"},
{"windows", "amd64"},
}
var (
// KnownOSs is the sorted list of operating systems that Go supports.
KnownOSs []string
// KnownOSSet is the set of operating systems that Go supports.
KnownOSSet map[string]bool
// KnownArchs is the sorted list of architectures that Go supports.
KnownArchs []string
// KnownArchSet is the set of architectures that Go supports.
KnownArchSet map[string]bool
// KnownOSArchs is a map from OS to the archictures they run on.
KnownOSArchs map[string][]string
// KnownArchOSs is a map from architectures to that OSs that run on them.
KnownArchOSs map[string][]string
)
func init() {
KnownOSSet = make(map[string]bool)
KnownArchSet = make(map[string]bool)
KnownOSArchs = make(map[string][]string)
KnownArchOSs = make(map[string][]string)
for _, p := range KnownPlatforms {
KnownOSSet[p.OS] = true
KnownArchSet[p.Arch] = true
KnownOSArchs[p.OS] = append(KnownOSArchs[p.OS], p.Arch)
KnownArchOSs[p.Arch] = append(KnownArchOSs[p.Arch], p.OS)
}
KnownOSs = make([]string, 0, len(KnownOSSet))
KnownArchs = make([]string, 0, len(KnownArchSet))
for os := range KnownOSSet {
KnownOSs = append(KnownOSs, os)
}
for arch := range KnownArchSet {
KnownArchs = append(KnownArchs, arch)
}
sort.Strings(KnownOSs)
sort.Strings(KnownArchs)
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"label.go",
"labeler.go",
],
importpath = "github.com/bazelbuild/bazel-gazelle/internal/label",
visibility = ["//vendor/github.com/bazelbuild/bazel-gazelle:__subpackages__"],
deps = [
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/config:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/pathtools: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"],
)
/* 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 label
import (
"fmt"
"log"
"path"
"regexp"
"strings"
"github.com/bazelbuild/bazel-gazelle/internal/pathtools"
)
// A Label represents a label of a build target in Bazel.
type Label struct {
Repo, Pkg, Name string
Relative bool
}
func New(repo, pkg, name string) Label {
return Label{Repo: repo, Pkg: pkg, Name: name}
}
// NoLabel is the nil value of Label. It is not a valid label and may be
// returned when an error occurs.
var NoLabel = Label{}
var (
labelRepoRegexp = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]*$`)
labelPkgRegexp = regexp.MustCompile(`^[A-Za-z0-9/._-]*$`)
labelNameRegexp = regexp.MustCompile(`^[A-Za-z0-9_/.+=,@~-]*$`)
)
// Parse reads a label from a string.
// See https://docs.bazel.build/versions/master/build-ref.html#lexi.
func Parse(s string) (Label, error) {
origStr := s
relative := true
var repo string
if strings.HasPrefix(s, "@") {
relative = false
endRepo := strings.Index(s, "//")
if endRepo < 0 {
return NoLabel, fmt.Errorf("label parse error: repository does not end with '//': %q", origStr)
}
repo = s[len("@"):endRepo]
if !labelRepoRegexp.MatchString(repo) {
return NoLabel, fmt.Errorf("label parse error: repository has invalid characters: %q", origStr)
}
s = s[endRepo:]
}
var pkg string
if strings.HasPrefix(s, "//") {
relative = false
endPkg := strings.Index(s, ":")
if endPkg < 0 {
pkg = s[len("//"):]
s = ""
} else {
pkg = s[len("//"):endPkg]
s = s[endPkg:]
}
if !labelPkgRegexp.MatchString(pkg) {
return NoLabel, fmt.Errorf("label parse error: package has invalid characters: %q", origStr)
}
}
if s == ":" {
return NoLabel, fmt.Errorf("label parse error: empty name: %q", origStr)
}
name := strings.TrimPrefix(s, ":")
if !labelNameRegexp.MatchString(name) {
return NoLabel, fmt.Errorf("label parse error: name has invalid characters: %q", origStr)
}
if pkg == "" && name == "" {
return NoLabel, fmt.Errorf("label parse error: empty package and name: %q", origStr)
}
if name == "" {
name = path.Base(pkg)
}
return Label{
Repo: repo,
Pkg: pkg,
Name: name,
Relative: relative,
}, nil
}
func (l Label) String() string {
if l.Relative {
return fmt.Sprintf(":%s", l.Name)
}
var repo string
if l.Repo != "" {
repo = fmt.Sprintf("@%s", l.Repo)
}
if path.Base(l.Pkg) == l.Name {
return fmt.Sprintf("%s//%s", repo, l.Pkg)
}
return fmt.Sprintf("%s//%s:%s", repo, l.Pkg, l.Name)
}
func (l Label) Abs(repo, pkg string) Label {
if !l.Relative {
return l
}
return Label{Repo: repo, Pkg: pkg, Name: l.Name}
}
func (l Label) Equal(other Label) bool {
return l.Repo == other.Repo &&
l.Pkg == other.Pkg &&
l.Name == other.Name &&
l.Relative == other.Relative
}
// Contains returns whether other is contained by the package of l or a
// sub-package. Neither label may be relative.
func (l Label) Contains(other Label) bool {
if l.Relative {
log.Panicf("l must not be relative: %s", l)
}
if other.Relative {
log.Panicf("other must not be relative: %s", other)
}
result := l.Repo == other.Repo && pathtools.HasPrefix(other.Pkg, l.Pkg)
return result
}
// ImportPathToBazelRepoName converts a Go import path into a bazel repo name
// following the guidelines in http://bazel.io/docs/be/functions.html#workspace
func ImportPathToBazelRepoName(importpath string) string {
importpath = strings.ToLower(importpath)
components := strings.Split(importpath, "/")
labels := strings.Split(components[0], ".")
var reversed []string
for i := range labels {
l := labels[len(labels)-i-1]
reversed = append(reversed, l)
}
repo := strings.Join(append(reversed, components[1:]...), "_")
return strings.NewReplacer("-", "_", ".", "_").Replace(repo)
}
/* Copyright 2017 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 label
import (
"github.com/bazelbuild/bazel-gazelle/internal/config"
"github.com/bazelbuild/bazel-gazelle/internal/pathtools"
)
// Labeler generates Bazel labels for rules, based on their locations
// within the repository.
type Labeler struct {
c *config.Config
}
func NewLabeler(c *config.Config) *Labeler {
return &Labeler{c}
}
func (l *Labeler) LibraryLabel(rel string) Label {
return Label{Pkg: rel, Name: config.DefaultLibName}
}
func (l *Labeler) TestLabel(rel string, isXTest bool) Label {
var name string
if isXTest {
name = config.DefaultXTestName
} else {
name = config.DefaultTestName
}
return Label{Pkg: rel, Name: name}
}
func (l *Labeler) BinaryLabel(rel string) Label {
name := pathtools.RelBaseName(rel, l.c.GoPrefix, l.c.RepoRoot)
return Label{Pkg: rel, Name: name}
}
func (l *Labeler) ProtoLabel(rel, name string) Label {
return Label{Pkg: rel, Name: name + "_proto"}
}
func (l *Labeler) GoProtoLabel(rel, name string) Label {
return Label{Pkg: rel, Name: name + "_go_proto"}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"fix.go",
"merger.go",
],
importpath = "github.com/bazelbuild/bazel-gazelle/internal/merger",
visibility = ["//vendor/github.com/bazelbuild/bazel-gazelle:__subpackages__"],
deps = [
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/config:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/label:go_default_library",
"//vendor/github.com/bazelbuild/buildtools/build: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"],
)
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"fileinfo.go",
"fileinfo_go.go",
"fileinfo_proto.go",
"package.go",
"walk.go",
],
importpath = "github.com/bazelbuild/bazel-gazelle/internal/packages",
visibility = ["//vendor/github.com/bazelbuild/bazel-gazelle:__subpackages__"],
deps = [
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/config:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/pathtools:go_default_library",
"//vendor/github.com/bazelbuild/buildtools/build: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"],
)
/* 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
/* Copyright 2017 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
import (
"bytes"
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"log"
"path/filepath"
"strconv"
"strings"
"unicode"
"unicode/utf8"
"github.com/bazelbuild/bazel-gazelle/internal/config"
)
// goFileInfo returns information about a .go file. It will parse part of the
// file to determine the package name, imports, and build constraints.
// If the file can't be read, an error will be logged, and partial information
// will be returned.
// This function is intended to match go/build.Context.Import.
// TODD(#53): extract canonical import path
func goFileInfo(c *config.Config, dir, rel, name string) fileInfo {
info := fileNameInfo(dir, rel, name)
fset := token.NewFileSet()
pf, err := parser.ParseFile(fset, info.path, nil, parser.ImportsOnly|parser.ParseComments)
if err != nil {
log.Printf("%s: error reading go file: %v", info.path, err)
return info
}
info.packageName = pf.Name.Name
if info.isTest && strings.HasSuffix(info.packageName, "_test") {
info.isXTest = true
info.packageName = info.packageName[:len(info.packageName)-len("_test")]
}
for _, decl := range pf.Decls {
d, ok := decl.(*ast.GenDecl)
if !ok {
continue
}
for _, dspec := range d.Specs {
spec, ok := dspec.(*ast.ImportSpec)
if !ok {
continue
}
quoted := spec.Path.Value
path, err := strconv.Unquote(quoted)
if err != nil {
log.Printf("%s: error reading go file: %v", info.path, err)
continue
}
if path == "C" {
if info.isTest {
log.Printf("%s: warning: use of cgo in test not supported", info.path)
}
info.isCgo = true
cg := spec.Doc
if cg == nil && len(d.Specs) == 1 {
cg = d.Doc
}
if cg != nil {
if err := saveCgo(&info, cg); err != nil {
log.Printf("%s: error reading go file: %v", info.path, err)
}
}
continue
}
info.imports = append(info.imports, path)
}
}
tags, err := readTags(info.path)
if err != nil {
log.Printf("%s: error reading go file: %v", info.path, err)
return info
}
info.tags = tags
return info
}
// saveCgo extracts CFLAGS, CPPFLAGS, CXXFLAGS, and LDFLAGS directives
// from a comment above a "C" import. This is intended to match logic in
// go/build.Context.saveCgo.
func saveCgo(info *fileInfo, cg *ast.CommentGroup) error {
text := cg.Text()
for _, line := range strings.Split(text, "\n") {
orig := line
// Line is
// #cgo [GOOS/GOARCH...] LDFLAGS: stuff
//
line = strings.TrimSpace(line)
if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
continue
}
// Split at colon.
line = strings.TrimSpace(line[4:])
i := strings.Index(line, ":")
if i < 0 {
return fmt.Errorf("%s: invalid #cgo line: %s", info.path, orig)
}
line, optstr := strings.TrimSpace(line[:i]), strings.TrimSpace(line[i+1:])
// Parse tags and verb.
f := strings.Fields(line)
if len(f) < 1 {
return fmt.Errorf("%s: invalid #cgo line: %s", info.path, orig)
}
verb := f[len(f)-1]
tags := parseTagsInGroups(f[:len(f)-1])
// Parse options.
opts, err := splitQuoted(optstr)
if err != nil {
return fmt.Errorf("%s: invalid #cgo line: %s", info.path, orig)
}
var ok bool
for i, opt := range opts {
if opt, ok = expandSrcDir(opt, info.rel); !ok {
return fmt.Errorf("%s: malformed #cgo argument: %s", info.path, orig)
}
opts[i] = opt
}
joinedStr := strings.Join(opts, OptSeparator)
// Add tags to appropriate list.
switch verb {
case "CFLAGS", "CPPFLAGS", "CXXFLAGS":
info.copts = append(info.copts, taggedOpts{tags, joinedStr})
case "LDFLAGS":
info.clinkopts = append(info.clinkopts, taggedOpts{tags, joinedStr})
case "pkg-config":
return fmt.Errorf("%s: pkg-config not supported: %s", info.path, orig)
default:
return fmt.Errorf("%s: invalid #cgo verb: %s", info.path, orig)
}
}
return nil
}
// splitQuoted splits the string s around each instance of one or more consecutive
// white space characters while taking into account quotes and escaping, and
// returns an array of substrings of s or an empty list if s contains only white space.
// Single quotes and double quotes are recognized to prevent splitting within the
// quoted region, and are removed from the resulting substrings. If a quote in s
// isn't closed err will be set and r will have the unclosed argument as the
// last element. The backslash is used for escaping.
//
// For example, the following string:
//
// a b:"c d" 'e''f' "g\""
//
// Would be parsed as:
//
// []string{"a", "b:c d", "ef", `g"`}
//
// Copied from go/build.splitQuoted
func splitQuoted(s string) (r []string, err error) {
var args []string
arg := make([]rune, len(s))
escaped := false
quoted := false
quote := '\x00'
i := 0
for _, rune := range s {
switch {
case escaped:
escaped = false
case rune == '\\':
escaped = true
continue
case quote != '\x00':
if rune == quote {
quote = '\x00'
continue
}
case rune == '"' || rune == '\'':
quoted = true
quote = rune
continue
case unicode.IsSpace(rune):
if quoted || i > 0 {
quoted = false
args = append(args, string(arg[:i]))
i = 0
}
continue
}
arg[i] = rune
i++
}
if quoted || i > 0 {
args = append(args, string(arg[:i]))
}
if quote != 0 {
err = errors.New("unclosed quote")
} else if escaped {
err = errors.New("unfinished escaping")
}
return args, err
}
// expandSrcDir expands any occurrence of ${SRCDIR}, making sure
// the result is safe for the shell.
//
// Copied from go/build.expandSrcDir
func expandSrcDir(str string, srcdir string) (string, bool) {
// "\" delimited paths cause safeCgoName to fail
// so convert native paths with a different delimiter
// to "/" before starting (eg: on windows).
srcdir = filepath.ToSlash(srcdir)
// Spaces are tolerated in ${SRCDIR}, but not anywhere else.
chunks := strings.Split(str, "${SRCDIR}")
if len(chunks) < 2 {
return str, safeCgoName(str, false)
}
ok := true
for _, chunk := range chunks {
ok = ok && (chunk == "" || safeCgoName(chunk, false))
}
ok = ok && (srcdir == "" || safeCgoName(srcdir, true))
res := strings.Join(chunks, srcdir)
return res, ok && res != ""
}
// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
// See golang.org/issue/6038.
// The @ is for OS X. See golang.org/issue/13720.
// The % is for Jenkins. See golang.org/issue/16959.
const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%"
const safeSpaces = " "
var safeBytes = []byte(safeSpaces + safeString)
// Copied from go/build.safeCgoName
func safeCgoName(s string, spaces bool) bool {
if s == "" {
return false
}
safe := safeBytes
if !spaces {
safe = safe[len(safeSpaces):]
}
for i := 0; i < len(s); i++ {
if c := s[i]; c < utf8.RuneSelf && bytes.IndexByte(safe, c) < 0 {
return false
}
}
return true
}
/* Copyright 2017 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
import (
"bytes"
"io/ioutil"
"log"
"path"
"regexp"
"sort"
"strconv"
"strings"
"unicode"
"github.com/bazelbuild/bazel-gazelle/internal/config"
)
var protoRe = buildProtoRegexp()
const (
importSubexpIndex = 1
packageSubexpIndex = 2
goPackageSubexpIndex = 3
serviceSubexpIndex = 4
)
func protoFileInfo(c *config.Config, dir, rel, name string) fileInfo {
info := fileNameInfo(dir, rel, name)
content, err := ioutil.ReadFile(info.path)
if err != nil {
log.Printf("%s: error reading proto file: %v", info.path, err)
return info
}
for _, match := range protoRe.FindAllSubmatch(content, -1) {
switch {
case match[importSubexpIndex] != nil:
imp := unquoteProtoString(match[importSubexpIndex])
info.imports = append(info.imports, imp)
case match[packageSubexpIndex] != nil:
pkg := string(match[packageSubexpIndex])
if info.packageName == "" {
info.packageName = strings.Replace(pkg, ".", "_", -1)
}
case match[goPackageSubexpIndex] != nil:
gopkg := unquoteProtoString(match[goPackageSubexpIndex])
// If there's no / in the package option, then it's just a
// simple package name, not a full import path.
if strings.LastIndexByte(gopkg, '/') == -1 {
info.packageName = gopkg
} else {
if i := strings.LastIndexByte(gopkg, ';'); i != -1 {
info.importPath = gopkg[:i]
info.packageName = gopkg[i+1:]
} else {
info.importPath = gopkg
info.packageName = path.Base(gopkg)
}
}
case match[serviceSubexpIndex] != nil:
info.hasServices = true
default:
// Comment matched. Nothing to extract.
}
}
sort.Strings(info.imports)
if info.packageName == "" {
stem := strings.TrimSuffix(name, ".proto")
fs := strings.FieldsFunc(stem, func(r rune) bool {
return !(unicode.IsLetter(r) || unicode.IsNumber(r) || r == '_')
})
info.packageName = strings.Join(fs, "_")
}
return info
}
// Based on https://developers.google.com/protocol-buffers/docs/reference/proto3-spec
func buildProtoRegexp() *regexp.Regexp {
hexEscape := `\\[xX][0-9a-fA-f]{2}`
octEscape := `\\[0-7]{3}`
charEscape := `\\[abfnrtv'"\\]`
charValue := strings.Join([]string{hexEscape, octEscape, charEscape, "[^\x00\\'\\\"\\\\]"}, "|")
strLit := `'(?:` + charValue + `|")*'|"(?:` + charValue + `|')*"`
ident := `[A-Za-z][A-Za-z0-9_]*`
fullIdent := ident + `(?:\.` + ident + `)*`
importStmt := `\bimport\s*(?:public|weak)?\s*(?P<import>` + strLit + `)\s*;`
packageStmt := `\bpackage\s*(?P<package>` + fullIdent + `)\s*;`
goPackageStmt := `\boption\s*go_package\s*=\s*(?P<go_package>` + strLit + `)\s*;`
serviceStmt := `(?P<service>service)`
comment := `//[^\n]*`
protoReSrc := strings.Join([]string{importStmt, packageStmt, goPackageStmt, serviceStmt, comment}, "|")
return regexp.MustCompile(protoReSrc)
}
func unquoteProtoString(q []byte) string {
// Adjust quotes so that Unquote is happy. We need a double quoted string
// without unescaped double quote characters inside.
noQuotes := bytes.Split(q[1:len(q)-1], []byte{'"'})
if len(noQuotes) != 1 {
for i := 0; i < len(noQuotes)-1; i++ {
if len(noQuotes[i]) == 0 || noQuotes[i][len(noQuotes[i])-1] != '\\' {
noQuotes[i] = append(noQuotes[i], '\\')
}
}
q = append([]byte{'"'}, bytes.Join(noQuotes, []byte{'"'})...)
q = append(q, '"')
}
if q[0] == '\'' {
q[0] = '"'
q[len(q)-1] = '"'
}
s, err := strconv.Unquote(string(q))
if err != nil {
log.Panicf("unquoting string literal %s from proto: %v", q, err)
}
return s
}
...@@ -2,9 +2,9 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") ...@@ -2,9 +2,9 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = ["buffruneio.go"], srcs = ["path.go"],
importpath = "github.com/pelletier/go-buffruneio", importpath = "github.com/bazelbuild/bazel-gazelle/internal/pathtools",
visibility = ["//visibility:public"], visibility = ["//vendor/github.com/bazelbuild/bazel-gazelle:__subpackages__"],
) )
filegroup( filegroup(
......
/* Copyright 2018 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 pathtools
import (
"path"
"path/filepath"
"strings"
)
// HasPrefix returns whether the slash-separated path p has the given
// prefix. Unlike strings.HasPrefix, this function respects component
// boundaries, so "/home/foo" is not a prefix is "/home/foobar/baz". If the
// prefix is empty, this function always returns true.
func HasPrefix(p, prefix string) bool {
return prefix == "" || p == prefix || strings.HasPrefix(p, prefix+"/")
}
// TrimPrefix returns p without the provided prefix. If p doesn't start
// with prefix, it returns p unchanged. Unlike strings.HasPrefix, this function
// respects component boundaries (assuming slash-separated paths), so
// TrimPrefix("foo/bar", "foo") returns "baz".
func TrimPrefix(p, prefix string) string {
if prefix == "" {
return p
}
if prefix == p {
return ""
}
return strings.TrimPrefix(p, prefix+"/")
}
// RelBaseName returns the base name for rel, a slash-separated path relative
// to the repository root. If rel is empty, RelBaseName returns the base name
// of prefix. If prefix is empty, RelBaseName returns the base name of root,
// the absolute file path of the repository root directory. If that's empty
// to, then RelBaseName returns "root".
func RelBaseName(rel, prefix, root string) string {
base := path.Base(rel)
if base == "." || base == "/" {
base = path.Base(prefix)
}
if base == "." || base == "/" {
base = filepath.Base(root)
}
if base == "." || base == "/" {
base = "root"
}
return base
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"dep.go",
"remote.go",
"repo.go",
],
importpath = "github.com/bazelbuild/bazel-gazelle/internal/repos",
visibility = ["//vendor/github.com/bazelbuild/bazel-gazelle:__subpackages__"],
deps = [
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/label:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/pathtools:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/rules:go_default_library",
"//vendor/github.com/bazelbuild/buildtools/build:go_default_library",
"//vendor/github.com/pelletier/go-toml:go_default_library",
"//vendor/golang.org/x/tools/go/vcs: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"],
)
/* Copyright 2017 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 repos
import (
"io/ioutil"
"github.com/bazelbuild/bazel-gazelle/internal/label"
toml "github.com/pelletier/go-toml"
)
type depLockFile struct {
Projects []depProject `toml:"projects"`
}
type depProject struct {
Name string `toml:"name"`
Revision string `toml:"revision"`
Source string `toml:"source"`
}
func importRepoRulesDep(filename string) ([]Repo, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var file depLockFile
if err := toml.Unmarshal(data, &file); err != nil {
return nil, err
}
var repos []Repo
for _, p := range file.Projects {
repos = append(repos, Repo{
Name: label.ImportPathToBazelRepoName(p.Name),
GoPrefix: p.Name,
Commit: p.Revision,
Remote: p.Source,
})
}
return repos, nil
}
/* Copyright 2017 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 repos
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/bazelbuild/bazel-gazelle/internal/rules"
bf "github.com/bazelbuild/buildtools/build"
)
// Repo describes an external repository rule declared in a Bazel
// WORKSPACE file.
type Repo struct {
// Name is the value of the "name" attribute of the repository rule.
Name string
// GoPrefix is the portion of the Go import path for the root of this
// repository. Usually the same as Remote.
GoPrefix string
// Commit is the revision at which a repository is checked out (for example,
// a Git commit id).
Commit string
// Tag is the name of the version at which a repository is checked out.
Tag string
// Remote is the URL the repository can be cloned or checked out from.
Remote string
// VCS is the version control system used to check out the repository.
// May also be "http" for HTTP archives.
VCS string
}
type byName []Repo
func (s byName) Len() int { return len(s) }
func (s byName) Less(i, j int) bool { return s[i].Name < s[j].Name }
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type lockFileFormat int
const (
unknownFormat lockFileFormat = iota
depFormat
)
var lockFileParsers = map[lockFileFormat]func(string) ([]Repo, error){
depFormat: importRepoRulesDep,
}
// ImportRepoRules reads the lock file of a vendoring tool and returns
// a list of equivalent repository rules that can be merged into a WORKSPACE
// file. The format of the file is inferred from its basename. Currently,
// only Gopkg.lock is supported.
func ImportRepoRules(filename string) ([]bf.Expr, error) {
format := getLockFileFormat(filename)
if format == unknownFormat {
return nil, fmt.Errorf(`%s: unrecognized lock file format. Expected "Gopkg.lock"`, filename)
}
parser := lockFileParsers[format]
repos, err := parser(filename)
if err != nil {
return nil, fmt.Errorf("error parsing %q: %v", filename, err)
}
sort.Stable(byName(repos))
rules := make([]bf.Expr, 0, len(repos))
for _, repo := range repos {
rules = append(rules, GenerateRule(repo))
}
return rules, nil
}
func getLockFileFormat(filename string) lockFileFormat {
switch filepath.Base(filename) {
case "Gopkg.lock":
return depFormat
default:
return unknownFormat
}
}
// GenerateRule returns a repository rule for the given repository that can
// be written in a WORKSPACE file.
func GenerateRule(repo Repo) bf.Expr {
attrs := []rules.KeyValue{
{Key: "name", Value: repo.Name},
{Key: "commit", Value: repo.Commit},
{Key: "importpath", Value: repo.GoPrefix},
}
if repo.Remote != "" {
attrs = append(attrs, rules.KeyValue{Key: "remote", Value: repo.Remote})
}
if repo.VCS != "" {
attrs = append(attrs, rules.KeyValue{Key: "vcs", Value: repo.VCS})
}
return rules.NewRule("go_repository", attrs)
}
// FindExternalRepo attempts to locate the directory where Bazel has fetched
// the external repository with the given name. An error is returned if the
// repository directory cannot be located.
func FindExternalRepo(repoRoot, name string) (string, error) {
// See https://docs.bazel.build/versions/master/output_directories.html
// for documentation on Bazel directory layout.
// We expect the bazel-out symlink in the workspace root directory to point to
// <output-base>/execroot/<workspace-name>/bazel-out
// We expect the external repository to be checked out at
// <output-base>/external/<name>
// Note that users can change the prefix for most of the Bazel symlinks with
// --symlink_prefix, but this does not include bazel-out.
externalPath := strings.Join([]string{repoRoot, "bazel-out", "..", "..", "..", "external", name}, string(os.PathSeparator))
cleanPath, err := filepath.EvalSymlinks(externalPath)
if err != nil {
return "", err
}
st, err := os.Stat(cleanPath)
if err != nil {
return "", err
}
if !st.IsDir() {
return "", fmt.Errorf("%s: not a directory", externalPath)
}
return cleanPath, nil
}
// ListRepositories extracts metadata about repositories declared in a
// WORKSPACE file.
//
// The set of repositories returned is necessarily incomplete, since we don't
// evaluate the file, and repositories may be declared in macros in other files.
func ListRepositories(workspace *bf.File) []Repo {
var repos []Repo
for _, e := range workspace.Stmt {
call, ok := e.(*bf.CallExpr)
if !ok {
continue
}
r := bf.Rule{Call: call}
name := r.Name()
if name == "" {
continue
}
var repo Repo
switch r.Kind() {
case "go_repository":
// TODO(jayconrod): extract other fields needed by go_repository.
// Currently, we don't use the result of this function to produce new
// go_repository rules, so it doesn't matter.
goPrefix := r.AttrString("importpath")
revision := r.AttrString("commit")
remote := r.AttrString("remote")
vcs := r.AttrString("vcs")
if goPrefix == "" {
continue
}
repo = Repo{
Name: name,
GoPrefix: goPrefix,
Commit: revision,
Remote: remote,
VCS: vcs,
}
// TODO(jayconrod): infer from {new_,}git_repository, {new_,}http_archive,
// local_repository.
default:
continue
}
repos = append(repos, repo)
}
// TODO(jayconrod): look for directives that describe repositories that
// aren't declared in the top-level of WORKSPACE (e.g., behind a macro).
return repos
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"index.go",
"resolve.go",
"resolve_external.go",
"resolve_vendored.go",
"std_package_list.go",
],
importpath = "github.com/bazelbuild/bazel-gazelle/internal/resolve",
visibility = ["//vendor/github.com/bazelbuild/bazel-gazelle:__subpackages__"],
deps = [
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/config:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/label:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/pathtools:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/repos:go_default_library",
"//vendor/github.com/bazelbuild/buildtools/build: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"],
)
/* 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 resolve
import (
"fmt"
"go/build"
"log"
"path"
"strings"
"github.com/bazelbuild/bazel-gazelle/internal/config"
"github.com/bazelbuild/bazel-gazelle/internal/label"
"github.com/bazelbuild/bazel-gazelle/internal/pathtools"
"github.com/bazelbuild/bazel-gazelle/internal/repos"
bf "github.com/bazelbuild/buildtools/build"
)
// Resolver resolves import strings in source files (import paths in Go,
// import statements in protos) into Bazel labels.
type Resolver struct {
c *config.Config
l *label.Labeler
ix *RuleIndex
external nonlocalResolver
}
// nonlocalResolver resolves import paths outside of the current repository's
// prefix. Once we have smarter import path resolution, this shouldn't
// be necessary, and we can remove this abstraction.
type nonlocalResolver interface {
resolve(imp string) (label.Label, error)
}
func NewResolver(c *config.Config, l *label.Labeler, ix *RuleIndex, rc *repos.RemoteCache) *Resolver {
var e nonlocalResolver
switch c.DepMode {
case config.ExternalMode:
e = newExternalResolver(l, rc)
case config.VendorMode:
e = newVendoredResolver(l)
}
return &Resolver{
c: c,
l: l,
ix: ix,
external: e,
}
}
// ResolveRule copies and modifies a generated rule e by replacing the import
// paths in the "_gazelle_imports" attribute with labels in a "deps"
// attribute. This may be safely called on expressions that aren't Go rules
// (the original expression will be returned). Any existing "deps" attribute
// is deleted, so it may be necessary to merge the result.
func (r *Resolver) ResolveRule(e bf.Expr, pkgRel string) bf.Expr {
call, ok := e.(*bf.CallExpr)
if !ok {
return e
}
rule := bf.Rule{Call: call}
from := label.New("", pkgRel, rule.Name())
var resolve func(imp string, from label.Label) (label.Label, error)
switch rule.Kind() {
case "go_library", "go_binary", "go_test":
resolve = r.resolveGo
case "proto_library":
resolve = r.resolveProto
case "go_proto_library", "go_grpc_library":
resolve = r.resolveGoProto
default:
return e
}
resolved := *call
resolved.List = append([]bf.Expr{}, call.List...)
rule.Call = &resolved
imports := rule.Attr(config.GazelleImportsKey)
rule.DelAttr(config.GazelleImportsKey)
rule.DelAttr("deps")
deps := mapExprStrings(imports, func(imp string) string {
label, err := resolve(imp, from)
if err != nil {
switch err.(type) {
case standardImportError, selfImportError:
return ""
default:
log.Print(err)
return ""
}
}
label.Relative = label.Repo == "" && label.Pkg == pkgRel
return label.String()
})
if deps != nil {
rule.SetAttr("deps", deps)
}
return &resolved
}
type standardImportError struct {
imp string
}
func (e standardImportError) Error() string {
return fmt.Sprintf("import path %q is in the standard library", e.imp)
}
// mapExprStrings applies a function f to the strings in e and returns a new
// expression with the results. Scalar strings, lists, dicts, selects, and
// concatenations are supported.
func mapExprStrings(e bf.Expr, f func(string) string) bf.Expr {
if e == nil {
return nil
}
switch expr := e.(type) {
case *bf.StringExpr:
s := f(expr.Value)
if s == "" {
return nil
}
ret := *expr
ret.Value = s
return &ret
case *bf.ListExpr:
var list []bf.Expr
for _, elem := range expr.List {
elem = mapExprStrings(elem, f)
if elem != nil {
list = append(list, elem)
}
}
if len(list) == 0 && len(expr.List) > 0 {
return nil
}
ret := *expr
ret.List = list
return &ret
case *bf.DictExpr:
var cases []bf.Expr
isEmpty := true
for _, kv := range expr.List {
keyval, ok := kv.(*bf.KeyValueExpr)
if !ok {
log.Panicf("unexpected expression in generated imports dict: %#v", kv)
}
value := mapExprStrings(keyval.Value, f)
if value != nil {
cases = append(cases, &bf.KeyValueExpr{Key: keyval.Key, Value: value})
if key, ok := keyval.Key.(*bf.StringExpr); !ok || key.Value != "//conditions:default" {
isEmpty = false
}
}
}
if isEmpty {
return nil
}
ret := *expr
ret.List = cases
return &ret
case *bf.CallExpr:
if x, ok := expr.X.(*bf.LiteralExpr); !ok || x.Token != "select" || len(expr.List) != 1 {
log.Panicf("unexpected call expression in generated imports: %#v", e)
}
arg := mapExprStrings(expr.List[0], f)
if arg == nil {
return nil
}
call := *expr
call.List[0] = arg
return &call
case *bf.BinaryExpr:
x := mapExprStrings(expr.X, f)
y := mapExprStrings(expr.Y, f)
if x == nil {
return y
}
if y == nil {
return x
}
binop := *expr
binop.X = x
binop.Y = y
return &binop
default:
log.Panicf("unexpected expression in generated imports: %#v", e)
return nil
}
}
// resolveGo resolves an import path from a Go source file to a label.
// pkgRel is the path to the Go package relative to the repository root; it
// is used to resolve relative imports.
func (r *Resolver) resolveGo(imp string, from label.Label) (label.Label, error) {
if build.IsLocalImport(imp) {
cleanRel := path.Clean(path.Join(from.Pkg, imp))
if build.IsLocalImport(cleanRel) {
return label.NoLabel, fmt.Errorf("relative import path %q from %q points outside of repository", imp, from.Pkg)
}
imp = path.Join(r.c.GoPrefix, cleanRel)
}
if IsStandard(imp) {
return label.NoLabel, standardImportError{imp}
}
if l, err := r.ix.findLabelByImport(importSpec{config.GoLang, imp}, config.GoLang, from); err != nil {
if _, ok := err.(ruleNotFoundError); !ok {
return label.NoLabel, err
}
} else {
return l, nil
}
if pathtools.HasPrefix(imp, r.c.GoPrefix) {
return r.l.LibraryLabel(pathtools.TrimPrefix(imp, r.c.GoPrefix)), nil
}
return r.external.resolve(imp)
}
const (
wellKnownPrefix = "google/protobuf/"
wellKnownGoProtoPkg = "ptypes"
descriptorPkg = "protoc-gen-go/descriptor"
)
// resolveProto resolves an import statement in a .proto file to a label
// for a proto_library rule.
func (r *Resolver) resolveProto(imp string, from label.Label) (label.Label, error) {
if !strings.HasSuffix(imp, ".proto") {
return label.NoLabel, fmt.Errorf("can't import non-proto: %q", imp)
}
if isWellKnown(imp) {
name := path.Base(imp[:len(imp)-len(".proto")]) + "_proto"
return label.New(config.WellKnownTypesProtoRepo, "", name), nil
}
if l, err := r.ix.findLabelByImport(importSpec{config.ProtoLang, imp}, config.ProtoLang, from); err != nil {
if _, ok := err.(ruleNotFoundError); !ok {
return label.NoLabel, err
}
} else {
return l, nil
}
rel := path.Dir(imp)
if rel == "." {
rel = ""
}
name := pathtools.RelBaseName(rel, r.c.GoPrefix, r.c.RepoRoot)
return r.l.ProtoLabel(rel, name), nil
}
// resolveGoProto resolves an import statement in a .proto file to a
// label for a go_library rule that embeds the corresponding go_proto_library.
func (r *Resolver) resolveGoProto(imp string, from label.Label) (label.Label, error) {
if !strings.HasSuffix(imp, ".proto") {
return label.NoLabel, fmt.Errorf("can't import non-proto: %q", imp)
}
stem := imp[:len(imp)-len(".proto")]
if isWellKnown(stem) {
// Well Known Type
base := path.Base(stem)
if base == "descriptor" {
switch r.c.DepMode {
case config.ExternalMode:
label := r.l.LibraryLabel(descriptorPkg)
if r.c.GoPrefix != config.WellKnownTypesGoPrefix {
label.Repo = config.WellKnownTypesGoProtoRepo
}
return label, nil
case config.VendorMode:
pkg := path.Join("vendor", config.WellKnownTypesGoPrefix, descriptorPkg)
label := r.l.LibraryLabel(pkg)
return label, nil
default:
log.Panicf("unknown external mode: %v", r.c.DepMode)
}
}
switch r.c.DepMode {
case config.ExternalMode:
pkg := path.Join(wellKnownGoProtoPkg, base)
label := r.l.LibraryLabel(pkg)
if r.c.GoPrefix != config.WellKnownTypesGoPrefix {
label.Repo = config.WellKnownTypesGoProtoRepo
}
return label, nil
case config.VendorMode:
pkg := path.Join("vendor", config.WellKnownTypesGoPrefix, wellKnownGoProtoPkg, base)
return r.l.LibraryLabel(pkg), nil
default:
log.Panicf("unknown external mode: %v", r.c.DepMode)
}
}
if l, err := r.ix.findLabelByImport(importSpec{config.ProtoLang, imp}, config.GoLang, from); err != nil {
if _, ok := err.(ruleNotFoundError); !ok {
return label.NoLabel, err
}
} else {
return l, err
}
// As a fallback, guess the label based on the proto file name. We assume
// all proto files in a directory belong to the same package, and the
// package name matches the directory base name. We also assume that protos
// in the vendor directory must refer to something else in vendor.
rel := path.Dir(imp)
if rel == "." {
rel = ""
}
if from.Pkg == "vendor" || strings.HasPrefix(from.Pkg, "vendor/") {
rel = path.Join("vendor", rel)
}
return r.l.LibraryLabel(rel), nil
}
// IsStandard returns whether a package is in the standard library.
func IsStandard(imp string) bool {
return stdPackages[imp]
}
func isWellKnown(imp string) bool {
return strings.HasPrefix(imp, wellKnownPrefix) && strings.TrimPrefix(imp, wellKnownPrefix) == path.Base(imp)
}
/* 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 resolve
import (
"github.com/bazelbuild/bazel-gazelle/internal/label"
"github.com/bazelbuild/bazel-gazelle/internal/pathtools"
"github.com/bazelbuild/bazel-gazelle/internal/repos"
)
// externalResolver resolves import paths to external repositories. It uses
// vcs to determine the prefix of the import path that corresponds to the root
// of the repository (this will perform a network fetch for unqualified paths).
// The prefix is converted to a Bazel external name repo according to the
// guidelines in http://bazel.io/docs/be/functions.html#workspace. The remaining
// portion of the import path is treated as the package name.
type externalResolver struct {
l *label.Labeler
rc *repos.RemoteCache
}
var _ nonlocalResolver = (*externalResolver)(nil)
func newExternalResolver(l *label.Labeler, rc *repos.RemoteCache) *externalResolver {
return &externalResolver{l: l, rc: rc}
}
// Resolve resolves "importPath" into a label, assuming that it is a label in an
// external repository. It also assumes that the external repository follows the
// recommended reverse-DNS form of workspace name as described in
// http://bazel.io/docs/be/functions.html#workspace.
func (r *externalResolver) resolve(importPath string) (label.Label, error) {
prefix, repo, err := r.rc.Root(importPath)
if err != nil {
return label.NoLabel, err
}
var pkg string
if importPath != prefix {
pkg = pathtools.TrimPrefix(importPath, prefix)
}
l := r.l.LibraryLabel(pkg)
l.Repo = repo
return l, nil
}
/* 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 resolve
import (
"github.com/bazelbuild/bazel-gazelle/internal/label"
)
// vendoredResolver resolves external packages as packages in vendor/.
type vendoredResolver struct {
l *label.Labeler
}
var _ nonlocalResolver = (*vendoredResolver)(nil)
func newVendoredResolver(l *label.Labeler) *vendoredResolver {
return &vendoredResolver{l}
}
func (v *vendoredResolver) resolve(importpath string) (label.Label, error) {
return v.l.LibraryLabel("vendor/" + importpath), nil
}
/* Copyright 2017 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.
*/
// Generated by gen_std_package_list.go
// DO NOT EDIT
package resolve
var stdPackages = map[string]bool{
"archive/tar": true,
"archive/zip": true,
"bufio": true,
"bytes": true,
"cmd/addr2line": true,
"cmd/api": true,
"cmd/asm": true,
"cmd/asm/internal/arch": true,
"cmd/asm/internal/asm": true,
"cmd/asm/internal/flags": true,
"cmd/asm/internal/lex": true,
"cmd/cgo": true,
"cmd/compile": true,
"cmd/compile/internal/amd64": true,
"cmd/compile/internal/arm": true,
"cmd/compile/internal/arm64": true,
"cmd/compile/internal/gc": true,
"cmd/compile/internal/mips": true,
"cmd/compile/internal/mips64": true,
"cmd/compile/internal/ppc64": true,
"cmd/compile/internal/s390x": true,
"cmd/compile/internal/ssa": true,
"cmd/compile/internal/syntax": true,
"cmd/compile/internal/test": true,
"cmd/compile/internal/types": true,
"cmd/compile/internal/x86": true,
"cmd/cover": true,
"cmd/dist": true,
"cmd/doc": true,
"cmd/fix": true,
"cmd/go": true,
"cmd/go/internal/base": true,
"cmd/go/internal/bug": true,
"cmd/go/internal/buildid": true,
"cmd/go/internal/cfg": true,
"cmd/go/internal/clean": true,
"cmd/go/internal/cmdflag": true,
"cmd/go/internal/doc": true,
"cmd/go/internal/envcmd": true,
"cmd/go/internal/fix": true,
"cmd/go/internal/fmtcmd": true,
"cmd/go/internal/generate": true,
"cmd/go/internal/get": true,
"cmd/go/internal/help": true,
"cmd/go/internal/list": true,
"cmd/go/internal/load": true,
"cmd/go/internal/run": true,
"cmd/go/internal/str": true,
"cmd/go/internal/test": true,
"cmd/go/internal/tool": true,
"cmd/go/internal/version": true,
"cmd/go/internal/vet": true,
"cmd/go/internal/web": true,
"cmd/go/internal/work": true,
"cmd/gofmt": true,
"cmd/internal/bio": true,
"cmd/internal/browser": true,
"cmd/internal/dwarf": true,
"cmd/internal/gcprog": true,
"cmd/internal/goobj": true,
"cmd/internal/obj": true,
"cmd/internal/obj/arm": true,
"cmd/internal/obj/arm64": true,
"cmd/internal/obj/mips": true,
"cmd/internal/obj/ppc64": true,
"cmd/internal/obj/s390x": true,
"cmd/internal/obj/x86": true,
"cmd/internal/objabi": true,
"cmd/internal/objfile": true,
"cmd/internal/src": true,
"cmd/internal/sys": true,
"cmd/link": true,
"cmd/link/internal/amd64": true,
"cmd/link/internal/arm": true,
"cmd/link/internal/arm64": true,
"cmd/link/internal/ld": true,
"cmd/link/internal/mips": true,
"cmd/link/internal/mips64": true,
"cmd/link/internal/ppc64": true,
"cmd/link/internal/s390x": true,
"cmd/link/internal/x86": true,
"cmd/nm": true,
"cmd/objdump": true,
"cmd/pack": true,
"cmd/pprof": true,
"cmd/trace": true,
"cmd/vet": true,
"cmd/vet/internal/cfg": true,
"cmd/vet/internal/whitelist": true,
"compress/bzip2": true,
"compress/flate": true,
"compress/gzip": true,
"compress/lzw": true,
"compress/zlib": true,
"container/heap": true,
"container/list": true,
"container/ring": true,
"context": true,
"crypto": true,
"crypto/aes": true,
"crypto/cipher": true,
"crypto/des": true,
"crypto/dsa": true,
"crypto/ecdsa": true,
"crypto/elliptic": true,
"crypto/hmac": true,
"crypto/internal/cipherhw": true,
"crypto/md5": true,
"crypto/rand": true,
"crypto/rc4": true,
"crypto/rsa": true,
"crypto/sha1": true,
"crypto/sha256": true,
"crypto/sha512": true,
"crypto/subtle": true,
"crypto/tls": true,
"crypto/x509": true,
"crypto/x509/pkix": true,
"database/sql": true,
"database/sql/driver": true,
"debug/dwarf": true,
"debug/elf": true,
"debug/gosym": true,
"debug/macho": true,
"debug/pe": true,
"debug/plan9obj": true,
"encoding": true,
"encoding/ascii85": true,
"encoding/asn1": true,
"encoding/base32": true,
"encoding/base64": true,
"encoding/binary": true,
"encoding/csv": true,
"encoding/gob": true,
"encoding/hex": true,
"encoding/json": true,
"encoding/pem": true,
"encoding/xml": true,
"errors": true,
"expvar": true,
"flag": true,
"fmt": true,
"go/ast": true,
"go/build": true,
"go/constant": true,
"go/doc": true,
"go/format": true,
"go/importer": true,
"go/internal/gccgoimporter": true,
"go/internal/gcimporter": true,
"go/internal/srcimporter": true,
"go/parser": true,
"go/printer": true,
"go/scanner": true,
"go/token": true,
"go/types": true,
"hash": true,
"hash/adler32": true,
"hash/crc32": true,
"hash/crc64": true,
"hash/fnv": true,
"html": true,
"html/template": true,
"image": true,
"image/color": true,
"image/color/palette": true,
"image/draw": true,
"image/gif": true,
"image/internal/imageutil": true,
"image/jpeg": true,
"image/png": true,
"index/suffixarray": true,
"internal/cpu": true,
"internal/nettrace": true,
"internal/poll": true,
"internal/race": true,
"internal/singleflight": true,
"internal/syscall/unix": true,
"internal/syscall/windows": true,
"internal/syscall/windows/registry": true,
"internal/syscall/windows/sysdll": true,
"internal/testenv": true,
"internal/trace": true,
"io": true,
"io/ioutil": true,
"log": true,
"log/syslog": true,
"math": true,
"math/big": true,
"math/bits": true,
"math/cmplx": true,
"math/rand": true,
"mime": true,
"mime/multipart": true,
"mime/quotedprintable": true,
"net": true,
"net/http": true,
"net/http/cgi": true,
"net/http/cookiejar": true,
"net/http/fcgi": true,
"net/http/httptest": true,
"net/http/httptrace": true,
"net/http/httputil": true,
"net/http/internal": true,
"net/http/pprof": true,
"net/internal/socktest": true,
"net/mail": true,
"net/rpc": true,
"net/rpc/jsonrpc": true,
"net/smtp": true,
"net/textproto": true,
"net/url": true,
"os": true,
"os/exec": true,
"os/signal": true,
"os/user": true,
"path": true,
"path/filepath": true,
"plugin": true,
"reflect": true,
"regexp": true,
"regexp/syntax": true,
"runtime": true,
"runtime/cgo": true,
"runtime/debug": true,
"runtime/internal/atomic": true,
"runtime/internal/sys": true,
"runtime/pprof": true,
"runtime/pprof/internal/profile": true,
"runtime/race": true,
"runtime/trace": true,
"sort": true,
"strconv": true,
"strings": true,
"sync": true,
"sync/atomic": true,
"syscall": true,
"testing": true,
"testing/internal/testdeps": true,
"testing/iotest": true,
"testing/quick": true,
"text/scanner": true,
"text/tabwriter": true,
"text/template": true,
"text/template/parse": true,
"time": true,
"unicode": true,
"unicode/utf16": true,
"unicode/utf8": true,
"unsafe": true,
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"construct.go",
"doc.go",
"generator.go",
"sort_labels.go",
],
importpath = "github.com/bazelbuild/bazel-gazelle/internal/rules",
visibility = ["//vendor/github.com/bazelbuild/bazel-gazelle:__subpackages__"],
deps = [
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/config:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/label:go_default_library",
"//vendor/github.com/bazelbuild/bazel-gazelle/internal/packages:go_default_library",
"//vendor/github.com/bazelbuild/buildtools/build:go_default_library",
"//vendor/github.com/bazelbuild/buildtools/tables: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"],
)
/* 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 rules
import (
"fmt"
"log"
"reflect"
"sort"
"github.com/bazelbuild/bazel-gazelle/internal/config"
"github.com/bazelbuild/bazel-gazelle/internal/packages"
bf "github.com/bazelbuild/buildtools/build"
bt "github.com/bazelbuild/buildtools/tables"
)
// KeyValue represents a key-value pair. This gets converted into a
// rule attribute, i.e., a Skylark keyword argument.
type KeyValue struct {
Key string
Value interface{}
}
// GlobValue represents a Bazel glob expression.
type GlobValue struct {
Patterns []string
Excludes []string
}
// EmptyRule generates an empty rule with the given kind and name.
func EmptyRule(kind, name string) *bf.CallExpr {
return NewRule(kind, []KeyValue{{"name", name}})
}
// NewRule generates a rule of the given kind with the given attributes.
func NewRule(kind string, kwargs []KeyValue) *bf.CallExpr {
sort.Sort(byAttrName(kwargs))
var list []bf.Expr
for _, arg := range kwargs {
expr := newValue(arg.Value)
list = append(list, &bf.BinaryExpr{
X: &bf.LiteralExpr{Token: arg.Key},
Op: "=",
Y: expr,
})
}
return &bf.CallExpr{
X: &bf.LiteralExpr{Token: kind},
List: list,
}
}
// newValue converts a Go value into the corresponding expression in Bazel BUILD file.
func newValue(val interface{}) bf.Expr {
rv := reflect.ValueOf(val)
switch rv.Kind() {
case reflect.Bool:
tok := "False"
if rv.Bool() {
tok = "True"
}
return &bf.LiteralExpr{Token: tok}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return &bf.LiteralExpr{Token: fmt.Sprintf("%d", val)}
case reflect.Float32, reflect.Float64:
return &bf.LiteralExpr{Token: fmt.Sprintf("%f", val)}
case reflect.String:
return &bf.StringExpr{Value: val.(string)}
case reflect.Slice, reflect.Array:
var list []bf.Expr
for i := 0; i < rv.Len(); i++ {
elem := newValue(rv.Index(i).Interface())
list = append(list, elem)
}
return &bf.ListExpr{List: list}
case reflect.Map:
rkeys := rv.MapKeys()
sort.Sort(byString(rkeys))
args := make([]bf.Expr, len(rkeys))
for i, rk := range rkeys {
label := fmt.Sprintf("@%s//go/platform:%s", config.RulesGoRepoName, mapKeyString(rk))
k := &bf.StringExpr{Value: label}
v := newValue(rv.MapIndex(rk).Interface())
if l, ok := v.(*bf.ListExpr); ok {
l.ForceMultiLine = true
}
args[i] = &bf.KeyValueExpr{Key: k, Value: v}
}
args = append(args, &bf.KeyValueExpr{
Key: &bf.StringExpr{Value: "//conditions:default"},
Value: &bf.ListExpr{},
})
sel := &bf.CallExpr{
X: &bf.LiteralExpr{Token: "select"},
List: []bf.Expr{&bf.DictExpr{List: args, ForceMultiLine: true}},
}
return sel
case reflect.Struct:
switch val := val.(type) {
case GlobValue:
patternsValue := newValue(val.Patterns)
globArgs := []bf.Expr{patternsValue}
if len(val.Excludes) > 0 {
excludesValue := newValue(val.Excludes)
globArgs = append(globArgs, &bf.KeyValueExpr{
Key: &bf.StringExpr{Value: "excludes"},
Value: excludesValue,
})
}
return &bf.CallExpr{
X: &bf.LiteralExpr{Token: "glob"},
List: globArgs,
}
case packages.PlatformStrings:
var pieces []bf.Expr
if len(val.Generic) > 0 {
pieces = append(pieces, newValue(val.Generic))
}
if len(val.OS) > 0 {
pieces = append(pieces, newValue(val.OS))
}
if len(val.Arch) > 0 {
pieces = append(pieces, newValue(val.Arch))
}
if len(val.Platform) > 0 {
pieces = append(pieces, newValue(val.Platform))
}
if len(pieces) == 0 {
return &bf.ListExpr{}
} else if len(pieces) == 1 {
return pieces[0]
} else {
e := pieces[0]
if list, ok := e.(*bf.ListExpr); ok {
list.ForceMultiLine = true
}
for _, piece := range pieces[1:] {
e = &bf.BinaryExpr{X: e, Y: piece, Op: "+"}
}
return e
}
}
}
log.Panicf("type not supported: %T", val)
return nil
}
func mapKeyString(k reflect.Value) string {
switch s := k.Interface().(type) {
case string:
return s
case config.Platform:
return s.String()
default:
log.Panicf("unexpected map key: %v", k)
return ""
}
}
type byAttrName []KeyValue
var _ sort.Interface = byAttrName{}
func (s byAttrName) Len() int {
return len(s)
}
func (s byAttrName) Less(i, j int) bool {
if cmp := bt.NamePriority[s[i].Key] - bt.NamePriority[s[j].Key]; cmp != 0 {
return cmp < 0
}
return s[i].Key < s[j].Key
}
func (s byAttrName) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
type byString []reflect.Value
var _ sort.Interface = byString{}
func (s byString) Len() int {
return len(s)
}
func (s byString) Less(i, j int) bool {
return mapKeyString(s[i]) < mapKeyString(s[j])
}
func (s byString) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
/* 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 rules provides Bazel rule generation for Go build targets.
package rules
/* 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 rules
import (
"fmt"
"log"
"path"
"strings"
"github.com/bazelbuild/bazel-gazelle/internal/config"
"github.com/bazelbuild/bazel-gazelle/internal/label"
"github.com/bazelbuild/bazel-gazelle/internal/packages"
bf "github.com/bazelbuild/buildtools/build"
)
// NewGenerator returns a new instance of Generator.
// "oldFile" is the existing build file. May be nil.
func NewGenerator(c *config.Config, l *label.Labeler, oldFile *bf.File) *Generator {
shouldSetVisibility := oldFile == nil || !hasDefaultVisibility(oldFile)
return &Generator{c: c, l: l, shouldSetVisibility: shouldSetVisibility}
}
// Generator generates Bazel build rules for Go build targets.
type Generator struct {
c *config.Config
l *label.Labeler
shouldSetVisibility bool
}
// GenerateRules generates a list of rules for targets in "pkg". It also returns
// a list of empty rules that may be deleted from an existing file.
func (g *Generator) GenerateRules(pkg *packages.Package) (rules []bf.Expr, empty []bf.Expr, err error) {
var rs []bf.Expr
protoLibName, protoRules := g.generateProto(pkg)
rs = append(rs, protoRules...)
libName, libRule := g.generateLib(pkg, protoLibName)
rs = append(rs, libRule)
rs = append(rs,
g.generateBin(pkg, libName),
g.generateTest(pkg, libName, false),
g.generateTest(pkg, "", true))
for _, r := range rs {
if isEmpty(r) {
empty = append(empty, r)
} else {
rules = append(rules, r)
}
}
return rules, empty, nil
}
func (g *Generator) generateProto(pkg *packages.Package) (string, []bf.Expr) {
if g.c.ProtoMode == config.DisableProtoMode {
// Don't create or delete proto rules in this mode. Any existing rules
// are likely hand-written.
return "", nil
}
filegroupName := config.DefaultProtosName
protoName := g.l.ProtoLabel(pkg.Rel, pkg.Name).Name
goProtoName := g.l.GoProtoLabel(pkg.Rel, pkg.Name).Name
if g.c.ProtoMode == config.LegacyProtoMode {
if !pkg.Proto.HasProto() {
return "", []bf.Expr{EmptyRule("filegroup", filegroupName)}
}
attrs := []KeyValue{
{Key: "name", Value: filegroupName},
{Key: "srcs", Value: pkg.Proto.Sources},
}
if g.shouldSetVisibility {
attrs = append(attrs, KeyValue{"visibility", []string{checkInternalVisibility(pkg.Rel, "//visibility:public")}})
}
return "", []bf.Expr{NewRule("filegroup", attrs)}
}
if !pkg.Proto.HasProto() {
return "", []bf.Expr{
EmptyRule("filegroup", filegroupName),
EmptyRule("proto_library", protoName),
EmptyRule("go_proto_library", goProtoName),
}
}
var rules []bf.Expr
visibility := []string{checkInternalVisibility(pkg.Rel, "//visibility:public")}
protoAttrs := []KeyValue{
{"name", protoName},
{"srcs", pkg.Proto.Sources},
}
if g.shouldSetVisibility {
protoAttrs = append(protoAttrs, KeyValue{"visibility", visibility})
}
imports := pkg.Proto.Imports
if !imports.IsEmpty() {
protoAttrs = append(protoAttrs, KeyValue{config.GazelleImportsKey, imports})
}
rules = append(rules, NewRule("proto_library", protoAttrs))
goProtoAttrs := []KeyValue{
{"name", goProtoName},
{"proto", ":" + protoName},
{"importpath", pkg.ImportPath},
}
if pkg.Proto.HasServices {
goProtoAttrs = append(goProtoAttrs, KeyValue{"compilers", []string{"@io_bazel_rules_go//proto:go_grpc"}})
}
if g.shouldSetVisibility {
goProtoAttrs = append(goProtoAttrs, KeyValue{"visibility", visibility})
}
if !imports.IsEmpty() {
goProtoAttrs = append(goProtoAttrs, KeyValue{config.GazelleImportsKey, imports})
}
rules = append(rules, NewRule("go_proto_library", goProtoAttrs))
return goProtoName, rules
}
func (g *Generator) generateBin(pkg *packages.Package, library string) bf.Expr {
name := g.l.BinaryLabel(pkg.Rel).Name
if !pkg.IsCommand() || pkg.Binary.Sources.IsEmpty() && library == "" {
return EmptyRule("go_binary", name)
}
visibility := checkInternalVisibility(pkg.Rel, "//visibility:public")
attrs := g.commonAttrs(pkg.Rel, name, visibility, pkg.Binary)
if library != "" {
attrs = append(attrs, KeyValue{"embed", []string{":" + library}})
}
return NewRule("go_binary", attrs)
}
func (g *Generator) generateLib(pkg *packages.Package, goProtoName string) (string, *bf.CallExpr) {
name := g.l.LibraryLabel(pkg.Rel).Name
if !pkg.Library.HasGo() && goProtoName == "" {
return "", EmptyRule("go_library", name)
}
var visibility string
if pkg.IsCommand() {
// Libraries made for a go_binary should not be exposed to the public.
visibility = "//visibility:private"
} else {
visibility = checkInternalVisibility(pkg.Rel, "//visibility:public")
}
attrs := g.commonAttrs(pkg.Rel, name, visibility, pkg.Library)
attrs = append(attrs, KeyValue{"importpath", pkg.ImportPath})
if goProtoName != "" {
attrs = append(attrs, KeyValue{"embed", []string{":" + goProtoName}})
}
rule := NewRule("go_library", attrs)
return name, rule
}
// hasDefaultVisibility returns whether oldFile contains a "package" rule with
// a "default_visibility" attribute. Rules generated by Gazelle should not
// have their own visibility attributes if this is the case.
func hasDefaultVisibility(oldFile *bf.File) bool {
for _, s := range oldFile.Stmt {
c, ok := s.(*bf.CallExpr)
if !ok {
continue
}
r := bf.Rule{Call: c}
if r.Kind() == "package" && r.Attr("default_visibility") != nil {
return true
}
}
return false
}
// checkInternalVisibility overrides the given visibility if the package is
// internal.
func checkInternalVisibility(rel, visibility string) string {
if i := strings.LastIndex(rel, "/internal/"); i >= 0 {
visibility = fmt.Sprintf("//%s:__subpackages__", rel[:i])
} else if strings.HasPrefix(rel, "internal/") {
visibility = "//:__subpackages__"
}
return visibility
}
func (g *Generator) generateTest(pkg *packages.Package, library string, isXTest bool) bf.Expr {
name := g.l.TestLabel(pkg.Rel, isXTest).Name
target := pkg.Test
if isXTest {
target = pkg.XTest
}
if !target.HasGo() {
return EmptyRule("go_test", name)
}
attrs := g.commonAttrs(pkg.Rel, name, "", target)
if library != "" {
attrs = append(attrs, KeyValue{"embed", []string{":" + library}})
}
if pkg.HasTestdata {
glob := GlobValue{Patterns: []string{"testdata/**"}}
attrs = append(attrs, KeyValue{"data", glob})
}
return NewRule("go_test", attrs)
}
func (g *Generator) commonAttrs(pkgRel, name, visibility string, target packages.GoTarget) []KeyValue {
attrs := []KeyValue{{"name", name}}
if !target.Sources.IsEmpty() {
attrs = append(attrs, KeyValue{"srcs", target.Sources})
}
if target.Cgo {
attrs = append(attrs, KeyValue{"cgo", true})
}
if !target.CLinkOpts.IsEmpty() {
attrs = append(attrs, KeyValue{"clinkopts", g.options(target.CLinkOpts, pkgRel)})
}
if !target.COpts.IsEmpty() {
attrs = append(attrs, KeyValue{"copts", g.options(target.COpts, pkgRel)})
}
if g.shouldSetVisibility && visibility != "" {
attrs = append(attrs, KeyValue{"visibility", []string{visibility}})
}
imports := target.Imports
if !imports.IsEmpty() {
attrs = append(attrs, KeyValue{config.GazelleImportsKey, imports})
}
return attrs
}
var (
// shortOptPrefixes are strings that come at the beginning of an option
// argument that includes a path, e.g., -Ifoo/bar.
shortOptPrefixes = []string{"-I", "-L", "-F"}
// longOptPrefixes are separate arguments that come before a path argument,
// e.g., -iquote foo/bar.
longOptPrefixes = []string{"-I", "-L", "-F", "-iquote", "-isystem"}
)
// options transforms package-relative paths in cgo options into repository-
// root-relative paths that Bazel can understand. For example, if a cgo file
// in //foo declares an include flag in its copts: "-Ibar", this method
// will transform that flag into "-Ifoo/bar".
func (g *Generator) options(opts packages.PlatformStrings, pkgRel string) packages.PlatformStrings {
fixPath := func(opt string) string {
if strings.HasPrefix(opt, "/") {
return opt
}
return path.Clean(path.Join(pkgRel, opt))
}
fixGroups := func(groups []string) ([]string, error) {
fixedGroups := make([]string, len(groups))
for i, group := range groups {
opts := strings.Split(group, packages.OptSeparator)
fixedOpts := make([]string, len(opts))
isPath := false
for j, opt := range opts {
if isPath {
opt = fixPath(opt)
isPath = false
goto next
}
for _, short := range shortOptPrefixes {
if strings.HasPrefix(opt, short) && len(opt) > len(short) {
opt = short + fixPath(opt[len(short):])
goto next
}
}
for _, long := range longOptPrefixes {
if opt == long {
isPath = true
goto next
}
}
next:
fixedOpts[j] = escapeOption(opt)
}
fixedGroups[i] = strings.Join(fixedOpts, " ")
}
return fixedGroups, nil
}
opts, errs := opts.MapSlice(fixGroups)
if errs != nil {
log.Panicf("unexpected error when transforming options with pkg %q: %v", pkgRel, errs)
}
return opts
}
func escapeOption(opt string) string {
return strings.NewReplacer(
`\`, `\\`,
`'`, `\'`,
`"`, `\"`,
` `, `\ `,
"\t", "\\\t",
"\n", "\\\n",
"\r", "\\\r",
).Replace(opt)
}
func isEmpty(r bf.Expr) bool {
c, ok := r.(*bf.CallExpr)
return ok && len(c.List) == 1 // name
}
/* Copyright 2017 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 rules
import (
"sort"
"strings"
bf "github.com/bazelbuild/buildtools/build"
)
var (
goRuleKinds = map[string]bool{
"cgo_library": true,
"go_binary": true,
"go_library": true,
"go_test": true,
}
sortedAttrs = []string{"srcs", "deps"}
)
// SortLabels sorts lists of strings in "srcs" and "deps" attributes of
// Go rules using the same order as buildifier. Buildifier also sorts string
// lists, but not those involved with "select" expressions.
// TODO(jayconrod): remove this when bazelbuild/buildtools#122 is fixed.
func SortLabels(f *bf.File) {
for _, s := range f.Stmt {
c, ok := s.(*bf.CallExpr)
if !ok {
continue
}
r := bf.Rule{Call: c}
if !goRuleKinds[r.Kind()] {
continue
}
for _, key := range []string{"srcs", "deps"} {
attr := r.AttrDefn(key)
if attr == nil {
continue
}
bf.Walk(attr.Y, sortExprLabels)
}
}
}
func sortExprLabels(e bf.Expr, _ []bf.Expr) {
list, ok := e.(*bf.ListExpr)
if !ok || len(list.List) == 0 {
return
}
keys := make([]stringSortKey, len(list.List))
for i, elem := range list.List {
s, ok := elem.(*bf.StringExpr)
if !ok {
return // don't sort lists unless all elements are strings
}
keys[i] = makeSortKey(i, s)
}
before := keys[0].x.Comment().Before
keys[0].x.Comment().Before = nil
sort.Sort(byStringExpr(keys))
keys[0].x.Comment().Before = append(before, keys[0].x.Comment().Before...)
for i, k := range keys {
list.List[i] = k.x
}
}
// Code below this point is adapted from
// github.com/bazelbuild/buildtools/build/rewrite.go
// A stringSortKey records information about a single string literal to be
// sorted. The strings are first grouped into four phases: most strings,
// strings beginning with ":", strings beginning with "//", and strings
// beginning with "@". The next significant part of the comparison is the list
// of elements in the value, where elements are split at `.' and `:'. Finally
// we compare by value and break ties by original index.
type stringSortKey struct {
phase int
split []string
value string
original int
x bf.Expr
}
func makeSortKey(index int, x *bf.StringExpr) stringSortKey {
key := stringSortKey{
value: x.Value,
original: index,
x: x,
}
switch {
case strings.HasPrefix(x.Value, ":"):
key.phase = 1
case strings.HasPrefix(x.Value, "//"):
key.phase = 2
case strings.HasPrefix(x.Value, "@"):
key.phase = 3
}
key.split = strings.Split(strings.Replace(x.Value, ":", ".", -1), ".")
return key
}
// byStringExpr implements sort.Interface for a list of stringSortKey.
type byStringExpr []stringSortKey
func (x byStringExpr) Len() int { return len(x) }
func (x byStringExpr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byStringExpr) Less(i, j int) bool {
xi := x[i]
xj := x[j]
if xi.phase != xj.phase {
return xi.phase < xj.phase
}
for k := 0; k < len(xi.split) && k < len(xj.split); k++ {
if xi.split[k] != xj.split[k] {
return xi.split[k] < xj.split[k]
}
}
if len(xi.split) != len(xj.split) {
return len(xi.split) < len(xj.split)
}
if xi.value != xj.value {
return xi.value < xj.value
}
return xi.original < xj.original
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["version.go"],
importpath = "github.com/bazelbuild/bazel-gazelle/internal/version",
visibility = ["//vendor/github.com/bazelbuild/bazel-gazelle:__subpackages__"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
/* Copyright 2018 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 version
import (
"fmt"
"strconv"
"strings"
)
// Version is a tuple of non-negative integers that represents the version of
// a software package.
type Version []int
func (v Version) String() string {
cstrs := make([]string, len(v))
for i, cn := range v {
cstrs[i] = strconv.Itoa(cn)
}
return strings.Join(cstrs, ".")
}
// Compare returns an integer comparing two versions lexicographically.
func (x Version) Compare(y Version) int {
n := len(x)
if len(y) < n {
n = len(y)
}
for i := 0; i < n; i++ {
cmp := x[i] - y[i]
if cmp != 0 {
return cmp
}
}
return len(x) - len(y)
}
// ParseVersion parses a version of the form "12.34.56-abcd". Non-negative
// integer components are separated by dots. An arbitrary suffix may appear
// after '-', which is ignored.
func ParseVersion(vs string) (Version, error) {
i := strings.IndexByte(vs, '-')
if i >= 0 {
vs = vs[:i]
}
cstrs := strings.Split(vs, ".")
v := make(Version, len(cstrs))
for i, cstr := range cstrs {
cn, err := strconv.Atoi(cstr)
if err != nil {
return nil, fmt.Errorf("could not parse version string: %q is not an integer", cstr)
}
if cn < 0 {
return nil, fmt.Errorf("could not parse version string: %q is negative", cstr)
}
v[i] = cn
}
return v, nil
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["finder.go"],
importpath = "github.com/bazelbuild/bazel-gazelle/internal/wspace",
visibility = ["//vendor/github.com/bazelbuild/bazel-gazelle:__subpackages__"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
/* 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 wspace provides functions to locate and modify a bazel WORKSPACE file.
package wspace
import (
"os"
"path/filepath"
"strings"
)
const workspaceFile = "WORKSPACE"
// Find searches from the given dir and up for the WORKSPACE file
// returning the directory containing it, or an error if none found in the tree.
func Find(dir string) (string, error) {
dir, err := filepath.Abs(dir)
if err != nil {
return "", err
}
for {
_, err = os.Stat(filepath.Join(dir, workspaceFile))
if err == nil {
return dir, nil
}
if !os.IsNotExist(err) {
return "", err
}
if strings.HasSuffix(dir, string(os.PathSeparator)) { // stop at root dir
return "", os.ErrNotExist
}
dir = filepath.Dir(dir)
}
}
# People who have agreed to one of the CLAs and can contribute patches.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# https://developers.google.com/open-source/cla/individual
# https://developers.google.com/open-source/cla/corporate
#
# Names should be added to this file as:
# Name <email address>
Paul Bethe <pbethe@google.com>
Russ Cox <rsc@google.com>
Laurent Le Brun <laurentlb@google.com>
Justine Alexandra Roberts Tunney <jart@google.com>
Nilton Volpato <nilton@google.com>
\ No newline at end of file
Copyright 2016 Google Inc. 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.
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"lex.go",
"parse.y.go",
"print.go",
"quote.go",
"rewrite.go",
"rule.go",
"syntax.go",
"walk.go",
],
importpath = "github.com/bazelbuild/buildtools/build",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/bazelbuild/buildtools/tables: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"],
)
"""Provides go_yacc and genfile_check_test
Copyright 2016 Google Inc. 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.
"""
_GO_YACC_TOOL = "@org_golang_x_tools//cmd/goyacc"
def go_yacc(src, out, visibility=None):
"""Runs go tool yacc -o $out $src."""
native.genrule(
name = src + ".go_yacc",
srcs = [src],
outs = [out],
tools = [_GO_YACC_TOOL],
cmd = ("export GOROOT=$$(dirname $(location " + _GO_YACC_TOOL + "))/..;" +
" $(location " + _GO_YACC_TOOL + ") " +
" -o $(location " + out + ") $(SRCS)"),
visibility = visibility,
local = 1,
)
def genfile_check_test(src, gen):
"""Asserts that any checked-in generated code matches regen."""
if not src:
fail("src is required", "src")
if not gen:
fail("gen is required", "gen")
native.genrule(
name = src + "_checksh",
outs = [src + "_check.sh"],
cmd = "echo 'diff $$@' > $@",
)
native.sh_test(
name = src + "_checkshtest",
size = "small",
srcs = [src + "_check.sh"],
data = [src, gen],
args = ["$(location " + src + ")", "$(location " + gen + ")"],
)
/*
Copyright 2016 Google Inc. 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.
*/
// Python quoted strings.
package build
import (
"bytes"
"fmt"
"strconv"
"strings"
)
// unesc maps single-letter chars following \ to their actual values.
var unesc = [256]byte{
'a': '\a',
'b': '\b',
'f': '\f',
'n': '\n',
'r': '\r',
't': '\t',
'v': '\v',
'\\': '\\',
'\'': '\'',
'"': '"',
}
// esc maps escape-worthy bytes to the char that should follow \.
var esc = [256]byte{
'\a': 'a',
'\b': 'b',
'\f': 'f',
'\n': 'n',
'\r': 'r',
'\t': 't',
'\v': 'v',
'\\': '\\',
'\'': '\'',
'"': '"',
}
// notEsc is a list of characters that can follow a \ in a string value
// without having to escape the \. That is, since ( is in this list, we
// quote the Go string "foo\\(bar" as the Python literal "foo\(bar".
// This really does happen in BUILD files, especially in strings
// being used as shell arguments containing regular expressions.
const notEsc = " !#$%&()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~"
// unquote unquotes the quoted string, returning the actual
// string value, whether the original was triple-quoted, and
// an error describing invalid input.
func unquote(quoted string) (s string, triple bool, err error) {
// Check for raw prefix: means don't interpret the inner \.
raw := false
if strings.HasPrefix(quoted, "r") {
raw = true
quoted = quoted[1:]
}
if len(quoted) < 2 {
err = fmt.Errorf("string literal too short")
return
}
if quoted[0] != '"' && quoted[0] != '\'' || quoted[0] != quoted[len(quoted)-1] {
err = fmt.Errorf("string literal has invalid quotes")
}
// Check for triple quoted string.
quote := quoted[0]
if len(quoted) >= 6 && quoted[1] == quote && quoted[2] == quote && quoted[:3] == quoted[len(quoted)-3:] {
triple = true
quoted = quoted[3 : len(quoted)-3]
} else {
quoted = quoted[1 : len(quoted)-1]
}
// Now quoted is the quoted data, but no quotes.
// If we're in raw mode or there are no escapes, we're done.
if raw || !strings.Contains(quoted, `\`) {
s = quoted
return
}
// Otherwise process quoted string.
// Each iteration processes one escape sequence along with the
// plain text leading up to it.
var buf bytes.Buffer
for {
// Remove prefix before escape sequence.
i := strings.Index(quoted, `\`)
if i < 0 {
i = len(quoted)
}
buf.WriteString(quoted[:i])
quoted = quoted[i:]
if len(quoted) == 0 {
break
}
// Process escape sequence.
if len(quoted) == 1 {
err = fmt.Errorf(`truncated escape sequence \`)
return
}
switch quoted[1] {
default:
// In Python, if \z (for some byte z) is not a known escape sequence
// then it appears as literal text in the string.
buf.WriteString(quoted[:2])
quoted = quoted[2:]
case '\n':
// Ignore the escape and the line break.
quoted = quoted[2:]
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"':
// One-char escape
buf.WriteByte(unesc[quoted[1]])
quoted = quoted[2:]
case '0', '1', '2', '3', '4', '5', '6', '7':
// Octal escape, up to 3 digits.
n := int(quoted[1] - '0')
quoted = quoted[2:]
for i := 1; i < 3; i++ {
if len(quoted) == 0 || quoted[0] < '0' || '7' < quoted[0] {
break
}
n = n*8 + int(quoted[0]-'0')
quoted = quoted[1:]
}
if n >= 256 {
// NOTE: Python silently discards the high bit,
// so that '\541' == '\141' == 'a'.
// Let's see if we can avoid doing that in BUILD files.
err = fmt.Errorf(`invalid escape sequence \%03o`, n)
return
}
buf.WriteByte(byte(n))
case 'x':
// Hexadecimal escape, exactly 2 digits.
if len(quoted) < 4 {
err = fmt.Errorf(`truncated escape sequence %s`, quoted)
return
}
n, err1 := strconv.ParseInt(quoted[2:4], 16, 0)
if err1 != nil {
err = fmt.Errorf(`invalid escape sequence %s`, quoted[:4])
return
}
buf.WriteByte(byte(n))
quoted = quoted[4:]
}
}
s = buf.String()
return
}
// indexByte returns the index of the first instance of b in s, or else -1.
func indexByte(s string, b byte) int {
for i := 0; i < len(s); i++ {
if s[i] == b {
return i
}
}
return -1
}
// hex is a list of the hexadecimal digits, for use in quoting.
// We always print lower-case hexadecimal.
const hex = "0123456789abcdef"
// quote returns the quoted form of the string value "x".
// If triple is true, quote uses the triple-quoted form """x""".
func quote(unquoted string, triple bool) string {
q := `"`
if triple {
q = `"""`
}
var buf bytes.Buffer
buf.WriteString(q)
for i := 0; i < len(unquoted); i++ {
c := unquoted[i]
if c == '"' && triple && (i+1 < len(unquoted) && unquoted[i+1] != '"' || i+2 < len(unquoted) && unquoted[i+2] != '"') {
// Can pass up to two quotes through, because they are followed by a non-quote byte.
buf.WriteByte(c)
if i+1 < len(unquoted) && unquoted[i+1] == '"' {
buf.WriteByte(c)
i++
}
continue
}
if triple && c == '\n' {
// Can allow newline in triple-quoted string.
buf.WriteByte(c)
continue
}
if c == '\'' {
// Can allow ' since we always use ".
buf.WriteByte(c)
continue
}
if c == '\\' {
if i+1 < len(unquoted) && indexByte(notEsc, unquoted[i+1]) >= 0 {
// Can pass \ through when followed by a byte that
// known not to be a valid escape sequence and also
// that does not trigger an escape sequence of its own.
// Use this, because various BUILD files do.
buf.WriteByte('\\')
buf.WriteByte(unquoted[i+1])
i++
continue
}
}
if esc[c] != 0 {
buf.WriteByte('\\')
buf.WriteByte(esc[c])
continue
}
if c < 0x20 || c >= 0x80 {
// BUILD files are supposed to be Latin-1, so escape all control and high bytes.
// I'd prefer to use \x here, but Blaze does not implement
// \x in quoted strings (b/7272572).
buf.WriteByte('\\')
buf.WriteByte(hex[c>>6]) // actually octal but reusing hex digits 0-7.
buf.WriteByte(hex[(c>>3)&7])
buf.WriteByte(hex[c&7])
/*
buf.WriteByte('\\')
buf.WriteByte('x')
buf.WriteByte(hex[c>>4])
buf.WriteByte(hex[c&0xF])
*/
continue
}
buf.WriteByte(c)
continue
}
buf.WriteString(q)
return buf.String()
}
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