1load("@rules_cc//cc:action_names.bzl", "C_COMPILE_ACTION_NAME")
2load("@rules_cc//cc:toolchain_utils.bzl", "find_cpp_toolchain")
3
4def _generate_fruit_config_impl(ctx):
5    cc_toolchain = find_cpp_toolchain(ctx)
6
7    feature_configuration = cc_common.configure_features(
8        ctx = ctx,
9        cc_toolchain = cc_toolchain,
10        requested_features = ctx.features,
11        unsupported_features = ctx.disabled_features,
12    )
13    c_compiler_path = cc_common.get_tool_for_action(
14        feature_configuration = feature_configuration,
15        action_name = C_COMPILE_ACTION_NAME,
16    )
17
18    check_output_files = []
19    for check_source in ctx.files.check_sources:
20        check_name = check_source.path[:-len(".cpp")].split('/')[-1].split('\\')[-1]
21
22        output_file = ctx.actions.declare_file(check_name + ".o")
23
24        c_compile_variables = cc_common.create_compile_variables(
25            feature_configuration = feature_configuration,
26            cc_toolchain = cc_toolchain,
27            user_compile_flags = ctx.fragments.cpp.copts + ctx.fragments.cpp.conlyopts,
28            source_file = check_source.path,
29            output_file = output_file.path,
30        )
31        command_line = cc_common.get_memory_inefficient_command_line(
32            feature_configuration = feature_configuration,
33            action_name = C_COMPILE_ACTION_NAME,
34            variables = c_compile_variables,
35        )
36        env = cc_common.get_environment_variables(
37            feature_configuration = feature_configuration,
38            action_name = C_COMPILE_ACTION_NAME,
39            variables = c_compile_variables,
40        )
41
42        check_define = 'FRUIT_HAS_%s' % check_name.upper()
43        check_output_file = ctx.actions.declare_file(check_name + ".h")
44
45        ctx.actions.run_shell(
46            command = '"$@" &>/dev/null && echo "#define %s 1" >"%s" || echo "#define %s 0" >"%s"; touch "%s"' % (
47                check_define, check_output_file.path, check_define, check_output_file.path, output_file.path
48            ),
49            arguments = [c_compiler_path] + command_line,
50            env = env,
51            inputs = depset(
52                [check_source],
53                transitive = [cc_toolchain.all_files],
54            ),
55            outputs = [output_file, check_output_file],
56        )
57        check_output_files.append(check_output_file)
58
59    merged_output_file = ctx.actions.declare_file("fruit/impl/fruit-config-base.h")
60    ctx.actions.run_shell(
61        command = '\n'.join([
62            '(',
63            'echo "#ifndef FRUIT_CONFIG_BASE_H"',
64            'echo "#define FRUIT_CONFIG_BASE_H"',
65            'echo "#define FRUIT_USES_BOOST 1"',
66            'cat %s' % ' '.join([check_output_file.path for check_output_file in check_output_files]),
67            'echo "#endif"',
68            ')>%s' % merged_output_file.path
69        ]),
70        inputs = check_output_files,
71        outputs = [merged_output_file],
72    )
73
74    compilation_context, compilation_outputs = cc_common.compile(
75        actions = ctx.actions,
76        feature_configuration = feature_configuration,
77        cc_toolchain = cc_toolchain,
78        public_hdrs = [merged_output_file],
79        name = "%s_link" % ctx.label.name,
80    )
81
82    linking_context, linking_outputs = cc_common.create_linking_context_from_compilation_outputs(
83        actions = ctx.actions,
84        feature_configuration = feature_configuration,
85        compilation_outputs = compilation_outputs,
86        cc_toolchain = cc_toolchain,
87        name = "%s_link" % ctx.label.name,
88    )
89
90    return [
91        DefaultInfo(files = depset([merged_output_file]), runfiles = ctx.runfiles(files = [merged_output_file])),
92        CcInfo(compilation_context=compilation_context, linking_context=linking_context),
93    ]
94
95generate_fruit_config = rule(
96    implementation = _generate_fruit_config_impl,
97    attrs = {
98        "check_sources": attr.label_list(allow_files = True),
99        "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
100    },
101    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
102    fragments = ["cpp"],
103)