Skip to content

Commit

Permalink
introduce inline_src (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuyawk authored Dec 21, 2024
1 parent e004ddc commit 60deec0
Show file tree
Hide file tree
Showing 11 changed files with 369 additions and 15 deletions.
6 changes: 6 additions & 0 deletions development/cli/execute_tests.bash
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,9 @@ check_bazel_build_error //tests/cc/cpp_successful_build_with_deps:with_substr_ma
check_bazel_build_error //tests/cc/cpp_successful_build_with_deps:with_substr_matcher.test
check_bazel_build_error //tests/cc/cpp_compile_error:incorrect_matcher
check_bazel_build_error //tests/cc/cpp_compile_error:incorrect_matcher.test
check_bazel_build_error //tests/cc/cpp_inline_src:incorrect_matcher
check_bazel_build_error //tests/cc/cpp_inline_src:incorrect_matcher.test
check_bazel_build_error //tests/cc/cpp_inline_src:incorrect_extension
check_bazel_build_error //tests/cc/cpp_inline_src:incorrect_extension.test
check_bazel_build_error //tests/cc/c_inline_src:incorrect_extension
check_bazel_build_error //tests/cc/c_inline_src:incorrect_extension.test
51 changes: 51 additions & 0 deletions examples/cc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ load(
"@rules_build_error//lang/cc:defs.bzl",
"cc_build_error",
"cc_build_error_test",
"inline_src",
)
load("@rules_build_error//matcher:defs.bzl", "matcher")

Expand Down Expand Up @@ -44,3 +45,53 @@ cc_build_error_test(
src = "link_error.cpp",
link_stderr = matcher.contains_basic_regex("Declared.*Undefined"),
)

# With `inline_src`, you can also provide the source as an inline string.

cc_build_error_test(
name = "compile_error_test_with_inline_c",
src = inline_src.c("""
#include <assert.h>
int main(void) {
static_assert(0, "Compile error message for inline src");
return 0;
}
"""),
compile_stderr = matcher.has_substr("static assertion failed"),
)

cc_build_error_test(
name = "compile_error_test_with_inline_cpp",
src = inline_src.cpp("""
constexpr bool Predicate() noexcept { return false; }
int main() {
static_assert(Predicate(), "Compile error message for inline src");
return 0;
}
"""),
compile_stderr = matcher.has_substr("static assertion failed"),
)

cc_build_error_test(
name = "link_error_test_with_inline_c",
src = inline_src.c("""
int DeclaredButUndefined();
int main(void) {
return DeclaredButUndefined();
}
"""),
link_stderr = matcher.has_substr("DeclaredButUndefined"),
)

cc_build_error_test(
name = "link_error_test_with_inline_cpp",
src = inline_src.cpp("""
int DeclaredButUndefined();
int main() {
return DeclaredButUndefined();
}
"""),
link_stderr = matcher.has_substr("DeclaredButUndefined"),
)
Empty file added inline_src/BUILD.bazel
Empty file.
102 changes: 102 additions & 0 deletions inline_src/inline_src.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""Define the inline source object and its functions.
"""

visibility("//lang/...")

_INLINE_SRC_TYPE = "INLINE_SRC"

def is_inline_src(obj):
"""Test if an object is an inline source object or not.
Args:
An object to test.
Returns:
True it's an inline source object, false otherwise.
"""
return type(obj) == "struct" and getattr(obj, "type", None) == _INLINE_SRC_TYPE

def inline_src_c(content):
"""Construct an inline source object for C.
Args:
content(str): Content of the source file.
Returns:
An inline source object for C.
"""

return struct(
type = _INLINE_SRC_TYPE,
content = content,
extension = "c",
)

def inline_src_cpp(content):
"""Construct an inline source object for C++.
Args:
content(str): Content of the source file.
Returns:
An inline source object for C++.
"""

return struct(
type = _INLINE_SRC_TYPE,
content = content,
extension = "cpp",
)

def _generate_inline_src_rule_impl(ctx):
"""Implementation of `_generate_inline_src_rule`.
Args:
ctx: Rule context.
Returns:
Provider for the rule.
"""
output = ctx.actions.declare_file(ctx.label.name + "." + ctx.attr.extension)
ctx.actions.write(
output = output,
content = ctx.attr.content,
)
return [DefaultInfo(files = depset([output]))]

_generate_inline_src_rule = rule(
implementation = _generate_inline_src_rule_impl,
attrs = {
"content": attr.string(
doc = (
"The content of the source file."
),
mandatory = True,
),
"extension": attr.string(
doc = (
"The extension of the source file."
),
mandatory = True,
),
},
provides = [DefaultInfo],
)

def generate_inline_src(*, name, inline_src, **kwargs):
"""Rule to generate inline source.
Args:
name(str): The name of the target.
inline_src(inline source object): The inline source object to generate.
**kwargs(dict): Passed to internal rules.
"""
if not is_inline_src(inline_src):
fail("Precondition: `inline_src` must be an inline source object.")

_generate_inline_src_rule(
name = name,
content = inline_src.content,
extension = inline_src.extension,
**kwargs
)
32 changes: 18 additions & 14 deletions lang/cc/README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
# C/C++ build error

Defines some implementations to check build error in C/C++.
Defines some implementations to check build error in C/C++. Each of them can be loaded from `//lang/cc:defs.bzl`.

## `cc_build_error`

`cc_build_error` is a rule providing [`CcBuildErrorInfo`](#ccbuilderrorinfo).

In addition to the common rule attributes listed [here](https://bazel.build/reference/be/common-definitions#common-attributes), it can receive the following attributes (regarding the specific matcher, please refer to [its readme](../../matcher/README.md)):

| Attribute | Description | Type | Is this attribute required? | Other constraints |
| ------------------------ | ------------------------------------------------------------------ | ---------------- | --------------------------- | --------------------------------------------------- |
| name | Name of the target. | str | Yes | |
| src | C/C++ source file to check build | label | Yes | Must be a single file having an extension for C/C++ |
| additional_linker_inputs | Pass these files to the linker command | list of labels | No (defaults to `[]`) | |
| copts | C/C++ compilation options | list of str | No (defaults to `[]`) | |
| deps | The list of CcInfo libraries to be linked in to the binary target. | list of label | No (defaults to `[]`) | Each list element must provide `CcInfo` |
| linkopts | C/C++ linking options | list of str | No (defaults to `[]`) | |
| local_defines | Pre-processor macro definitions | list of str | No (defaults to `[]`) | |
| compile_stderr | Matcher for the stderr message while compiling | specific matcher | No (defaults to no-op) | |
| compile_stdout | Matcher for the stdout message while compiling | specific matcher | No (defaults to no-op) | |
| link_stderr | Matcher for the stderr message while linking | specific matcher | No (defaults to no-op) | |
| link_stdout | Matcher for the stdout message while linking | specific matcher | No (defaults to no-op) | |
| Attribute | Description | Type | Is this attribute required? | Other constraints |
| ------------------------ | ------------------------------------------------------------------ | ---------------------------------------- | --------------------------- | --------------------------------------------------- |
| name | Name of the target. | str | Yes | |
| src | C/C++ source file to check build | label or [an inline source](#inline_src) | Yes | Must be a single file having an extension for C/C++ |
| additional_linker_inputs | Pass these files to the linker command | list of labels | No (defaults to `[]`) | |
| copts | C/C++ compilation options | list of str | No (defaults to `[]`) | |
| deps | The list of CcInfo libraries to be linked in to the binary target. | list of label | No (defaults to `[]`) | Each list element must provide `CcInfo` |
| linkopts | C/C++ linking options | list of str | No (defaults to `[]`) | |
| local_defines | Pre-processor macro definitions | list of str | No (defaults to `[]`) | |
| compile_stderr | Matcher for the stderr message while compiling | specific matcher | No (defaults to no-op) | |
| compile_stdout | Matcher for the stdout message while compiling | specific matcher | No (defaults to no-op) | |
| link_stderr | Matcher for the stderr message while linking | specific matcher | No (defaults to no-op) | |
| link_stdout | Matcher for the stdout message while linking | specific matcher | No (defaults to no-op) | |

### `CcBuildErrorInfo`

Expand All @@ -29,3 +29,7 @@ In addition to the common rule attributes listed [here](https://bazel.build/refe
## `cc_build_error_test`

`cc_build_error_test` is a test rule used to verify that `cc_build_error` builds successfully. It accepts the same set of arguments as `cc_build_error`.

## `inline_src`

Using `inline_src`, you can provide source code as an inline string. The function `inline_src.c` generates a C source file, while `inline_src.cpp` generates a C++ source file. Both functions accept a string containing the source code as their argument.
18 changes: 18 additions & 0 deletions lang/cc/build_error.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ load(
"@bazel_tools//tools/cpp:toolchain_utils.bzl",
"find_cpp_toolchain",
)
load(
"//inline_src:inline_src.bzl",
"generate_inline_src",
"is_inline_src",
)
load(
"//lang/private:general_build_actions.bzl",
"DEFAULT_MATCHER",
Expand Down Expand Up @@ -614,9 +619,22 @@ def cc_build_error(
}
kwargs.clear()

src = kwargs_try_build.pop("src")
if is_inline_src(src):
inline_src_target = name + "__i"
generate_inline_src(
name = inline_src_target,
inline_src = src,
tags = ["manual"] + tags,
testonly = testonly,
visibility = ["//visibility:private"],
)
src = ":" + inline_src_target

try_build_target = name + "__0"
_try_build(
name = try_build_target,
src = src,
tags = ["manual"] + tags,
os = select({
Label("//platforms/os:linux"): "linux",
Expand Down
5 changes: 5 additions & 0 deletions lang/cc/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ load(
_cc_build_error = "cc_build_error",
_cc_build_error_test = "cc_build_error_test",
)
load(
":inline_src.bzl",
_inline_src = "inline_src",
)

visibility("public")

cc_build_error = _cc_build_error
cc_build_error_test = _cc_build_error_test
CcBuildErrorInfo = _CcBuildErrorInfo
inline_src = _inline_src
15 changes: 15 additions & 0 deletions lang/cc/inline_src.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""Define `inline_src`.
"""

load(
"//inline_src:inline_src.bzl",
"inline_src_c",
"inline_src_cpp",
)

visibility("private")

inline_src = struct(
c = inline_src_c,
cpp = inline_src_cpp,
)
2 changes: 1 addition & 1 deletion matcher/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Matcher

Defines a struct `matcher`.
Defines a struct `matcher`, which can be loaded from `//matcher:defs.bzl`.

`matcher` has some member functions each of which receives a pattern string as a positional string argument, and which returns its specific matcher. Each specific matcher can be used to specify the way of validating the build error message (stderr or stdout).

Expand Down
100 changes: 100 additions & 0 deletions tests/cc/c_inline_src/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
load("@bazel_skylib//rules:build_test.bzl", "build_test")
load("//lang/cc:defs.bzl", "inline_src")
load("//matcher:defs.bzl", "matcher")
load("//tests/cc:utils.bzl", "check_build_and_test")

check_build_and_test(
name = "plain",
src = inline_src.c("""
#include <assert.h>
int main(void) {
#ifndef __cplusplus
static_assert(0, "Compile error message for inline src");
#endif
return 0;
}
"""),
)

check_build_and_test(
name = "with_substr_matcher",
src = inline_src.c("""
#include <assert.h>
int main(void) {
#ifndef __cplusplus
static_assert(0, "Compile error message for inline src");
#endif
return 0;
}
"""),
compile_stderr = matcher.has_substr("static assertion failed"),
)

check_build_and_test(
name = "with_basic_regex_matcher",
src = inline_src.c("""
#include <assert.h>
int main(void) {
#ifndef __cplusplus
static_assert(0, "Compile error message for inline src");
#endif
return 0;
}
"""),
compile_stderr = matcher.contains_basic_regex(r"static[[:space:]]assertion \(failed\|error\)"),
)

check_build_and_test(
name = "with_extended_regex_matcher",
src = inline_src.c("""
#include <assert.h>
int main(void) {
#ifndef __cplusplus
static_assert(0, "Compile error message for inline src");
#endif
return 0;
}
"""),
compile_stderr = matcher.contains_extended_regex(r"static[[:space:]]assertion (failed|error)"),
)

build_test(
name = "build_test",
targets = [
":plain",
":with_substr_matcher",
":with_basic_regex_matcher",
":with_extended_regex_matcher",
],
)

# Test case which should fail to build
check_build_and_test(
name = "incorrect_extension",
src = inline_src.cpp("""
#include <assert.h>
int main(void) {
#ifndef __cplusplus
static_assert(0, "Compile error message for inline src");
#endif
return 0;
}
"""),
compile_stderr = matcher.has_substr("static assertion failed"),
tags = ["manual"],
)
Loading

0 comments on commit 60deec0

Please sign in to comment.