From 227d9d0fbd4a072524de14aaecf0400877f0642a Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Thu, 5 Oct 2023 14:59:18 -0700 Subject: [PATCH] feat: ecsact_binary and ecsact_library (#29) --- MODULE.bazel | 1 + ecsact/defs.bzl | 6 ++ ecsact/private/ecsact_binary.bzl | 113 +++++++++++++++++++++++++ ecsact/private/ecsact_build_recipe.bzl | 66 +++++++++++++++ ecsact/private/ecsact_codegen.bzl | 3 - ecsact/private/ecsact_library.bzl | 17 ++++ ecsact/toolchain.bzl | 17 ++-- 7 files changed, 210 insertions(+), 13 deletions(-) create mode 100644 ecsact/private/ecsact_binary.bzl create mode 100644 ecsact/private/ecsact_build_recipe.bzl create mode 100644 ecsact/private/ecsact_library.bzl diff --git a/MODULE.bazel b/MODULE.bazel index 64e5268..f45b275 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -4,6 +4,7 @@ module( compatibility_level = 4, ) +bazel_dep(name = "rules_cc", version = "0.0.9") bazel_dep(name = "bazel_skylib", version = "1.4.2") bazel_dep(name = "platforms", version = "0.0.5") diff --git a/ecsact/defs.bzl b/ecsact/defs.bzl index 58e54e7..4f5b859 100644 --- a/ecsact/defs.bzl +++ b/ecsact/defs.bzl @@ -3,6 +3,12 @@ load("//ecsact/private:ecsact_codegen.bzl", _ecsact_codegen = "ecsact_codegen") load("//ecsact/private:ecsact_codegen_plugin.bzl", _ecsact_codegen_plugin = "ecsact_codegen_plugin") +load("//ecsact/private:ecsact_binary.bzl", _ecsact_binary = "ecsact_binary") +load("//ecsact/private:ecsact_build_recipe.bzl", _ecsact_build_recipe = "ecsact_build_recipe") +load("//ecsact/private:ecsact_library.bzl", _ecsact_library = "ecsact_library") ecsact_codegen = _ecsact_codegen ecsact_codegen_plugin = _ecsact_codegen_plugin +ecsact_binary = _ecsact_binary +ecsact_build_recipe = _ecsact_build_recipe +ecsact_library = _ecsact_library diff --git a/ecsact/private/ecsact_binary.bzl b/ecsact/private/ecsact_binary.bzl new file mode 100644 index 0000000..3b21976 --- /dev/null +++ b/ecsact/private/ecsact_binary.bzl @@ -0,0 +1,113 @@ +load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain", "use_cc_toolchain") +load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") +load("//ecsact/private:ecsact_build_recipe.bzl", "EcsactBuildRecipeInfo") + +def _ecsact_binary(ctx): + cc_toolchain = find_cc_toolchain(ctx) + + temp_dir = ctx.actions.declare_directory(ctx.attr.name) + + inputs = [] + + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + ) + + variables = cc_common.create_link_variables( + cc_toolchain = cc_toolchain, + feature_configuration = feature_configuration, + ) + + env = cc_common.get_environment_variables( + feature_configuration = feature_configuration, + action_name = ACTION_NAMES.cpp_link_dynamic_library, + variables = variables, + ) + + ecsact_toolchain = ctx.toolchains["//ecsact:toolchain_type"].ecsact_info + + # TODO(zaucy): derive runtime library extension based on ctx and cc_toolchain + runtime_output_file = ctx.actions.declare_file("{}.dll".format(ctx.attr.name)) + outputs = [runtime_output_file] + tools = [] + ecsact_toolchain.tool_files + + args = ctx.actions.args() + args.add("build") + args.add_all(ctx.files.srcs) + args.add_all(ctx.files.recipes, before_each = "-r") + args.add("-o", runtime_output_file) + args.add("--temp_dir", temp_dir.path) + args.add("-f", "text") + + # TODO(zaucy): detect shared library extension + preferred_output_extension = ".dll" + + compiler_config = { + "compiler_type": "auto", + "compiler_path": cc_toolchain.compiler_executable, + "compiler_version": "bazel c++ toolchain", + "install_path": "", + "std_inc_paths": cc_toolchain.built_in_include_directories, + "std_lib_paths": [], + "preferred_output_extension": preferred_output_extension, + "allowed_output_extensions": [preferred_output_extension], + } + + compiler_config_file = ctx.actions.declare_file("{}.compiler_config.json".format(ctx.attr.name)) + + ctx.actions.write(compiler_config_file, json.encode(compiler_config)) + + args.add("--compiler_config", compiler_config_file) + + if len(ctx.files.recipes) > 1: + fail("Only 1 recipe is allowed at this time") + + inputs.extend(ctx.files.srcs) + inputs.extend(ctx.files.recipes) + inputs.append(compiler_config_file) + + for recipe in ctx.attr.recipes: + recipe_info = recipe[EcsactBuildRecipeInfo] + inputs.extend(recipe_info.data) + + executable = ecsact_toolchain.target_tool if ecsact_toolchain.target_tool != None else ecsact_toolchain.target_tool_path + + ctx.actions.run( + mnemonic = "EcsactBuild", + progress_message = "Building Ecsact Runtime %{output}", + outputs = outputs + [temp_dir], + inputs = inputs, + executable = executable, + tools = tools, + arguments = [args], + env = env, + toolchain = Label("//ecsact:toolchain_type"), + ) + + return [ + DefaultInfo( + files = depset(outputs), + ), + ] + +ecsact_binary = rule( + implementation = _ecsact_binary, + attrs = { + "srcs": attr.label_list( + allow_files = [".ecsact"], + ), + "recipes": attr.label_list( + allow_empty = False, + mandatory = True, + providers = [EcsactBuildRecipeInfo], + ), + "_cc_toolchain": attr.label( + default = Label( + "@rules_cc//cc:current_cc_toolchain", + ), + ), + }, + toolchains = ["//ecsact:toolchain_type"] + use_cc_toolchain(), + fragments = ["cpp"], +) diff --git a/ecsact/private/ecsact_build_recipe.bzl b/ecsact/private/ecsact_build_recipe.bzl new file mode 100644 index 0000000..68eb31f --- /dev/null +++ b/ecsact/private/ecsact_build_recipe.bzl @@ -0,0 +1,66 @@ +load("//ecsact/private:ecsact_codegen_plugin.bzl", "EcsactCodegenPluginInfo") + +EcsactBuildRecipeInfo = provider( + doc = "", + fields = { + "recipe_path": "path to recipe yaml file", + "data": "files needed to build recipe", + }, +) + +def _ecsact_build_recipe(ctx): + recipe_yaml = ctx.actions.declare_file("{}.yml".format(ctx.attr.name)) + + sources = [] + recipe_data = [] + + for src in ctx.files.srcs: + sources.append({ + "path": src.path, + "outdir": "src", + "relative_to_cwd": True, + }) + recipe_data.append(src) + + for codegen_plugin in ctx.attr.codegen_plugins: + info = codegen_plugin[EcsactCodegenPluginInfo] + sources.append({ + "codegen": [info.plugin], + "outdir": ctx.attr.codegen_plugins[codegen_plugin], + }) + + recipe = { + "name": ctx.attr.name, + "sources": sources, + "imports": ctx.attr.imports, + "exports": ctx.attr.exports, + } + + ctx.actions.write(recipe_yaml, json.encode(recipe)) + + return [ + DefaultInfo( + files = depset([recipe_yaml]), + ), + EcsactBuildRecipeInfo( + recipe_path = recipe_yaml, + data = recipe_data, + ), + ] + +ecsact_build_recipe = rule( + implementation = _ecsact_build_recipe, + attrs = { + "srcs": attr.label_list( + allow_files = True, + ), + "codegen_plugins": attr.label_keyed_string_dict( + providers = [EcsactCodegenPluginInfo], + ), + "imports": attr.string_list( + ), + "exports": attr.string_list( + mandatory = True, + ), + }, +) diff --git a/ecsact/private/ecsact_codegen.bzl b/ecsact/private/ecsact_codegen.bzl index 0f16901..3a161b4 100644 --- a/ecsact/private/ecsact_codegen.bzl +++ b/ecsact/private/ecsact_codegen.bzl @@ -25,9 +25,6 @@ def _ecsact_codegen(ctx): out_basename = ctx.attr.name + "/" + src.basename outputs.append(ctx.actions.declare_file(out_basename + "." + plugin_info.output_extension)) - for p in info.target_tool_path_runfiles: - tools.extend(p.files.to_list()) - ctx.actions.run( mnemonic = "EcsactCodegen", outputs = [outdir] + outputs, diff --git a/ecsact/private/ecsact_library.bzl b/ecsact/private/ecsact_library.bzl new file mode 100644 index 0000000..d410d19 --- /dev/null +++ b/ecsact/private/ecsact_library.bzl @@ -0,0 +1,17 @@ +EcsactLibraryInfo = provider() + +def _ecsact_library(ctx): + return [EcsactLibraryInfo()] + +ecsact_library = rule( + implementation = _ecsact_library, + attrs = { + "srcs": attr.label_list( + allow_files = [".ecsact"], + ), + # "deps": attr.label_list( + # allow_rules = [EcsactLibraryInfo], + # ), + }, + toolchains = ["//ecsact:toolchain_type"], +) diff --git a/ecsact/toolchain.bzl b/ecsact/toolchain.bzl index 1794403..0378493 100644 --- a/ecsact/toolchain.bzl +++ b/ecsact/toolchain.bzl @@ -4,8 +4,8 @@ EcsactInfo = provider( doc = "Information about how to invoke the tool executable.", fields = { + "target_tool": "", "target_tool_path": "Path to the tool executable for the target platform.", - "target_tool_path_runfiles": "", "tool_files": """Files required in runfiles to make the tool executable available. May be empty if the target_tool_path points to a locally installed tool binary.""", @@ -17,14 +17,13 @@ def _ecsact_toolchain_impl(ctx): fail("Can only set one of target_tool or target_tool_path but both were set.") if not ctx.attr.target_tool and not ctx.attr.target_tool_path: fail("Must set one of target_tool or target_tool_path.") - if ctx.attr.target_tool and ctx.attr.target_tool_path_runfiles: - fail("Cannot use target_tool_path_runfiles with target_tool.") tool_files = [] target_tool_path = ctx.attr.target_tool_path if ctx.attr.target_tool: - tool_files = ctx.attr.target_tool.files.to_list() + tool_files = ctx.attr.target_tool[DefaultInfo].files.to_list() + tool_files.extend(ctx.attr.target_tool[DefaultInfo].default_runfiles.files.to_list()) target_tool_path = tool_files[0].path # Make the $(ECSACT_BIN) variable available in places like genrules. @@ -36,9 +35,12 @@ def _ecsact_toolchain_impl(ctx): files = depset(tool_files), runfiles = ctx.runfiles(files = tool_files), ) + + target_tool = ctx.attr.target_tool[DefaultInfo].files_to_run if ctx.attr.target_tool else None + ecsact_info = EcsactInfo( + target_tool = target_tool, target_tool_path = target_tool_path, - target_tool_path_runfiles = ctx.attr.target_tool_path_runfiles, tool_files = tool_files, ) @@ -65,11 +67,6 @@ ecsact_toolchain = rule( doc = "Path to an existing executable for the target platform.", mandatory = False, ), - "target_tool_path_runfiles": attr.label_list( - doc = "List of files needed at runtime for `target_tool_path`. Not valid with `target_tool`.", - mandatory = False, - allow_files = True, - ), }, doc = """Defines a ecsact compiler/runtime toolchain.