load("@rules_dotnet//dotnet/private:common.bzl", "is_debug")
load("@rules_dotnet//dotnet/private:providers.bzl", "DotnetAssemblyRuntimeInfo")
load(":dotnet_utils.bzl", "dotnet_preamble")
def _guess_dotnet_version(label, assembly_info):
if len(assembly_info.libs) == 0:
fail("Cannot guess .Net version without an output dll: ", assembly_info.name)
# We're going to rely on the structure of the output names for now
# rather than scanning through the dependencies. If this works,
# life will be good.
# The dirname will be something like `bazel-out/darwin_arm64-fastbuild-ST-5c013bc87029/bin/dotnet/src/webdriver/bazelout/net5.0`
# Note that the last segment of the path is the framework we're
# targeting. Happy days!
return assembly_info.libs[0].dirname.split("/")[::-1][0]
def nuget_pack_impl(ctx):
nuspec = ctx.actions.declare_file("%s-generated.nuspec" % ctx.label.name)
ctx.actions.expand_template(
template = ctx.file.nuspec_template,
output = nuspec,
substitutions = {
"$packageid$": ctx.attr.id,
"$version$": ctx.attr.version,
},
)
build_flavor = "Debug" if is_debug(ctx) else "Release"
# A mapping of files to the paths in which we expect to find them in the package
paths = {}
for (lib, name) in ctx.attr.libs.items():
assembly_info = lib[DotnetAssemblyRuntimeInfo]
for dll in assembly_info.libs:
paths[dll] = "lib/%s/%s.dll" % (_guess_dotnet_version(lib.label, assembly_info), name)
for pdb in assembly_info.pdbs:
paths[pdb] = "lib/%s/%s.pdb" % (_guess_dotnet_version(lib.label, assembly_info), name)
for doc in assembly_info.xml_docs:
paths[doc] = "lib/%s/%s.xml" % (_guess_dotnet_version(lib.label, assembly_info), name)
csproj_template = """<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>%s</AssemblyName>
<RootNamespace>OpenQA.Selenium</RootNamespace>
</PropertyGroup>
</Project>
""" % ctx.attr.id
csproj_file = ctx.actions.declare_file("%s-generated.csproj" % ctx.label.name)
ctx.actions.write(csproj_file, csproj_template)
paths[csproj_file] = "project.csproj"
for (file, name) in ctx.attr.files.items():
paths[file.files.to_list()[0]] = name
# Zip everything up so we have the right file structure
zip_file = ctx.actions.declare_file("%s-intermediate.zip" % ctx.label.name)
args = ctx.actions.args()
args.add_all(["Cc", zip_file])
for (file, path) in paths.items():
args.add("%s=%s" % (path, file.path))
args.add("project.nuspec=%s" % (nuspec.path))
ctx.actions.run(
executable = ctx.executable._zip,
arguments = [args],
inputs = paths.keys() + [nuspec],
outputs = [zip_file],
)
# Now lay everything out on disk and execute the dotnet pack rule
# Now we have everything, let's build our package
toolchain = ctx.toolchains["@rules_dotnet//dotnet:toolchain_type"]
nupkg_name_stem = "%s.%s" % (ctx.attr.id, ctx.attr.version)
dotnet = toolchain.runtime.files_to_run.executable
pkg = ctx.actions.declare_file("%s.nupkg" % nupkg_name_stem)
symbols_pkg = ctx.actions.declare_file("%s.snupkg" % nupkg_name_stem)
# Prepare our cache of nupkg files
packages = ctx.actions.declare_directory("%s-nuget-packages" % ctx.label.name)
packages_cmd = "mkdir -p %s " % packages.path
transitive_libs = depset(transitive = [l[DotnetAssemblyRuntimeInfo].deps for l in ctx.attr.libs]).to_list()
package_files = depset([lib.nuget_info.nupkg for lib in transitive_libs if lib.nuget_info]).to_list()
if len(package_files):
packages_cmd += "&& cp " + " ".join([f.path for f in package_files]) + " " + packages.path
ctx.actions.run_shell(
outputs = [packages],
inputs = package_files,
command = packages_cmd,
mnemonic = "LayoutNugetPackages",
)
cmd = dotnet_preamble(toolchain) + \
"mkdir %s-working-dir && " % ctx.label.name + \
"echo $(pwd) && " + \
"$(location @bazel_tools//tools/zip:zipper) x %s -d %s-working-dir && " % (zip_file.path, ctx.label.name) + \
"cd %s-working-dir && " % ctx.label.name + \
"echo '<configuration><packageSources><clear /><add key=\"local\" value=\"%%CWD%%/%s\" /></packageSources></configuration>' >nuget.config && " % packages.path + \
"$DOTNET restore --no-dependencies && " + \
"$DOTNET pack --no-build --include-symbols -p:NuspecFile=project.nuspec --include-symbols -p:SymbolPackageFormat=snupkg -p:Configuration=%s -p:PackageId=%s -p:Version=%s -p:PackageVersion=%s -p:NuspecProperties=\"version=%s\" && " % (build_flavor, ctx.attr.id, ctx.attr.version, ctx.attr.version, ctx.attr.version) + \
"cp bin/%s/%s.%s.nupkg ../%s && " % (build_flavor, ctx.attr.id, ctx.attr.version, pkg.path) + \
"cp bin/%s/%s.%s.snupkg ../%s" % (build_flavor, ctx.attr.id, ctx.attr.version, symbols_pkg.path)
cmd = ctx.expand_location(
cmd,
targets = [
ctx.attr._zip,
],
)
ctx.actions.run_shell(
outputs = [pkg, symbols_pkg],
inputs = [
zip_file,
dotnet,
packages,
],
tools = [
ctx.executable._zip,
dotnet,
] + toolchain.default.files.to_list() + toolchain.runtime.default_runfiles.files.to_list() + toolchain.runtime.data_runfiles.files.to_list(),
command = cmd,
mnemonic = "CreateNupkg",
)
return [
DefaultInfo(
files = depset([pkg, symbols_pkg]),
runfiles = ctx.runfiles(files = [pkg, symbols_pkg]),
),
]
nuget_pack = rule(
nuget_pack_impl,
attrs = {
"id": attr.string(
doc = "Nuget ID of the package",
mandatory = True,
),
"version": attr.string(
mandatory = True,
),
"libs": attr.label_keyed_string_dict(
doc = "The .Net libraries that are being published",
providers = [DotnetAssemblyRuntimeInfo],
),
"files": attr.label_keyed_string_dict(
doc = "Mapping of files to paths within the nuget package",
allow_empty = True,
allow_files = True,
),
"property_group_vars": attr.string_dict(
doc = "Keys and values for variables declared in `PropertyGroup`s in the `csproj_file`",
allow_empty = True,
),
"nuspec_template": attr.label(
mandatory = True,
allow_single_file = True,
),
"_zip": attr.label(
default = "@bazel_tools//tools/zip:zipper",
executable = True,
cfg = "exec",
),
},
toolchains = ["@rules_dotnet//dotnet:toolchain_type"],
)