├── .bazeliskrc ├── .bazelrc ├── .github └── workflows │ └── bazel.yml ├── .gitignore ├── BUILD.bazel ├── LICENSE ├── README.md ├── WORKSPACE ├── gomock.bzl └── tests ├── BUILD.bazel ├── fake_copyright.txt ├── gomock_test.go └── hello ├── BUILD.bazel └── hello.go /.bazeliskrc: -------------------------------------------------------------------------------- 1 | 3.4.1 -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | test --test_output=errors 2 | -------------------------------------------------------------------------------- /.github/workflows/bazel.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | name: Bazel 10 | jobs: 11 | checks: 12 | name: Tests 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Mount bazel cache 18 | uses: actions/cache@v1 19 | with: 20 | path: "/home/runner/.cache/bazel" 21 | key: bazel 22 | 23 | - uses: actions/setup-go@v2 24 | with: 25 | go-version: '^1.14.1' # The Go version to download (if necessary) and use. 26 | 27 | - run: go get github.com/bazelbuild/bazelisk 28 | 29 | - run: bazelisk test //tests/... -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-bazel_gomock 2 | bazel-bin 3 | bazel-genfiles 4 | bazel-out 5 | bazel-testlogs 6 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@bazel_gazelle//:def.bzl", "gazelle") 2 | 3 | # gazelle:prefix github.com/jmhodges/bazel_gomock 4 | gazelle(name = "gazelle") 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2018 Jeff Hodges 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the “Software”), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project is deprecated and a newer version is supported by the rules_go 2 | project itself. You can now load the `gomock` macro with: 3 | 4 | 5 | ```skylark 6 | load("@io_bazel_rules_go//go:def.bzl", "gomock") 7 | ``` 8 | 9 | However, some of the ways import paths are handled differ, so the docs below 10 | might not exactly match behavior. (There's a ticket for adding some docs in 11 | https://github.com/bazelbuild/rules_go/issues/3721) 12 | 13 | gomock for Bazel 14 | ================ 15 | 16 | This skylark code allows you to generate code with `mockgen` (from 17 | [`golang/mock`](https://github.com/golang/mock)) and use that code as a dependency in 18 | your bazel projects. It handles all the `GOPATH` stuff for you. 19 | 20 | 21 | Setup 22 | --- 23 | 24 | `bazel_gomock` requires a `rules_go` external to be set up in your `WORKSPACE` 25 | as well as a `go_repository` call for `com_github_golang_mock`. 26 | 27 | Then in your `WORKSPACE`, add 28 | 29 | ```python 30 | # This commit is tagged as v1.3 31 | bazel_gomock_commit = "fde78c91cf1783cc1e33ba278922ba67a6ee2a84" 32 | http_archive( 33 | name = "bazel_gomock", 34 | sha256 = "692421b0c5e04ae4bc0bfff42fb1ce8671fe68daee2b8d8ea94657bb1fcddc0a", 35 | strip_prefix = "bazel_gomock-{v}".format(v = bazel_gomock_commit), 36 | urls = [ 37 | "https://github.com/jmhodges/bazel_gomock/archive/{v}.tar.gz".format(v = bazel_gomock_commit), 38 | ], 39 | ) 40 | ``` 41 | 42 | An example of a `com_github_golang_mock` you'd need: 43 | 44 | ```python 45 | go_repository( 46 | name = "com_github_golang_mock", 47 | importpath = "github.com/golang/mock", 48 | sum = "h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=", 49 | version = "v1.4.4", 50 | ) 51 | ``` 52 | 53 | Use 54 | --- 55 | 56 | Once your `WORKSPACE` is set up, you can call `gomock` in your BUILD files like: 57 | 58 | ```python 59 | load("@bazel_gomock//:gomock.bzl", "gomock") 60 | 61 | gomock( 62 | name = "mock_sess", 63 | out = "mock_sess_test.go", 64 | interfaces = ["SessionsClient"], 65 | library = "//proto/sessions/sessproto:go_default_library", 66 | package = "main", 67 | ) 68 | ``` 69 | 70 | where `library` is a `go_library` target, `interfaces` is the list of names of 71 | the Go interfaces you'd like `mockgen` to generate mocks of, `package` is the 72 | name of the Go package at the top of the generated file (in this example, 73 | `package "main"`), and `out` is the path of generated source file that will be 74 | made. 75 | 76 | There is also a `source` parameter described below. 77 | 78 | You use this target's `out` file directly in the `srcs` parameter in `go_test`, 79 | `go_library`, and so on. So, when the above example `gomock` call is used in the 80 | same BUILD file, you put `mock_sess_test.go` in the `srcs` parameter like so: 81 | 82 | 83 | ```python 84 | go_test( 85 | name = "go_default_test", 86 | srcs = [ 87 | "cool_test.go", 88 | "mock_sess_test.go", 89 | ], 90 | embed = [":go_default_library"] 91 | ... 92 | ) 93 | ``` 94 | 95 | If you need to generate mocks from a specific Go file instead of a 96 | import path (say, because the `go_library` you have is a `main` package and is 97 | therefore unreflectable by Go tools and specifically unimportable by `mockgen`), 98 | add the `source` parameter with the location of source file. E.g. `source = 99 | "//fancy/path:foo.go"` or just `source = "foo.go"` if the file is in the same 100 | directory). The `library` parameter must still be set to the library that source 101 | file lives in so that any referenced dependencies can be pulled into the Go 102 | path. 103 | 104 | Also, `gazelle` will remove the generated source file from a `go_test` target's 105 | `srcs` unless you end the generated file name with `_test.go`. 106 | 107 | As a likely unused feature, you can pass in an alternative 108 | external for where to find the `mockgen` tool target using the `mockgen_tool` 109 | parameter. The only rule for the target is that must be a binary. The current 110 | default is `"@com_github_golang_mock//mockgen"`. 111 | 112 | If you try to use `gomock` on a `go_library` that is in the package `main` (and so 113 | probably being immediately used as an `embed` target for a `go_binary`), you'll 114 | get an annoying error like: 115 | 116 | ``` 117 | prog.go:13:2: import "your/main/package/deal" is a program, not an importable package 118 | ``` 119 | 120 | You can resolve that by setting the `source` parameter to the location of the 121 | file with the interfaces you want in it. 122 | 123 | ## `gomock` arguments: 124 | 125 | | Name | Default value | Type | Documentation | 126 | |------|---------------|------|---------------| 127 | | name | | string | The name of the target. (Required.) | 128 | | library| | Label | The go_library to find the interfaces in. (Required.) | 129 | | interfaces | | list of string | The names of interfaces in `library` to generate mocks for. (Required if `source` is not set, and ignored if `source` is set.) | 130 | | source | | string | Prefer using `library` only, instead of using this argument. The Go source file to generate interfaces from. If this is set, `interfaces` is ignored because `mockgen` will always generate code for all interfaces. See the gomock documentation on `-source` for more information. | 131 | | out | | string | The file name to give the generated output. (Required.) | 132 | | package | | string | The package name to use in the generated output. See the gomock documentation on `-package` for more information. | 133 | | imports | | string\_dict | Dictionary of keys of package names and values of import paths to use the keys as the identifier to use when the generated output uses the given import path. See the gomock documentation on `-imports` for more information. | 134 | | self\_package | | string | The full import path for the generated code. See the gomock documentation on `-self_package` for more information. | 135 | | mock\_names | | string\_dict | Dictionary of interface name to mock name pairs to change the output names of the mock objects. Mock names default to 'Mock' prepended to the name of the interface. See the gomock documentation on `-mock_names` for more information. | 136 | | copyright\_file | | Label | The file containing the copyright to prepend to the generated output. See the gomock documentation on `-copyright_file` for more information. | 137 | | aux\_files | | string\_list\_dict | A map from packages to auxilliary Go source files to load for those packages. Currently, assumes that the file (the value) is a path relative to the directory of `library` in the GOPATH. See the gomock documentation on `-aux_files` for more information. | 138 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "com_github_jmhodges_bazel_gomock") 2 | 3 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 4 | 5 | http_archive( 6 | name = "io_bazel_rules_go", 7 | sha256 = "6f111c57fd50baf5b8ee9d63024874dd2a014b069426156c55adbf6d3d22cb7b", 8 | urls = [ 9 | "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.25.0/rules_go-v0.25.0.tar.gz", 10 | "https://github.com/bazelbuild/rules_go/releases/download/v0.25.0/rules_go-v0.25.0.tar.gz", 11 | ], 12 | ) 13 | 14 | load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") 15 | 16 | go_rules_dependencies() 17 | 18 | go_register_toolchains(version = "1.15.5") 19 | 20 | http_archive( 21 | name = "bazel_gazelle", 22 | sha256 = "b85f48fa105c4403326e9525ad2b2cc437babaa6e15a3fc0b1dbab0ab064bc7c", 23 | urls = [ 24 | "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.22.2/bazel-gazelle-v0.22.2.tar.gz", 25 | "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.22.2/bazel-gazelle-v0.22.2.tar.gz", 26 | ], 27 | ) 28 | 29 | load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") 30 | 31 | gazelle_dependencies() 32 | 33 | go_repository( 34 | name = "com_github_golang_mock", 35 | importpath = "github.com/golang/mock", 36 | sum = "h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=", 37 | version = "v1.4.4", 38 | ) 39 | 40 | go_repository( 41 | name = "com_github_google_go_cmp", 42 | importpath = "github.com/google/go-cmp", 43 | sum = "h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=", 44 | version = "v0.3.1", 45 | ) 46 | 47 | go_repository( 48 | name = "org_golang_x_mod", 49 | importpath = "golang.org/x/mod", 50 | sum = "h1:xUIPaMhvROX9dhPvRCenIJtU78+lbEenGbgqB5hfHCQ=", 51 | version = "v0.3.1-0.20200828183125-ce943fd02449", 52 | ) 53 | -------------------------------------------------------------------------------- /gomock.bzl: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_context", "go_path") 2 | load("@io_bazel_rules_go//go/private:providers.bzl", "GoLibrary", "GoPath") 3 | 4 | _MOCKGEN_TOOL = "@com_github_golang_mock//mockgen" 5 | _MOCKGEN_MODEL_LIB = "@com_github_golang_mock//mockgen/model:go_default_library" 6 | 7 | def _gomock_source_impl(ctx): 8 | go_ctx = go_context(ctx) 9 | gopath = "$(pwd)/" + ctx.bin_dir.path + "/" + ctx.attr.gopath_dep[GoPath].gopath 10 | 11 | # passed in source needs to be in gopath to not trigger module mode 12 | args = ["-source", gopath + "/src/" + ctx.attr.library[GoLibrary].importmap + "/"+ ctx.file.source.basename] 13 | 14 | args, needed_files = _handle_shared_args(ctx, args) 15 | 16 | if len(ctx.attr.aux_files) > 0: 17 | aux_files = [] 18 | for pkg, files in ctx.attr.aux_files.items(): 19 | for f in files: 20 | mapped_f = gopath + "/src/" + ctx.attr.library[GoLibrary].importmap + "/"+ f 21 | aux_files.append("{0}={1}".format(pkg, mapped_f)) 22 | args += ["-aux_files", ",".join(aux_files)] 23 | 24 | inputs = ( 25 | ctx.attr.gopath_dep.files.to_list() + needed_files + 26 | go_ctx.sdk.headers + go_ctx.sdk.srcs + go_ctx.sdk.tools 27 | ) + [ctx.file.source] 28 | 29 | # We can use the go binary from the stdlib for most of the environment 30 | # variables, but our GOPATH is specific to the library target we were given. 31 | ctx.actions.run_shell( 32 | outputs = [ctx.outputs.out], 33 | inputs = inputs, 34 | tools = [ 35 | ctx.file.mockgen_tool, 36 | go_ctx.go, 37 | ], 38 | command = """ 39 | source <($PWD/{godir}/go env) && 40 | export PATH=$GOROOT/bin:$PWD/{godir}:$PATH && 41 | export GOPATH={gopath} && 42 | mkdir -p .gocache && 43 | export GOCACHE=$PWD/.gocache && 44 | {cmd} {args} > {out} 45 | """.format( 46 | godir = go_ctx.go.path[:-1 - len(go_ctx.go.basename)], 47 | gopath = gopath, 48 | cmd = "$(pwd)/" + ctx.file.mockgen_tool.path, 49 | args = " ".join(args), 50 | out = ctx.outputs.out.path, 51 | mnemonic = "GoMockSourceGen", 52 | ), 53 | env = { 54 | "GO111MODULE": "off", # explicitly relying on passed in go_path to not download modules while doing codegen 55 | }, 56 | ) 57 | 58 | _gomock_source = rule( 59 | _gomock_source_impl, 60 | attrs = { 61 | "library": attr.label( 62 | doc = "The target the Go library is at to look for the interfaces in. When this is set and source is not set, mockgen will use its reflect code to generate the mocks. If source is set, its dependencies will be included in the GOPATH that mockgen will be run in.", 63 | providers = [GoLibrary], 64 | mandatory = True, 65 | ), 66 | "source": attr.label( 67 | doc = "A Go source file to find all the interfaces to generate mocks for. See also the docs for library.", 68 | mandatory = False, 69 | allow_single_file = True, 70 | ), 71 | "out": attr.output( 72 | doc = "The new Go file to emit the generated mocks into", 73 | mandatory = True, 74 | ), 75 | "interfaces": attr.string_list( 76 | allow_empty = False, 77 | doc = "Ignored. If `source` is not set, this would be the list of Go interfaces to generate mocks for.", 78 | mandatory = True, 79 | ), 80 | "aux_files": attr.string_list_dict( 81 | default = {}, 82 | doc = "A map from packages to auxilliary Go source files to load for those packages.", 83 | ), 84 | "package": attr.string( 85 | doc = "The name of the package the generated mocks should be in. If not specified, uses mockgen's default.", 86 | ), 87 | "self_package": attr.string( 88 | doc = "The full package import path for the generated code. The purpose of this flag is to prevent import cycles in the generated code by trying to include its own package. This can happen if the mock's package is set to one of its inputs (usually the main one) and the output is stdio so mockgen cannot detect the final output package. Setting this flag will then tell mockgen which import to exclude.", 89 | ), 90 | "imports": attr.string_dict( 91 | doc = "Dictionary of name-path pairs of explicit imports to use.", 92 | ), 93 | "mock_names": attr.string_dict( 94 | doc = "Dictionary of interface name to mock name pairs to change the output names of the mock objects. Mock names default to 'Mock' prepended to the name of the interface.", 95 | default = {}, 96 | ), 97 | "copyright_file": attr.label( 98 | doc = "Optional file containing copyright to prepend to the generated contents.", 99 | allow_single_file = True, 100 | mandatory = False, 101 | ), 102 | "gopath_dep": attr.label( 103 | doc = "The go_path label to use to create the GOPATH for the given library. Will be set correctly by the gomock macro, so you don't need to set it.", 104 | providers = [GoPath], 105 | mandatory = False, 106 | ), 107 | "mockgen_tool": attr.label( 108 | doc = "The mockgen tool to run", 109 | default = Label(_MOCKGEN_TOOL), 110 | allow_single_file = True, 111 | executable = True, 112 | cfg = "exec", 113 | mandatory = False, 114 | ), 115 | "_go_context_data": attr.label( 116 | default = "@io_bazel_rules_go//:go_context_data", 117 | ), 118 | }, 119 | toolchains = ["@io_bazel_rules_go//go:toolchain"], 120 | ) 121 | 122 | def gomock(name, library, out, **kwargs): 123 | mockgen_tool = _MOCKGEN_TOOL 124 | if kwargs.get("mockgen_tool", None): 125 | mockgen_tool = kwargs["mockgen_tool"] 126 | 127 | if kwargs.get("source", None): 128 | gopath_name = name + "_gomock_gopath" 129 | go_path( 130 | name = gopath_name, 131 | deps = [library, mockgen_tool], 132 | ) 133 | _gomock_source( 134 | name = name, 135 | library = library, 136 | gopath_dep = gopath_name, 137 | out = out, 138 | **kwargs) 139 | else: 140 | _gomock_reflect( 141 | name = name, 142 | library = library, 143 | out = out, 144 | mockgen_tool = mockgen_tool, 145 | **kwargs) 146 | 147 | def _gomock_reflect(name, library, out, mockgen_tool, **kwargs): 148 | interfaces = kwargs.pop("interfaces", None) 149 | 150 | mockgen_model_lib = _MOCKGEN_MODEL_LIB 151 | if kwargs.get("mockgen_model_library", None): 152 | mockgen_model_lib = kwargs["mockgen_model_library"] 153 | 154 | prog_src = name + "_gomock_prog" 155 | prog_src_out = prog_src + ".go" 156 | _gomock_prog_gen( 157 | name = prog_src, 158 | interfaces = interfaces, 159 | library = library, 160 | out = prog_src_out, 161 | mockgen_tool = mockgen_tool, 162 | ) 163 | prog_bin = name + "_gomock_prog_bin" 164 | go_binary( 165 | name = prog_bin, 166 | srcs = [prog_src_out], 167 | deps = [library, mockgen_model_lib], 168 | ) 169 | _gomock_prog_exec( 170 | name = name, 171 | interfaces = interfaces, 172 | library = library, 173 | out = out, 174 | prog_bin = prog_bin, 175 | mockgen_tool = mockgen_tool, 176 | **kwargs) 177 | 178 | def _gomock_prog_gen_impl(ctx): 179 | args = ["-prog_only"] 180 | args += [ctx.attr.library[GoLibrary].importpath] 181 | args += [",".join(ctx.attr.interfaces)] 182 | 183 | cmd = ctx.file.mockgen_tool 184 | out = ctx.outputs.out 185 | ctx.actions.run_shell( 186 | outputs = [out], 187 | tools = [cmd], 188 | command = """ 189 | {cmd} {args} > {out} 190 | """.format( 191 | cmd = "$(pwd)/" + cmd.path, 192 | args = " ".join(args), 193 | out = out.path, 194 | ), 195 | mnemonic = "GoMockReflectProgOnlyGen" 196 | ) 197 | 198 | _gomock_prog_gen = rule( 199 | _gomock_prog_gen_impl, 200 | attrs = { 201 | "library": attr.label( 202 | doc = "The target the Go library is at to look for the interfaces in. When this is set and source is not set, mockgen will use its reflect code to generate the mocks. If source is set, its dependencies will be included in the GOPATH that mockgen will be run in.", 203 | providers = [GoLibrary], 204 | mandatory = True, 205 | ), 206 | "out": attr.output( 207 | doc = "The new Go source file put the mock generator code", 208 | mandatory = True, 209 | ), 210 | "interfaces": attr.string_list( 211 | allow_empty = False, 212 | doc = "The names of the Go interfaces to generate mocks for. If not set, all of the interfaces in the library or source file will have mocks generated for them.", 213 | mandatory = True, 214 | ), 215 | "mockgen_tool": attr.label( 216 | doc = "The mockgen tool to run", 217 | default = Label(_MOCKGEN_TOOL), 218 | allow_single_file = True, 219 | executable = True, 220 | cfg = "exec", 221 | mandatory = False, 222 | ), 223 | "_go_context_data": attr.label( 224 | default = "@io_bazel_rules_go//:go_context_data", 225 | ), 226 | }, 227 | toolchains = ["@io_bazel_rules_go//go:toolchain"], 228 | ) 229 | 230 | def _gomock_prog_exec_impl(ctx): 231 | args = ["-exec_only", ctx.file.prog_bin.path] 232 | args, needed_files = _handle_shared_args(ctx, args) 233 | 234 | # annoyingly, the interfaces join has to go after the importpath so we can't 235 | # share those. 236 | args += [ctx.attr.library[GoLibrary].importpath] 237 | args += [",".join(ctx.attr.interfaces)] 238 | 239 | ctx.actions.run_shell( 240 | outputs = [ctx.outputs.out], 241 | inputs = [ctx.file.prog_bin] + needed_files, 242 | tools = [ctx.file.mockgen_tool], 243 | command = """{cmd} {args} > {out}""".format( 244 | cmd = "$(pwd)/" + ctx.file.mockgen_tool.path, 245 | args = " ".join(args), 246 | out = ctx.outputs.out.path, 247 | ), 248 | env = { 249 | # GOCACHE is required starting in Go 1.12 250 | "GOCACHE": "./.gocache", 251 | }, 252 | mnemonic = "GoMockReflectExecOnlyGen", 253 | ) 254 | 255 | _gomock_prog_exec = rule( 256 | _gomock_prog_exec_impl, 257 | attrs = { 258 | "library": attr.label( 259 | doc = "The target the Go library is at to look for the interfaces in. When this is set and source is not set, mockgen will use its reflect code to generate the mocks. If source is set, its dependencies will be included in the GOPATH that mockgen will be run in.", 260 | providers = [GoLibrary], 261 | mandatory = True, 262 | ), 263 | "out": attr.output( 264 | doc = "The new Go source file to put the generated mock code", 265 | mandatory = True, 266 | ), 267 | "interfaces": attr.string_list( 268 | allow_empty = False, 269 | doc = "The names of the Go interfaces to generate mocks for. If not set, all of the interfaces in the library or source file will have mocks generated for them.", 270 | mandatory = True, 271 | ), 272 | "package": attr.string( 273 | doc = "The name of the package the generated mocks should be in. If not specified, uses mockgen's default.", 274 | ), 275 | "self_package": attr.string( 276 | doc = "The full package import path for the generated code. The purpose of this flag is to prevent import cycles in the generated code by trying to include its own package. This can happen if the mock's package is set to one of its inputs (usually the main one) and the output is stdio so mockgen cannot detect the final output package. Setting this flag will then tell mockgen which import to exclude.", 277 | ), 278 | "imports": attr.string_dict( 279 | doc = "Dictionary of name-path pairs of explicit imports to use.", 280 | ), 281 | "mock_names": attr.string_dict( 282 | doc = "Dictionary of interfaceName-mockName pairs of explicit mock names to use. Mock names default to 'Mock'+ interfaceName suffix.", 283 | default = {}, 284 | ), 285 | "copyright_file": attr.label( 286 | doc = "Optional file containing copyright to prepend to the generated contents.", 287 | allow_single_file = True, 288 | mandatory = False, 289 | ), 290 | "prog_bin": attr.label( 291 | doc = "The program binary generated by mockgen's -prog_only and compiled by bazel.", 292 | allow_single_file = True, 293 | executable = True, 294 | cfg = "exec", 295 | mandatory = True, 296 | ), 297 | "mockgen_tool": attr.label( 298 | doc = "The mockgen tool to run", 299 | default = Label(_MOCKGEN_TOOL), 300 | allow_single_file = True, 301 | executable = True, 302 | cfg = "exec", 303 | mandatory = False, 304 | ), 305 | "_go_context_data": attr.label( 306 | default = "@io_bazel_rules_go//:go_context_data", 307 | ), 308 | }, 309 | toolchains = ["@io_bazel_rules_go//go:toolchain"], 310 | ) 311 | 312 | def _handle_shared_args(ctx, args): 313 | needed_files = [] 314 | 315 | if ctx.attr.package != "": 316 | args += ["-package", ctx.attr.package] 317 | if ctx.attr.self_package != "": 318 | args += ["-self_package", ctx.attr.self_package] 319 | if len(ctx.attr.imports) > 0: 320 | imports = ",".join(["{0}={1}".format(name, pkg) for name, pkg in ctx.attr.imports.items()]) 321 | args += ["-imports", imports] 322 | if ctx.file.copyright_file != None: 323 | args += ["-copyright_file", ctx.file.copyright_file.path] 324 | needed_files.append(ctx.file.copyright_file) 325 | if len(ctx.attr.mock_names) > 0: 326 | mock_names = ",".join(["{0}={1}".format(name, pkg) for name, pkg in ctx.attr.mock_names.items()]) 327 | args += ["-mock_names", mock_names] 328 | 329 | return args, needed_files 330 | -------------------------------------------------------------------------------- /tests/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("//:gomock.bzl", "gomock") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") 3 | 4 | # keep 5 | gomock( 6 | name = "helloer_reflect_mock", 7 | interfaces = ["Helloer"], 8 | out = "hello_mock.go", 9 | package = "main", 10 | library = "//tests/hello:hello", 11 | ) 12 | 13 | # keep 14 | gomock( 15 | name = "helloer_mock_with_copyright", 16 | interfaces = ["Helloer"], 17 | out = "helloer_copyright.go", 18 | package = "main", 19 | library = "//tests/hello:hello", 20 | copyright_file = "fake_copyright.txt", 21 | ) 22 | 23 | # keep 24 | gomock( 25 | name = "helloer_source_mock", 26 | interfaces = ["Helloer"], 27 | out = "helloer_source_mock.go", 28 | mock_names = {"Helloer": "MockSourceHelloer"}, 29 | package = "main", 30 | library = "//tests/hello:hello", 31 | ) 32 | 33 | # keep 34 | gomock( 35 | name = "helloer_reflect_mock_with_mock_names", 36 | interfaces = ["Helloer"], 37 | out = "hello_mock_renamed.go", 38 | package = "main", 39 | library = "//tests/hello:hello", 40 | mock_names = {"Helloer": "MockRenamedReflectHelloer"}, 41 | ) 42 | 43 | # keep 44 | go_test( 45 | name = "go_default_test", 46 | srcs = [ 47 | "gomock_test.go", 48 | ":helloer_reflect_mock", 49 | ":helloer_source_mock", 50 | ":helloer_reflect_mock_with_mock_names", 51 | ], 52 | args = [ 53 | "-srcWithCopyright=$(location :helloer_mock_with_copyright)", 54 | "-copyright=$(location fake_copyright.txt)", 55 | ], 56 | data = [ 57 | ":helloer_mock_with_copyright", 58 | "fake_copyright.txt", 59 | ], 60 | deps = [ 61 | "@com_github_golang_mock//gomock:go_default_library", 62 | "@io_bazel_rules_go//go/tools/bazel:go_default_library", 63 | "@com_github_google_go_cmp//cmp:go_default_library", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /tests/fake_copyright.txt: -------------------------------------------------------------------------------- 1 | A fancy non-copyright 2 | It's nice, though. 3 | -------------------------------------------------------------------------------- /tests/gomock_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "io/ioutil" 7 | "testing" 8 | 9 | "github.com/bazelbuild/rules_go/go/tools/bazel" 10 | "github.com/golang/mock/gomock" 11 | "github.com/google/go-cmp/cmp" 12 | ) 13 | 14 | var ( 15 | srcWithCopyright = flag.String("srcWithCopyright", "", "generated gomock code with copyright prefix") 16 | copyrightFile = flag.String("copyright", "", "file with contents of the prefix we should see in srcWithCopyright's file") 17 | ) 18 | 19 | func TestGoldenPath(t *testing.T) { 20 | ctrl := gomock.NewController(t) 21 | m := NewMockHelloer(ctrl) 22 | m.EXPECT().Hello().Return("hey") 23 | m.Hello() 24 | 25 | m2 := NewMockSourceHelloer(ctrl) 26 | m2.EXPECT().Hello().Return("hey") 27 | m2.Hello() 28 | 29 | m3 := NewMockRenamedReflectHelloer(ctrl) 30 | m3.EXPECT().Hello().Return("hey") 31 | m3.Hello() 32 | 33 | defer ctrl.Finish() 34 | } 35 | 36 | func TestCopyright(t *testing.T) { 37 | cf, err := bazel.Runfile(*copyrightFile) 38 | if err != nil { 39 | t.Fatalf("copyrightFile %#v wasn't found in go_test's data arg", *copyrightFile) 40 | } 41 | copyrightPrefix, err := ioutil.ReadFile(cf) 42 | if err != nil { 43 | t.Fatalf("copyrightFile ReadFile: %s", err) 44 | } 45 | scf, err := bazel.Runfile(*srcWithCopyright) 46 | if err != nil { 47 | t.Fatalf("srcWithCopyright file %#v wasn't found in go_test's data arg", *srcWithCopyright) 48 | } 49 | srcContents, err := ioutil.ReadFile(scf) 50 | if err != nil { 51 | t.Fatalf("srcWithCopyright ReadFile: %s", err) 52 | } 53 | 54 | lines := bytes.Split(copyrightPrefix, []byte{'\n'}) 55 | for i, line := range lines { 56 | if len(line) == 0 { 57 | continue 58 | } 59 | lines[i] = append([]byte("// "), line...) 60 | } 61 | commentedPrefix := bytes.Join(lines, []byte{'\n'}) 62 | if !bytes.HasPrefix(srcContents, commentedPrefix) { 63 | expected := append(commentedPrefix, srcContents...) 64 | t.Fatalf("generated mock code file %#v did not start with copyright contents of %#v", *srcWithCopyright, *copyrightFile, cmp.Diff(srcContents, expected)) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/hello/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "hello", 5 | srcs = ["hello.go"], 6 | importpath = "github.com/jmhodges/bazel_gomock/tests/hello", 7 | visibility = ["//visibility:public"], 8 | ) 9 | -------------------------------------------------------------------------------- /tests/hello/hello.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | type Helloer interface { 4 | Hello() string 5 | } 6 | --------------------------------------------------------------------------------