| // Copyright 2021 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 hermeticity_test |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "fmt" |
| "strings" |
| "testing" |
| |
| "github.com/bazelbuild/rules_go/go/tools/bazel_testing" |
| ) |
| |
| func TestMain(m *testing.M) { |
| bazel_testing.TestMain(m, bazel_testing.Args{ |
| Main: ` |
| -- BUILD.bazel -- |
| load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") |
| load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") |
| load("@rules_proto//proto:defs.bzl", "proto_library") |
| |
| go_binary( |
| name = "main", |
| srcs = [ |
| "main.go", |
| ":gen_go", |
| ], |
| data = [":helper"], |
| embedsrcs = [":helper"], |
| cdeps = [":helper"], |
| cgo = True, |
| linkmode = "c-archive", |
| gotags = ["foo"], |
| deps = [":lib"], |
| ) |
| |
| go_library( |
| name = "lib", |
| srcs = [ |
| "lib.go", |
| ":gen_indirect_go", |
| ], |
| importpath = "example.com/lib", |
| data = [":indirect_helper"], |
| embedsrcs = [":indirect_helper"], |
| cdeps = [":indirect_helper"], |
| cgo = True, |
| ) |
| |
| go_test( |
| name = "main_test", |
| srcs = [ |
| "main.go", |
| ":gen_go", |
| ], |
| data = [":helper"], |
| embedsrcs = [":helper"], |
| cdeps = [":helper"], |
| cgo = True, |
| linkmode = "c-archive", |
| gotags = ["foo"], |
| ) |
| |
| cc_library( |
| name = "helper", |
| ) |
| |
| cc_library( |
| name = "indirect_helper", |
| ) |
| |
| genrule( |
| name = "gen_go", |
| outs = ["gen.go"], |
| exec_tools = [":helper"], |
| cmd = "# Not needed for bazel cquery", |
| ) |
| |
| genrule( |
| name = "gen_indirect_go", |
| outs = ["gen_indirect.go"], |
| exec_tools = [":indirect_helper"], |
| cmd = "# Not needed for bazel cquery", |
| ) |
| |
| proto_library( |
| name = "foo_proto", |
| srcs = ["foo.proto"], |
| ) |
| |
| go_proto_library( |
| name = "foo_go_proto", |
| importpath = "github.com/bazelbuild/rules_go/tests/core/transition/foo", |
| proto = ":foo_proto", |
| ) |
| -- main.go -- |
| package main |
| |
| func main() {} |
| -- lib.go -- |
| -- foo.proto -- |
| syntax = "proto3"; |
| |
| package tests.core.transition.foo; |
| option go_package = "github.com/bazelbuild/rules_go/tests/core/transition/foo"; |
| |
| message Foo { |
| int64 value = 1; |
| } |
| `, |
| WorkspaceSuffix: ` |
| load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") |
| |
| http_archive( |
| name = "com_google_protobuf", |
| sha256 = "75be42bd736f4df6d702a0e4e4d30de9ee40eac024c4b845d17ae4cc831fe4ae", |
| strip_prefix = "protobuf-21.7", |
| # latest available in BCR, as of 2022-09-30 |
| urls = [ |
| "https://github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz", |
| "https://mirror.bazel.build/github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz", |
| ], |
| ) |
| |
| load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") |
| |
| protobuf_deps() |
| |
| http_archive( |
| name = "rules_proto", |
| sha256 = "4d421d51f9ecfe9bf96ab23b55c6f2b809cbaf0eea24952683e397decfbd0dd0", |
| strip_prefix = "rules_proto-f6b8d89b90a7956f6782a4a3609b2f0eee3ce965", |
| # master, as of 2020-01-06 |
| urls = [ |
| "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/f6b8d89b90a7956f6782a4a3609b2f0eee3ce965.tar.gz", |
| "https://github.com/bazelbuild/rules_proto/archive/f6b8d89b90a7956f6782a4a3609b2f0eee3ce965.tar.gz", |
| ], |
| ) |
| `, |
| }) |
| } |
| |
| func TestGoBinaryNonGoAttrsAreReset(t *testing.T) { |
| assertDependsCleanlyOnWithFlags( |
| t, |
| "//:main", |
| "//:helper") |
| } |
| |
| func TestGoLibraryNonGoAttrsAreReset(t *testing.T) { |
| assertDependsCleanlyOnWithFlags( |
| t, |
| "//:main", |
| "//:indirect_helper") |
| } |
| |
| func TestGoTestNonGoAttrsAreReset(t *testing.T) { |
| assertDependsCleanlyOnWithFlags( |
| t, |
| "//:main_test", |
| "//:helper") |
| } |
| |
| func TestGoProtoLibraryToolAttrsAreReset(t *testing.T) { |
| assertDependsCleanlyOnWithFlags( |
| t, |
| "//:foo_go_proto", |
| "@com_google_protobuf//:protoc", |
| "--@io_bazel_rules_go//go/config:static", |
| "--@io_bazel_rules_go//go/config:msan", |
| "--@io_bazel_rules_go//go/config:race", |
| "--@io_bazel_rules_go//go/config:debug", |
| "--@io_bazel_rules_go//go/config:linkmode=c-archive", |
| "--@io_bazel_rules_go//go/config:tags=fake_tag", |
| ) |
| assertDependsCleanlyOnWithFlags( |
| t, |
| "//:foo_go_proto", |
| "@com_google_protobuf//:protoc", |
| "--@io_bazel_rules_go//go/config:pure", |
| ) |
| } |
| |
| func assertDependsCleanlyOnWithFlags(t *testing.T, targetA, targetB string, flags ...string) { |
| query := fmt.Sprintf("deps(%s) intersect %s", targetA, targetB) |
| out, err := bazel_testing.BazelOutput(append( |
| []string{ |
| "cquery", |
| "--transitions=full", |
| "--output=jsonproto", |
| query, |
| }, |
| flags..., |
| )..., |
| ) |
| if err != nil { |
| t.Fatalf("bazel cquery '%s': %v", query, err) |
| } |
| cqueryOut := bytes.TrimSpace(out) |
| configHashes := extractConfigHashes(t, cqueryOut) |
| if len(configHashes) != 1 { |
| differingGoOptions := getGoOptions(t, configHashes...) |
| if len(differingGoOptions) != 0 { |
| t.Fatalf( |
| "%s depends on %s in multiple configs with these differences in rules_go options: %s", |
| targetA, |
| targetB, |
| strings.Join(differingGoOptions, "\n"), |
| ) |
| } |
| } |
| goOptions := getGoOptions(t, configHashes[0]) |
| if len(goOptions) != 0 { |
| t.Fatalf( |
| "%s depends on %s in a config with rules_go options: %s", |
| targetA, |
| targetB, |
| strings.Join(goOptions, "\n"), |
| ) |
| } |
| } |
| |
| func extractConfigHashes(t *testing.T, rawJsonOut []byte) []string { |
| var jsonOut bazelCqueryOutput |
| err := json.Unmarshal(rawJsonOut, &jsonOut) |
| if err != nil { |
| t.Fatalf("Failed to decode bazel config JSON output %v: %q", err, string(rawJsonOut)) |
| } |
| var hashes []string |
| for _, result := range jsonOut.Results { |
| hashes = append(hashes, result.Configuration.Checksum) |
| } |
| return hashes |
| } |
| |
| func getGoOptions(t *testing.T, hashes ...string) []string { |
| out, err := bazel_testing.BazelOutput(append([]string{"config", "--output=json"}, hashes...)...) |
| if err != nil { |
| t.Fatalf("bazel config %s: %v", strings.Join(hashes, " "), err) |
| } |
| rawJsonOut := bytes.TrimSpace(out) |
| var jsonOut bazelConfigOutput |
| err = json.Unmarshal(rawJsonOut, &jsonOut) |
| if err != nil { |
| t.Fatalf("Failed to decode bazel config JSON output %v: %q", err, string(rawJsonOut)) |
| } |
| var differingGoOptions []string |
| for _, fragment := range jsonOut.Fragments { |
| if fragment.Name != starlarkOptionsFragment { |
| continue |
| } |
| for key, value := range fragment.Options { |
| if strings.HasPrefix(key, "@io_bazel_rules_go//") { |
| differingGoOptions = append(differingGoOptions, fmt.Sprintf("%s=%s", key, value)) |
| } |
| } |
| } |
| return differingGoOptions |
| } |
| |
| const starlarkOptionsFragment = "user-defined" |
| |
| type bazelConfigOutput struct { |
| Fragments []struct { |
| Name string `json:"name"` |
| Options map[string]string `json:"options"` |
| } `json:"fragmentOptions"` |
| } |
| |
| type bazelCqueryOutput struct { |
| Results []struct { |
| Configuration struct { |
| Checksum string `json:"checksum"` |
| } `json:"configuration"` |
| } `json:"results"` |
| } |