Skip to content

Commit 1dded17

Browse files
committed
Support proto_compiled_sources with ts_project
1 parent 3b147a8 commit 1dded17

11 files changed

+225
-116
lines changed

cmd/gencopy/gencopy.bash.in

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ CONFIG_SHORT_PATH=@@CONFIG_SHORT_PATH@@
1111

1212
# env
1313
# set -x
14+
# echo "PWD: $PWD"
1415
# find .
16+
# find ..
17+
18+
# Get the directory of the current script (part of the runfiles tree)
19+
script_dir=$(dirname "$0")
1520

1621
# find_runfile prints the location of a runfile in the source workspace,
1722
# either by reading the symbolic link or reading the runfiles manifest.
@@ -45,18 +50,18 @@ fi
4550
# Note that we don't change directories first; if we did, Generator wouldn't be
4651
# able to find runfiles, and some extensions rely on that. Generator can use
4752
# BUILD_WORKSPACE_DIRECTORY to interpret relative paths on the command line.
48-
GENCOPY_short_path=$(find_runfile "$GENCOPY_SHORT_PATH")
49-
if [ -z "$GENCOPY_short_path" ]; then
53+
GENCOPY_path=$(find_runfile "$GENCOPY_SHORT_PATH")
54+
if [ -z "$GENCOPY_path" ]; then
5055
echo "error: could not locate gencopy binary" >&2
5156
exit 1
5257
fi
5358

54-
CONFIG_short_path=$(find_runfile "$CONFIG_SHORT_PATH")
55-
if [ -z "$CONFIG_short_path" ]; then
59+
CONFIG_path=$(find_runfile "$CONFIG_SHORT_PATH")
60+
if [ -z "$CONFIG_path" ]; then
5661
echo "error: could not locate gencopy configuration file" >&2
5762
exit 1
5863
fi
5964

60-
"$GENCOPY_short_path" \
61-
-config="$CONFIG_short_path" \
65+
"$GENCOPY_path" \
66+
-config="$CONFIG_path" \
6267
-workspace_root_directory="${BUILD_WORKSPACE_DIRECTORY:-""}"

cmd/gencopy/gencopy.bzl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ gencopy_attrs = {
1919
doc = "The label.name used to regenerate targets",
2020
mandatory = True,
2121
),
22+
"extension": attr.string(
23+
doc = "optional file extension to add to the copied file",
24+
mandatory = False,
25+
),
2226
"_gencopy_script": attr.label(
2327
doc = "The gencopy script template",
2428
default = str(Label("//cmd/gencopy:gencopy.bash.in")),
@@ -43,7 +47,7 @@ def gencopy_config(ctx):
4347
)
4448

4549
def gencopy_action(ctx, config, runfiles):
46-
"""gencopy_action declared a bazel action that runs the gencopy.bash script.
50+
"""gencopy_action declares a bazel action that runs the gencopy.bash script.
4751
4852
Args:
4953
ctx: the context object.

cmd/gencopy/gencopy.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"os"
1212
"path/filepath"
1313
"strconv"
14-
"strings"
1514

