Skip to content

Commit

Permalink
Add src/test/many -- a simple C++ bazel stress test (#535)
Browse files Browse the repository at this point in the history
This allows you to build a simple bazel C++ build graph of any size.
It consists of a toplevel filegroup named `cc`. This filegroup
contains `MANY_CC_BINARIES` program binaries, where `MANY_CC_BINARIES`
is an environment variable whose default value is `1`. Each of these
programs links with `MANY_CC_LIBRARIES`, each of which is generated
from `MANY_CC_LIBRARY_SOURCES`+1 files.

Simply set the environment variables as desired in order to build the
corresponding graph. For instance:

    MANY_CC_BINARIES=20 MANY_CC_LIBRARIES=10 MANY_CC_LIBRARY_SOURCES=5 bazel build //:cc
  • Loading branch information
dws committed Oct 6, 2020
1 parent 97f79e8 commit 490c13f
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .bazelignore
@@ -0,0 +1,2 @@
.git
src/test/many
21 changes: 21 additions & 0 deletions src/test/many/BUILD
@@ -0,0 +1,21 @@
"""
Provide a simple C++ build graph as large as desired.
You may alter the values of cc_binaries(), cc_libraries(), and
cc_library_sources() via the environment variables MANY_CC_BINARIES,
MANY_CC_LIBRARIES, and MANY_CC_LIBRARY_SOURCES. For instance:
MANY_CC_BINARIES=20 MANY_CC_LIBRARIES=10 MANY_CC_LIBRARY_SOURCES=5 bazel build //:cc
"""

load(":many-cc.bzl", "many_cc")
load("@many-params//:cc-binaries.bzl", "cc_binaries")
load("@many-params//:cc-libraries.bzl", "cc_libraries")
load("@many-params//:cc-library-sources.bzl", "cc_library_sources")

many_cc(
name = "cc",
binary_count = cc_binaries(),
library_count = cc_libraries(),
library_source_count = cc_library_sources(),
)
13 changes: 13 additions & 0 deletions src/test/many/README.md
@@ -0,0 +1,13 @@
# Many

This allows you to build a simple bazel C++ build graph of any size.
It consists of a toplevel filegroup named `cc`. This filegroup
contains `MANY_CC_BINARIES` program binaries, where `MANY_CC_BINARIES`
is an environment variable whose default value is `1`. Each of these
programs links with `MANY_CC_LIBRARIES`, each of which is generated
from `MANY_CC_LIBRARY_SOURCES`+1 files.

Simply set the environment variables as desired in order to build the
corresponding graph. For instance:

MANY_CC_BINARIES=20 MANY_CC_LIBRARIES=10 MANY_CC_LIBRARY_SOURCES=5 bazel build //:cc
20 changes: 20 additions & 0 deletions src/test/many/WORKSPACE
@@ -0,0 +1,20 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
name = "bazel_skylib",
sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c",
urls = [
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz",
],
)

load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")

bazel_skylib_workspace()

load(":many-params.bzl", "many_params")

many_params(
name = "many-params",
)
185 changes: 185 additions & 0 deletions src/test/many/many-cc.bzl
@@ -0,0 +1,185 @@
"""
Provide a simple C++ build graph as large as desired.
"""

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("@bazel_skylib//rules:write_file.bzl", _write_file = "write_file")

def write_file(name, out, lines, **kwargs):
"""
Call the skylib write_file routine, ensuring that all lines end with a newline.
"""
_write_file(
name = name,
out = out,
content = lines + [""],
)

def id(s):
return s.replace("-", "_")

def many_cc_library_label(library_key):
return "lib{}".format(library_key)

def many_cc_library_header(library_key):
return "lib{}.hh".format(library_key)

def many_cc_library_function(library_key):
return "func_{}".format(id(library_key))

def many_cc_library(library_key, library_source_count):
"""
Generate a C++ library.
The library has library_source_count+1 distinct functions. One
function is always present and is named for the library. It calls
all of the remaining functions in the library.
Args:
library_key: determines the name of the C++ library
library_source_count: determines how many source files the library will have
"""
library_source_index = 0
cc_labels = []
hh_files = []
funcnames = []
for library_source_index in range(library_source_count):
rulename_hh = "lib{}-{}_hh".format(library_key, library_source_index)
filename_hh = "lib{}-{}.hh".format(library_key, library_source_index)
rulename_cc = "lib{}-{}_cc".format(library_key, library_source_index)
filename_cc = "lib{}-{}.cc".format(library_key, library_source_index)
funcname = "func_{}_{}".format(id(library_key), library_source_index)

cc_labels.append(rulename_cc)
cc_labels.append(rulename_hh)
hh_files.append(filename_hh)
funcnames.append(funcname)

lines = []
lines.append("#include <iostream>")
lines.append('#include "{}"'.format(filename_hh))
lines.append("void {}(void)".format(funcname))
lines.append("{")
lines.append(' std::cout << "Hello, World! {} {}" << std::endl;'.format(library_key, library_source_index))
lines.append("}")

