blob: 59e1ec0293a0a313375d078434cfa4d37d4cf8a3 [file]
"""
Macro to generate exponential dependency proto structure for both cached and notcached variants.
Generates proto files into separate subdirectories without duplication.
"""
load("//bazel:proto_library.bzl", "proto_library")
def _pad(n):
"""Zero-pad a number to 2 digits."""
return str(n) if n >= 10 else "0" + str(n)
def generate_deep_dependencies_protos(width, depth):
"""
Generate proto files and proto_library targets for both cached and notcached variants.
Args:
width: Number of files per level (except last level which has 1)
depth: Number of levels
"""
# Generate both variants
_generate_variant("cached", width, depth, "unittest_deep_dependencies_cached")
_generate_variant("notcached", width, depth, "unittest_deep_dependencies_not_cached")
def _generate_variant(variant, width, depth, package):
"""
Generate proto files and libraries for a specific variant.
Args:
variant: "cached" or "notcached"
width: Number of files per level
depth: Number of levels
package: Proto package name
"""
# Calculate all proto file names - use package name for the directory
all_files = [package + "/file_00_00.proto"]
for level in range(1, depth + 1):
num_files = width if level < depth else 1
for idx in range(num_files):
all_files.append(package + "/file_" + _pad(level) + "_" + _pad(idx) + ".proto")
all_files.append(package + "/file_entry.proto")
# Single genrule to generate ALL proto files for this variant
_generate_all_protos(
name = "generate_" + variant + "_protos",
variant = variant,
width = width,
depth = depth,
package = package,
all_files = all_files,
)
# Level 0: Base proto_library
proto_library(
name = variant + "_file_00_00_proto",
srcs = [":" + package + "/file_00_00.proto"],
strip_import_prefix = "/third_party/protobuf/csharp/protos/unittest_deep_dependencies",
)
# Level 1: WIDTH proto_library targets
for i in range(width):
i_pad = _pad(i)
proto_library(
name = variant + "_file_01_" + i_pad + "_proto",
srcs = [":" + package + "/file_01_" + i_pad + ".proto"],
strip_import_prefix = "/third_party/protobuf/csharp/protos/unittest_deep_dependencies",
deps = [":" + variant + "_file_00_00_proto"],
exports = [":" + variant + "_file_00_00_proto"],
)
# Levels 2 through DEPTH
for level in range(2, depth + 1):
num_files = width if level < depth else 1
level_pad = _pad(level)
level_prev_pad = _pad(level - 1)
for idx in range(num_files):
idx_pad = _pad(idx)
deps = [
":" + variant + "_file_" + level_prev_pad + "_" + _pad(prev_idx) + "_proto"
for prev_idx in range(width)
]
proto_library(
name = variant + "_file_" + level_pad + "_" + idx_pad + "_proto",
srcs = [":" + package + "/file_" + level_pad + "_" + idx_pad + ".proto"],
strip_import_prefix = "/third_party/protobuf/csharp/protos/unittest_deep_dependencies",
deps = deps,
exports = deps,
)
# Entry point proto_library
proto_library(
name = variant + "_entry_proto",
srcs = [":" + package + "/file_entry.proto"],
strip_import_prefix = "/third_party/protobuf/csharp/protos/unittest_deep_dependencies",
deps = [":" + variant + "_file_" + _pad(depth) + "_00_proto"],
exports = [":" + variant + "_file_" + _pad(depth) + "_00_proto"],
)
# Generate C# source code (.pb.cs) from these generated .proto files
_generate_csharp_sources(
name = variant + "_csharp_gen",
variant = variant,
all_files = all_files,
)
def _generate_all_protos(name, variant, width, depth, package, all_files):
"""Generate a single genrule that creates all proto files for a variant.
These files only contain imports to create deep dependency chains.
Only the entry file has an actual message.
"""
# Build the command to generate all files
cmd_parts = []
if variant == "cached":
# Cached version - Level 0 with a message
cmd_parts.append("""
mkdir -p $(RULEDIR)/%s
cat > $(RULEDIR)/%s/file_00_00.proto << 'EOFPROTO'
syntax = "proto3";
package %s;
message Level00Message {
string field = 1;
}
EOFPROTO
""" % (package, package, package))
# Level 1 files - with messages
for i in range(width):
i_pad = _pad(i)
cmd_parts.append("""
cat > $(RULEDIR)/%s/file_01_%s.proto << 'EOFPROTO'
syntax = "proto3";
package %s;
import public "%s/file_00_00.proto";
message Level01Message%s {
Level00Message field = 1;
}
EOFPROTO
""" % (package, i_pad, package, package, i_pad))
else:
# Notcached version - Level 0 empty
cmd_parts.append("""
mkdir -p $(RULEDIR)/%s
cat > $(RULEDIR)/%s/file_00_00.proto << 'EOFPROTO'
syntax = "proto3";
package %s;
EOFPROTO
""" % (package, package, package))
# Level 1 files - just imports
for i in range(width):
i_pad = _pad(i)
cmd_parts.append("""
cat > $(RULEDIR)/%s/file_01_%s.proto << 'EOFPROTO'
syntax = "proto3";
package %s;
import public "%s/file_00_00.proto";
EOFPROTO
""" % (package, i_pad, package, package))
# Levels 2 through DEPTH - just imports, no messages
for level in range(2, depth + 1):
num_files = width if level < depth else 1
level_pad = _pad(level)
level_prev_pad = _pad(level - 1)
for idx in range(num_files):
idx_pad = _pad(idx)
import_lines = []
for prev_idx in range(width):
import_lines.append('import public "%s/file_%s_%s.proto";' % (package, level_prev_pad, _pad(prev_idx)))
imports_str = "\n".join(import_lines)
cmd_parts.append("""
cat > $(RULEDIR)/%s/file_%s_%s.proto << 'EOFPROTO'
syntax = "proto3";
package %s;
%s
EOFPROTO
""" % (package, level_pad, idx_pad, package, imports_str))
# Entry point file
if variant == "cached":
message_type = "Example"
else:
message_type = "ExampleNotCached"
cmd_parts.append("""
cat > $(RULEDIR)/%s/file_entry.proto << 'EOFPROTO'
syntax = "proto3";
package %s;
import public "%s/file_%s_00.proto";
message %s {
string test_field = 1;
}
EOFPROTO
""" % (package, package, package, _pad(depth), message_type))
native.genrule(
name = name,
outs = all_files,
cmd = "".join(cmd_parts),
)
def _generate_csharp_sources(name, variant, all_files):
"""Generate C# source files from generated proto files using a single genrule."""
base_namespace = "UnittestDeepDependenciesCached" if variant == "cached" else "UnittestDeepDependenciesNotCached"
# Translate each proto file path to its expected C# output file name
csharp_outputs = []
for f in all_files:
basename = f.split("/")[-1] # "file_00_00.proto"
name_no_ext = basename.split(".proto")[0] # "file_00_00"
csharp_name = ""
for part in name_no_ext.split("_"):
if len(part) > 0:
csharp_name += part[0].upper() + part[1:]
csharp_outputs.append(variant + "_cs/" + csharp_name + ".pb.cs")
# Invoke protoc hermetically with the correct base namespace and include path
native.genrule(
name = name,
testonly = True,
srcs = [":" + f for f in all_files],
outs = csharp_outputs,
cmd = (
"$(location //net/proto2/compiler/public:protocol_compiler) " +
"--csharp_out=$(RULEDIR)/" + variant + "_cs " +
"--csharp_opt=file_extension=.pb.cs " +
"--csharp_opt=base_namespace=" + base_namespace + " " +
"-I$$(dirname $$(dirname $(location :" + all_files[0] + "))) " +
"$(SRCS)"
),
tools = ["//net/proto2/compiler/public:protocol_compiler"],
visibility = ["//src/google/protobuf/csharp:__subpackages__"],
)