1615
"github.com/google/go-cmp/cmp"
1716
)
@@ -174,7 +173,6 @@ func makePkgSrcDstPairs(cfg *Config, pkg *PackageConfig) []*SrcDst {
174173

175174
func makePkgSrcDstPair(cfg *Config, pkg *PackageConfig, src, dst string) *SrcDst {
176175
if pkg.TargetWorkspaceRoot != "" {
177-
src = filepath.Join("external", strings.TrimPrefix(src, ".."))
178176
dst = filepath.Join(pkg.TargetWorkspaceRoot, dst)
179177
}
180178
dst = filepath.Join(cfg.WorkspaceRootDirectory, dst)

cmd/gencopy/gencopy_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func TestMakePkgSrcDstPair(t *testing.T) {
6868
pkg: PackageConfig{TargetWorkspaceRoot: "external/foo"},
6969
src: "../foo/file.txt",
7070
dst: "file.txt",
71-
want: SrcDst{Src: "external/foo/file.txt", Dst: "/home/external/foo/file.txt"},
71+
want: SrcDst{Src: "../foo/file.txt", Dst: "/home/external/foo/file.txt"},
7272
},
7373
} {
7474
t.Run(name, func(t *testing.T) {

pkg/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ filegroup(
1515
"//pkg/plugin/grpc/grpcweb:all_files",
1616
"//pkg/plugin/grpcecosystem/grpcgateway:all_files",
1717
"//pkg/plugin/scalapb/scalapb:all_files",
18+
"//pkg/plugin/scalapb/zio_grpc:all_files",
1819
"//pkg/plugin/stackb/grpc_js:all_files",
1920
"//pkg/plugin/stephenh/ts-proto:all_files",
2021
"//pkg/plugintest:all_files",

pkg/protoc/proto_compiled_sources.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ func (s *protoCompiledSources) LoadInfo() rule.LoadInfo {
4444

4545
// ProvideRule implements part of the LanguageRule interface.
4646
func (s *protoCompiledSources) ProvideRule(cfg *LanguageRuleConfig, config *ProtocConfiguration) RuleProvider {
47+
if len(config.Outputs) == 0 {
48+
return nil
49+
}
4750
return &protoCompileRule{
4851
kind: "proto_compiled_sources",
4952
nameSuffix: "compiled_sources",

rules/private/proto_repository_tools_srcs.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ PROTO_REPOSITORY_TOOLS_SRCS = [
100100
"@build_stack_rules_proto//pkg/plugin/grpcecosystem/grpcgateway:protoc-gen-grpc-gateway.go",
101101
"@build_stack_rules_proto//pkg/plugin/scalapb/scalapb:BUILD.bazel",
102102
"@build_stack_rules_proto//pkg/plugin/scalapb/scalapb:protoc_gen_scala.go",
103+
"@build_stack_rules_proto//pkg/plugin/scalapb/zio_grpc:BUILD.bazel",
104+
"@build_stack_rules_proto//pkg/plugin/scalapb/zio_grpc:protoc_gen_zio_grpc.go",
103105
"@build_stack_rules_proto//pkg/plugin/stackb/grpc_js:BUILD.bazel",
104106
"@build_stack_rules_proto//pkg/plugin/stackb/grpc_js:protoc-gen-grpc-js.go",
105107
"@build_stack_rules_proto//pkg/plugin/stephenh/ts-proto:BUILD.bazel",
@@ -179,6 +181,7 @@ PROTO_REPOSITORY_TOOLS_SRCS = [
179181
"@build_stack_rules_proto//plugin/grpc/grpc-web:BUILD.bazel",
180182
"@build_stack_rules_proto//plugin/grpc-ecosystem/grpc-gateway:BUILD.bazel",
181183
"@build_stack_rules_proto//plugin/scalapb/scalapb:BUILD.bazel",
184+
"@build_stack_rules_proto//plugin/scalapb/zio-grpc:BUILD.bazel",
182185
"@build_stack_rules_proto//plugin/stackb/grpc_js:BUILD.bazel",
183186
"@build_stack_rules_proto//plugin/stephenh/ts-proto:BUILD.bazel",
184187
"@build_stack_rules_proto//rules:BUILD.bazel",

rules/proto_compile.bzl

Lines changed: 108 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -94,22 +94,48 @@ def is_windows(ctx):
9494
return ctx.configuration.host_path_separator == ";"
9595

9696
def _proto_compile_impl(ctx):
97-
# mut <list<File>>
98-
outputs = [] + ctx.outputs.outputs
99-
100-
# mut <?string> If defined, we are using the srcs to predict the outputs
101-
# srcgen_ext = None
102-
if len(ctx.attr.srcs) > 0:
103-
if len(ctx.outputs.outputs) > 0:
104-
fail("rule must provide 'srcs' or 'outputs', but not both")
105-
106-
# srcgen_ext = ctx.attr.srcgen_ext
107-
outputs = [ctx.actions.declare_file(name) for name in ctx.attr.srcs]
108-
10997
###
11098
### Part 1: setup variables used in scope
11199
###
112100

101+
# out_dir is used in conjunction with file.short_path to determine root
102+
# output file paths
103+
out_dir = ctx.bin_dir.path
104+
if ctx.label.workspace_root:
105+
out_dir = "/".join([out_dir, ctx.label.workspace_root])
106+
107+
if len(ctx.attr.srcs) > 0 and len(ctx.outputs.outputs) > 0:
108+
fail("rule must provide 'srcs' or 'outputs' (but not both)")
109+
110+
# <dict<string,File>: output files mapped by their package-relative path.
111+
# This struct is given to the provider.
112+
output_files_by_rel_path = {}
113+
114+
# const <dict<string,string>. The key is the file basename, value is the
115+
# short_path of the output file.
116+
output_short_paths_by_basename = {}
117+
118+
# renames is a mapping from the output filename that was produced by the
119+
# plugin to the actual name we want to output.
120+
renames = {}
121+
122+
if len(ctx.attr.srcs):
123+
# assume filenames in srcs are already package-relative
124+
for name in ctx.attr.srcs:
125+
rel = "/".join([ctx.label.package, name])
126+
actual_name = name + ctx.attr.output_file_suffix
127+
if actual_name != name:
128+
renames[rel] = "/".join([ctx.label.package, actual_name])
129+
f = ctx.actions.declare_file(actual_name)
130+
output_files_by_rel_path[rel] = f
131+
output_short_paths_by_basename[name] = rel
132+
else:
133+
for f in ctx.outputs.outputs:
134+
# rel = _get_package_relative_path(ctx.label, f.short_path)
135+
rel = f.short_path
136+
output_files_by_rel_path[rel] = f
137+
output_short_paths_by_basename[f.basename] = rel
138+
113139
# const <bool> verbosity flag
114140
verbose = ctx.attr.verbose
115141

@@ -125,9 +151,6 @@ def _proto_compile_impl(ctx):
125151
# const <dict<string,string>>
126152
outs = {_plugin_label_key(Label(k)): v for k, v in ctx.attr.outs.items()}
127153

128-
# const <dict<string,File>. outputs indexed by basename.
129-
outputs_by_basename = {f.basename: f for f in outputs}
130-
131154
# mut <list<File>> set of descriptors for the compile action
132155
descriptors = proto_info.transitive_descriptor_sets.to_list()
133156

@@ -279,16 +302,16 @@ def _proto_compile_impl(ctx):
279302
# into place
280303
if len(ctx.attr.output_mappings) > 0:
281304
copy_commands = []
282-
out_dir = ctx.bin_dir.path
283-
if ctx.label.workspace_root:
284-
out_dir = "/".join([out_dir, ctx.label.workspace_root])
285305
for mapping in ctx.attr.output_mappings:
286306
basename, _, intermediate_filename = mapping.partition("=")
287-
intermediate_filename = "/".join([out_dir, intermediate_filename])
288-
output = outputs_by_basename.get(basename, None)
289-
if not output:
307+
output_short_path = output_short_paths_by_basename.get(basename)
308+
if not output_short_path:
290309
fail("the mapped file '%s' was not listed in outputs" % basename)
291-
copy_commands.append("cp '{}' '{}'".format(intermediate_filename, output.path))
310+
copy_commands.append("cp '{dir}/{src}' '{dir}/{dst}'".format(
311+
dir = out_dir,
312+
src = intermediate_filename,
313+
dst = output_short_path,
314+
))
292315
copy_script = ctx.actions.declare_file(ctx.label.name + "_copy.sh")
293316
ctx.actions.write(copy_script, "\n".join(copy_commands), is_executable = True)
294317
inputs.append(copy_script)
@@ -298,17 +321,53 @@ def _proto_compile_impl(ctx):
298321
if len(mods):
299322
mv_commands = []
300323
for suffix, action in mods.items():
301-
for f in outputs:
302-
if f.short_path.endswith(suffix):
303-
mv_commands.append("awk '%s' %s > %s.tmp" % (action, f.path, f.path))
304-
mv_commands.append("mv %s.tmp %s" % (f.path, f.path))
324+
for output_short_path in output_short_paths_by_basename.values():
325+
if output_short_path.endswith(suffix):
326+
mv_commands.append("awk '{action}' {dir}/{short_path} > {dir}/{short_path}.tmp".format(
327+
action = action,
328+
dir = out_dir,
329+
short_path = output_short_path,
330+
))
331+
mv_commands.append("mv {dir}/{short_path}.tmp {dir}/{short_path}".format(
332+
dir = out_dir,
333+
short_path = output_short_path,
334+
))
305335
mv_script = ctx.actions.declare_file(ctx.label.name + "_mv.sh")
306336
ctx.actions.write(mv_script, "\n".join(mv_commands), is_executable = True)
307337
inputs.append(mv_script)
308338
commands.append(mv_script.path)
309339

340+
# if the ctx.attr.output_file_suffix was set in conjunction with
341+
# ctx.attr.srcs, we want to rename all the output files to a different
342+
# suffix (e.g. foo.ts -> foo.ts.gen). The relocates the files that were
343+
# generated by protoc plugins to a different name. This is used by the
344+
# 'proto_compiled_sources' rule. The reason is that if we also have a
345+
# `foo.ts` source file sitting in the workspace (checked into git), rules
346+
# like `ts_project` will perform a 'copy_to_bin' action on the file. If we
347+
# didn't do this rename, the ts_project rule and the proto_compile rule
348+
# would attempt to create the same output file in bazel-bin (foo.ts),
349+
# causing an error.
350+
#
351+
# In the case of proto_compiled_sources, executing `bazel run
352+
# //proto:foo_ts.update` would generate the file
353+
# `bazel-bin/proto/foo.ts.gen` and the gencopy operation will copy that file
354+
# to `WORKSPACE/proto/foo.ts`, essentially making the `.gen` a
355+
# temporary-like file.
356+
if len(renames):
357+
rename_commands = []
358+
for src, dst in renames.items():
359+
rename_commands.append("mv {dir}/{src} {dir}/{dst}".format(
360+
dir = out_dir,
361+
src = src,
362+
dst = dst,
363+
))
364+
rename_script = ctx.actions.declare_file(ctx.label.name + "_rename.sh")
365+
ctx.actions.write(rename_script, "\n".join(rename_commands), is_executable = True)
366+
inputs.append(rename_script)
367+
commands.append(rename_script.path)
368+
310369
if verbose:
311-
before = ["env", "pwd", "ls -al .", "echo '\n##### SANDBOX BEFORE RUNNING PROTOC'", "find * -type l"]
370+
before = ["env", "pwd", "ls -al .", "echo '\n##### SANDBOX BEFORE RUNNING PROTOC'", "find * -type l | grep -v node_modules"]
312371
after = ["echo '\n##### SANDBOX AFTER RUNNING PROTOC'", "find * -type f"]
313372
commands = before + commands + after
314373

@@ -327,7 +386,7 @@ def _proto_compile_impl(ctx):
327386
for f in inputs:
328387
# buildifier: disable=print
329388
print("INPUT:", f.path)
330-
for f in outputs:
389+
for f in output_files_by_rel_path.values():
331390
# buildifier: disable=print
332391
print("EXPECTED OUTPUT:", f.path)
333392

@@ -336,17 +395,26 @@ def _proto_compile_impl(ctx):
336395
command = "\n".join(commands),
337396
inputs = inputs,
338397
mnemonic = "Protoc",
339-
outputs = outputs,
398+
outputs = output_files_by_rel_path.values(),
340399
progress_message = "Compiling protoc outputs for %r" % [f.basename for f in protos],
341400
tools = tools,
342401
input_manifests = input_manifests,
343402
env = {"BAZEL_BINDIR": ctx.bin_dir.path},
344403
)
345404

346-
return [
347-
ProtoCompileInfo(label = ctx.label, outputs = outputs),
348-
DefaultInfo(files = depset(outputs)),
405+
outputs = output_files_by_rel_path.values()
406+
407+
providers = [
408+
ProtoCompileInfo(
409+
label = ctx.label,
410+
outputs = outputs,
411+
output_files_by_rel_path = output_files_by_rel_path,
412+
),
349413
]
414+
if ctx.attr.default_info:
415+
providers.append(DefaultInfo(files = depset(outputs)))
416+
417+
return providers
350418

351419
proto_compile = rule(
352420
implementation = _proto_compile_impl,
@@ -387,6 +455,14 @@ proto_compile = rule(
387455
),
388456
"verbose": attr.bool(
389457
doc = "The verbosity flag.",
458+
default = False,
459+
),
460+
"default_info": attr.bool(
461+
doc = "If false, do not return the DefaultInfo provider",
462+
default = True,
463+
),
464+
"output_file_suffix": attr.string(
465+
doc = "If set, copy the output files to a new set having this suffix",
390466
),
391467
},
392468
toolchains = ["@build_stack_rules_proto//toolchain:protoc"],

0 commit comments

Comments
 (0)