write_file(
name = rulename_cc,
out = filename_cc,
lines = lines,
)

lines = []
lines.append("extern void {}(void);".format(funcname))

write_file(
name = rulename_hh,
out = filename_hh,
lines = lines,
)

library_source_index += 1

rulename_hh = "lib{}_hh".format(library_key)
filename_hh = many_cc_library_header(library_key)
rulename_cc = "lib{}_cc".format(library_key)
filename_cc = "lib{}.cc".format(library_key)
funcname = many_cc_library_function(library_key)

lines = []
lines.extend(['#include "{}"'.format(x) for x in hh_files])
lines.append('#include "{}"'.format(filename_hh))
lines.append("void {}(void)".format(funcname))
lines.append("{")
lines.extend([" {}();".format(x) for x in funcnames])
lines.append("}")

write_file(
name = rulename_cc,
out = filename_cc,
lines = lines,
)

lines = []
lines.append("extern void {}(void);".format(funcname))

write_file(
name = rulename_hh,
out = filename_hh,
lines = lines,
)

cc_library(
name = many_cc_library_label(library_key),
srcs = cc_labels + [rulename_cc],
hdrs = [rulename_hh],
)

def many_cc_binary_label(binary_key):
return "{}".format(binary_key)

def many_cc_binary(binary_key, library_count, library_source_count):
"""
Generate a C++ program.
The program links with library_count libraries, each of which has
library_source_count+1 functions.
Args:
binary_key: determines the name of the C++ program
library_count: how many libraries this program should link with
library_source_count: how many source files each library should have
"""
library_keys = []
for library_index in range(library_count):
library_key = "{}-{}".format(binary_key, library_index)
library_keys.append(library_key)
many_cc_library(library_key, library_source_count)

rulename_cc = "{}_cc".format(binary_key)
filename_cc = "{}.cc".format(binary_key)

lines = []
lines.extend(['#include "{}"'.format(many_cc_library_header(x)) for x in library_keys])
lines.append("int main(void)")
lines.append("{")
lines.extend([" {}();".format(many_cc_library_function(x)) for x in library_keys])
lines.append(" return 0;")
lines.append("}")

write_file(
name = rulename_cc,
out = filename_cc,
lines = lines,
)

cc_binary(
name = many_cc_binary_label(binary_key),
srcs = [rulename_cc],
deps = [many_cc_library_label(x) for x in library_keys],
)

def many_cc(name, binary_count, library_count, library_source_count):
"""
Generate a C++ build graph under a filegroup of the given name.
The build graph has binary_count programs, each of which links with
library_count libraries, each of which has library_source_count+1
functions.
Args:
name: name of the filegroup to generate
binary_count: how many programs to generate in the filegroup
library_count: how many libraries each program should link with
library_source_count: how many source files each library should have
"""
binary_keys = []
for binary_index in range(binary_count):
binary_key = "{}-{}".format(name, binary_index)
binary_keys.append(binary_key)
many_cc_binary(binary_key, library_count, library_source_count)

native.filegroup(
name = name,
srcs = [many_cc_binary_label(x) for x in binary_keys],
)
61 changes: 61 additions & 0 deletions src/test/many/many-params.bzl
@@ -0,0 +1,61 @@
"""
Make the values of certain environment variables available to the rest
of the build as function values.
This is a way to pass parameters into the build that bazel macros can
consume. We cannot use Starlark's user defined build settings for this,
as these are themselves bazel rules, so their values are not available
when bazel processes macros. To get around this limitation, we here
define a custom repository rule that dynamically generates bazel macros
based on the values of environment variables.
"""

def _many_params_impl(repository_ctx):
params = [
{
"path": "cc-binaries.bzl",
"name": "cc_binaries",
"value": repository_ctx.os.environ.get("MANY_CC_BINARIES", "1"),
},
{
"path": "cc-libraries.bzl",
"name": "cc_libraries",
"value": repository_ctx.os.environ.get("MANY_CC_LIBRARIES", "1"),
},
{
"path": "cc-library-sources.bzl",
"name": "cc_library_sources",
"value": repository_ctx.os.environ.get("MANY_CC_LIBRARY_SOURCES", "1"),
},
]

for param in params:
content = """\
def {name}():
return {value}
""".format(name = param["name"], value = param["value"])

repository_ctx.file(
param["path"],
content = content,
executable = False,
)

# Need to have a BUILD file to define a bazel package for the rules files above.
repository_ctx.file(
"BUILD",
content = "",
executable = False,
)

return None

many_params = repository_rule(
implementation = _many_params_impl,
local = True,
environ = [
"MANY_CC_BINARIES",
"MANY_CC_LIBRARIES",
"MANY_CC_LIBRARY_SOURCES",
],
)

0 comments on commit 490c13f

Please sign in to comment.