blob: cec4149a60fc829dad6b2a2da3a3a63ae0a6f85d [file] [log] [blame] [edit]
load("@d2l_rules_csharp//csharp/private:actions/assembly.bzl", "AssemblyAction")
load("@d2l_rules_csharp//csharp/private:actions/misc.bzl", "write_runtimeconfig")
load(
"@d2l_rules_csharp//csharp/private:common.bzl",
"fill_in_missing_frameworks",
"is_core_framework",
"is_debug",
"is_standard_framework",
)
load("@d2l_rules_csharp//csharp/private:providers.bzl", "AnyTargetFrameworkInfo")
load("//dotnet/private:copy_files.bzl", "copy_files")
def _generate_execution_script_file(ctx, target):
tfm = target.actual_tfm
assembly_file_name = target.out.basename
shell_file_extension = "sh"
execution_line = "$( cd \"$(dirname \"$BASH_SOURCE[0]}\")\" >/dev/null 2>&1 && pwd -P )/" + assembly_file_name + " $@"
if ctx.attr.is_windows:
shell_file_extension = "bat"
execution_line = "%~dp0" + assembly_file_name + " %*"
if is_core_framework(tfm) or tfm == "net5.0" or tfm == "net6.0":
execution_line = "dotnet " + execution_line
else:
execution_line = "mono " + execution_line
toolchain = ctx.toolchains["@d2l_rules_csharp//csharp/private:toolchain_type"]
dotnet_sdk_location = toolchain.runtime.executable.dirname
environment = ""
if not ctx.attr.is_windows:
environment += "export DOTNET_CLI_HOME=%s\n" % dotnet_sdk_location
environment += "export APPDATA=%s\n" % dotnet_sdk_location
environment += "export PROGRAMFILES=%s\n" % dotnet_sdk_location
environment += "export USERPROFILE=%s\n" % dotnet_sdk_location
environment += "export DOTNET_CLI_TELEMETRY_OPTOUT=1\n"
else:
environment += "@echo off\n"
shell_content = environment + execution_line
shell_file_name = "bazelout/%s/%s.%s" % (tfm, assembly_file_name, shell_file_extension)
shell_file = ctx.actions.declare_file(shell_file_name)
ctx.actions.write(
output = shell_file,
content = shell_content,
is_executable = True,
)
return shell_file
def _copy_dependency_files(ctx, provider_value):
src_list = provider_value.transitive_runfiles.to_list()
target_dir = "bazelout/%s/" % (provider_value.actual_tfm)
file_list = []
for src_file in src_list:
file_list.append((src_file, src_file.basename))
return copy_files(ctx, file_list, target_dir, ctx.attr.is_windows)
def create_executable_assembly(ctx, extra_srcs, extra_deps):
stdrefs = [ctx.attr._stdrefs] if ctx.attr.include_stdrefs else []
providers = {}
for tfm in ctx.attr.target_frameworks:
if is_standard_framework(tfm):
fail("It doesn't make sense to build an executable for " + tfm)
providers[tfm] = AssemblyAction(
ctx.actions,
additionalfiles = ctx.files.additionalfiles,
analyzers = ctx.attr.analyzers,
debug = is_debug(ctx),
defines = ctx.attr.defines,
deps = ctx.attr.deps + extra_deps + stdrefs,
keyfile = ctx.file.keyfile,
internals_visible_to = None,
internals_visible_to_cs = None,
langversion = ctx.attr.langversion,
nullable = ctx.attr.nullable,
resources = ctx.files.resources,
srcs = ctx.files.srcs + extra_srcs,
out = ctx.attr.out,
target = "exe",
target_name = ctx.attr.name,
target_framework = tfm,
toolchain = ctx.toolchains["@d2l_rules_csharp//csharp/private:toolchain_type"],
)
fill_in_missing_frameworks(ctx.attr.out, providers)
result = providers.values()
dependency_files_list = _copy_dependency_files(ctx, result[0])
runtimeconfig = write_runtimeconfig(
ctx.actions,
ctx.file.runtimeconfig_template,
result[0].out.basename.replace("." + result[0].out.extension, ""),
result[0].actual_tfm,
)
data_runfiles = [] if ctx.attr.data == None else [d.files for d in ctx.attr.data]
shell_file = _generate_execution_script_file(ctx, result[0])
direct_runfiles = [result[0].out, result[0].pdb]
if runtimeconfig != None:
direct_runfiles.append(runtimeconfig)
result.append(DefaultInfo(
executable = shell_file,
runfiles = ctx.runfiles(
files = direct_runfiles,
transitive_files = depset(dependency_files_list, transitive = data_runfiles),
),
files = depset([result[0].out, result[0].prefout, result[0].pdb, shell_file]),
))
return result
def _csharp_executable_impl(ctx):
extra_srcs = []
extra_deps = []
return create_executable_assembly(ctx, extra_srcs, extra_deps)
csharp_executable = rule(
_csharp_executable_impl,
doc = "Create an executable C# exe",
attrs = {
"srcs": attr.label_list(
doc = "C# source files.",
allow_files = [".cs"],
),
"additionalfiles": attr.label_list(
doc = "Extra files to configure analyzers.",
allow_files = True,
),
"analyzers": attr.label_list(
doc = "A list of analyzer references.",
providers = AnyTargetFrameworkInfo,
),
"keyfile": attr.label(
doc = "The key file used to sign the assembly with a strong name.",
allow_single_file = True,
),
"langversion": attr.string(
doc = "The version string for the C# language.",
),
"nullable": attr.string(
doc = "Enable nullable context, or nullable warnings.",
),
"resources": attr.label_list(
doc = "A list of files to embed in the DLL as resources.",
allow_files = True,
),
"out": attr.string(
doc = "File name, without extension, of the built assembly.",
),
"target_frameworks": attr.string_list(
doc = "A list of target framework monikers to build" +
"See https://docs.microsoft.com/en-us/dotnet/standard/frameworks",
allow_empty = False,
),
"defines": attr.string_list(
doc = "A list of preprocessor directive symbols to define.",
default = [],
allow_empty = True,
),
"include_stdrefs": attr.bool(
doc = "Whether to reference @net//:StandardReferences (the default set of references that MSBuild adds to every project).",
default = True,
),
"runtimeconfig_template": attr.label(
doc = "A template file to use for generating runtimeconfig.json",
default = "@d2l_rules_csharp//csharp/private:runtimeconfig.json.tpl",
allow_single_file = True,
),
"_stdrefs": attr.label(
doc = "The standard set of assemblies to reference.",
default = "@net//:StandardReferences",
),
"deps": attr.label_list(
doc = "Other C# libraries, binaries, or imported DLLs",
providers = AnyTargetFrameworkInfo,
),
"data": attr.label_list(
doc = "Additional data files or targets that are required to run the executable.",
allow_files = True,
),
"is_windows": attr.bool(default = False),
},
executable = True,
toolchains = ["@d2l_rules_csharp//csharp/private:toolchain_type"],
)