From 22f8f89405790e4f675a6e46e81db46c078fc2ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Purkh=C3=BAs?= Date: Wed, 6 Sep 2023 11:28:22 +0000 Subject: [PATCH] Simplify NuGet parsing (#370) ## What? Simplifies NuGet package parsing and fixes an error where `lib` files were not added as compile time dlls when there were no `refs` for the target framework. --- BUILD.bazel | 2 +- WORKSPACE | 5 +- dotnet/BUILD.bazel | 10 +- dotnet/defs.bzl | 30 +- dotnet/private/common.bzl | 2 +- dotnet/private/macros/register_tfms.bzl | 2 +- dotnet/private/rules/common/attrs.bzl | 2 +- dotnet/private/rules/common/binary.bzl | 8 +- dotnet/private/rules/csharp/binary.bzl | 6 +- dotnet/private/rules/csharp/library.bzl | 8 +- dotnet/private/rules/csharp/test.bzl | 4 +- dotnet/private/rules/fsharp/binary.bzl | 6 +- dotnet/private/rules/fsharp/library.bzl | 10 +- dotnet/private/rules/fsharp/test.bzl | 6 +- dotnet/private/rules/nuget/imports.bzl | 2 +- dotnet/private/rules/nuget/nuget_archive.bzl | 269 ++++++++++-------- .../rules/publish_binary/publish_binary.bzl | 4 +- .../private/tests/nuget_structure/BUILD.bazel | 2 +- .../private/tests/nuget_structure/common.bzl | 6 +- .../tests/nuget_structure/resolution.bzl | 2 +- .../tests/use_as_tool/csharp/BUILD.bazel | 2 +- .../tests/use_as_tool/fsharp/BUILD.bazel | 2 +- .../tests/warning_settings/BUILD.bazel | 2 +- .../warning_settings/csharp_warnings.bzl | 2 +- .../warning_settings/fsharp_warnings.bzl | 2 +- .../transitions/default_transition.bzl | 2 +- dotnet/private/transitions/tfm_transition.bzl | 2 +- 27 files changed, 209 insertions(+), 191 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 43cb295b..0df903ef 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,5 +1,5 @@ -load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@bazel_gazelle//:def.bzl", "gazelle", "gazelle_binary") +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") package(default_visibility = ["//visibility:public"]) diff --git a/WORKSPACE b/WORKSPACE index 8ad86460..6c99b09e 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,7 +1,7 @@ workspace(name = "rules_dotnet") -load(":internal_deps.bzl", "rules_dotnet_internal_deps") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load(":internal_deps.bzl", "rules_dotnet_internal_deps") # Fetch deps needed only locally for development rules_dotnet_internal_deps() @@ -40,10 +40,11 @@ load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") rules_pkg_dependencies() +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") + ############################################ # Gazelle, for generating bzl_library targets load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") -load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") go_rules_dependencies() diff --git a/dotnet/BUILD.bazel b/dotnet/BUILD.bazel index 90b55a06..5b5fdc84 100644 --- a/dotnet/BUILD.bazel +++ b/dotnet/BUILD.bazel @@ -1,13 +1,13 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") -load( - "//dotnet/private/macros:register_tfms.bzl", - "register_tfms", -) +load("//dotnet/private:resolved_toolchain.bzl", "resolved_toolchain") load( "//dotnet/private/macros:register_rids.bzl", "register_rids", ) -load("//dotnet/private:resolved_toolchain.bzl", "resolved_toolchain") +load( + "//dotnet/private/macros:register_tfms.bzl", + "register_tfms", +) toolchain_type( name = "toolchain_type", diff --git a/dotnet/defs.bzl b/dotnet/defs.bzl index 607d584d..1495b40b 100644 --- a/dotnet/defs.bzl +++ b/dotnet/defs.bzl @@ -8,34 +8,29 @@ load( _csharp_binary = "csharp_binary", ) load( - "@rules_dotnet//dotnet/private/rules/fsharp:binary.bzl", - _fsharp_binary = "fsharp_binary", + "@rules_dotnet//dotnet/private/rules/csharp:library.bzl", + _csharp_library = "csharp_library", ) load( - "@rules_dotnet//dotnet/private/rules/publish_binary:publish_binary.bzl", - _publish_binary = "publish_binary", - _publish_binary_wrapper = "publish_binary_wrapper", + "@rules_dotnet//dotnet/private/rules/csharp:nunit_test.bzl", + _csharp_nunit_test = "csharp_nunit_test", ) load( - "@rules_dotnet//dotnet/private/rules/csharp:library.bzl", - _csharp_library = "csharp_library", + "@rules_dotnet//dotnet/private/rules/csharp:test.bzl", + _csharp_test = "csharp_test", ) load( - "@rules_dotnet//dotnet/private/rules/fsharp:library.bzl", - _fsharp_library = "fsharp_library", + "@rules_dotnet//dotnet/private/rules/fsharp:binary.bzl", + _fsharp_binary = "fsharp_binary", ) load( - "@rules_dotnet//dotnet/private/rules/csharp:nunit_test.bzl", - _csharp_nunit_test = "csharp_nunit_test", + "@rules_dotnet//dotnet/private/rules/fsharp:library.bzl", + _fsharp_library = "fsharp_library", ) load( "@rules_dotnet//dotnet/private/rules/fsharp:nunit_test.bzl", _fsharp_nunit_test = "fsharp_nunit_test", ) -load( - "@rules_dotnet//dotnet/private/rules/csharp:test.bzl", - _csharp_test = "csharp_test", -) load( "@rules_dotnet//dotnet/private/rules/fsharp:test.bzl", _fsharp_test = "fsharp_test", @@ -53,6 +48,11 @@ load( "@rules_dotnet//dotnet/private/rules/nuget:nuget_repo.bzl", _nuget_repo = "nuget_repo", ) +load( + "@rules_dotnet//dotnet/private/rules/publish_binary:publish_binary.bzl", + _publish_binary = "publish_binary", + _publish_binary_wrapper = "publish_binary_wrapper", +) def _get_runtime_runtime_identifier(rid): if rid: diff --git a/dotnet/private/common.bzl b/dotnet/private/common.bzl index 6895360b..c62c8a3b 100644 --- a/dotnet/private/common.bzl +++ b/dotnet/private/common.bzl @@ -2,6 +2,7 @@ Rules for compatability resolution of dependencies for .NET frameworks. """ +load("@aspect_bazel_lib//lib:paths.bzl", "to_manifest_path") load("@bazel_skylib//lib:sets.bzl", "sets") load( "//dotnet/private:providers.bzl", @@ -10,7 +11,6 @@ load( "NuGetInfo", ) load("//dotnet/private:rids.bzl", "RUNTIME_GRAPH") -load("@aspect_bazel_lib//lib:paths.bzl", "to_manifest_path") def _collect_transitive(): t = {} diff --git a/dotnet/private/macros/register_tfms.bzl b/dotnet/private/macros/register_tfms.bzl index 4cccaec7..f1465543 100644 --- a/dotnet/private/macros/register_tfms.bzl +++ b/dotnet/private/macros/register_tfms.bzl @@ -1,7 +1,7 @@ "Register TFM flags and set up the compatibility chains" -load("@bazel_skylib//lib:sets.bzl", "sets") load("@bazel_skylib//lib:dicts.bzl", "dicts") +load("@bazel_skylib//lib:sets.bzl", "sets") load("@bazel_skylib//rules:common_settings.bzl", "bool_setting", "string_flag") load( "//dotnet/private:common.bzl", diff --git a/dotnet/private/rules/common/attrs.bzl b/dotnet/private/rules/common/attrs.bzl index f5be2c01..869e82c2 100644 --- a/dotnet/private/rules/common/attrs.bzl +++ b/dotnet/private/rules/common/attrs.bzl @@ -2,8 +2,8 @@ load("@bazel_skylib//lib:dicts.bzl", "dicts") load("//dotnet/private:providers.bzl", "DotnetAssemblyInfo") -load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") load("//dotnet/private/transitions:default_transition.bzl", "default_transition") +load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") # These are attributes that are common across all the binary/library/test .Net rules COMMON_ATTRS = { diff --git a/dotnet/private/rules/common/binary.bzl b/dotnet/private/rules/common/binary.bzl index 4d589873..5e6c6a68 100644 --- a/dotnet/private/rules/common/binary.bzl +++ b/dotnet/private/rules/common/binary.bzl @@ -2,7 +2,9 @@ Base rule for building .Net binaries """ -load("//dotnet/private:providers.bzl", "DotnetBinaryInfo") +load("@aspect_bazel_lib//lib:paths.bzl", "to_manifest_path") +load("@bazel_skylib//lib:paths.bzl", "paths") +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load( "//dotnet/private:common.bzl", "generate_depsjson", @@ -10,9 +12,7 @@ load( "is_core_framework", "is_standard_framework", ) -load("@bazel_skylib//lib:paths.bzl", "paths") -load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") -load("@aspect_bazel_lib//lib:paths.bzl", "to_manifest_path") +load("//dotnet/private:providers.bzl", "DotnetBinaryInfo") def _create_shim_exe(ctx, dll): windows_constraint = ctx.attr._windows_constraint[platform_common.ConstraintValueInfo] diff --git a/dotnet/private/rules/csharp/binary.bzl b/dotnet/private/rules/csharp/binary.bzl index b7d9edc0..5da9a7eb 100644 --- a/dotnet/private/rules/csharp/binary.bzl +++ b/dotnet/private/rules/csharp/binary.bzl @@ -2,16 +2,16 @@ Rule for compiling C# binaries. """ -load("//dotnet/private/rules/csharp/actions:csharp_assembly.bzl", "AssemblyAction") +load("@bazel_skylib//lib:dicts.bzl", "dicts") load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load( "//dotnet/private:common.bzl", "is_debug", ) -load("//dotnet/private/rules/common:binary.bzl", "build_binary") load("//dotnet/private/rules/common:attrs.bzl", "CSHARP_BINARY_COMMON_ATTRS") +load("//dotnet/private/rules/common:binary.bzl", "build_binary") +load("//dotnet/private/rules/csharp/actions:csharp_assembly.bzl", "AssemblyAction") load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") -load("@bazel_skylib//lib:dicts.bzl", "dicts") def _compile_action(ctx, tfm): toolchain = ctx.toolchains["@rules_dotnet//dotnet:toolchain_type"] diff --git a/dotnet/private/rules/csharp/library.bzl b/dotnet/private/rules/csharp/library.bzl index e40d6f91..73ecbcbc 100644 --- a/dotnet/private/rules/csharp/library.bzl +++ b/dotnet/private/rules/csharp/library.bzl @@ -2,15 +2,15 @@ Rule for compiling C# libraries. """ -load("//dotnet/private/rules/csharp/actions:csharp_assembly.bzl", "AssemblyAction") load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") -load("//dotnet/private/rules/common:library.bzl", "build_library") -load("//dotnet/private/rules/common:attrs.bzl", "CSHARP_LIBRARY_COMMON_ATTRS") -load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") load( "//dotnet/private:common.bzl", "is_debug", ) +load("//dotnet/private/rules/common:attrs.bzl", "CSHARP_LIBRARY_COMMON_ATTRS") +load("//dotnet/private/rules/common:library.bzl", "build_library") +load("//dotnet/private/rules/csharp/actions:csharp_assembly.bzl", "AssemblyAction") +load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") def _compile_action(ctx, tfm): toolchain = ctx.toolchains["@rules_dotnet//dotnet:toolchain_type"] diff --git a/dotnet/private/rules/csharp/test.bzl b/dotnet/private/rules/csharp/test.bzl index 81620c05..6c027a9f 100644 --- a/dotnet/private/rules/csharp/test.bzl +++ b/dotnet/private/rules/csharp/test.bzl @@ -5,14 +5,14 @@ This rule can be used to compile and run any C# binary and run it as a Bazel test. """ -load("//dotnet/private/rules/csharp/actions:csharp_assembly.bzl", "AssemblyAction") load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load( "//dotnet/private:common.bzl", "is_debug", ) -load("//dotnet/private/rules/common:binary.bzl", "build_binary") load("//dotnet/private/rules/common:attrs.bzl", "CSHARP_BINARY_COMMON_ATTRS") +load("//dotnet/private/rules/common:binary.bzl", "build_binary") +load("//dotnet/private/rules/csharp/actions:csharp_assembly.bzl", "AssemblyAction") load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") def _compile_action(ctx, tfm): diff --git a/dotnet/private/rules/fsharp/binary.bzl b/dotnet/private/rules/fsharp/binary.bzl index 77583964..7759e0b4 100644 --- a/dotnet/private/rules/fsharp/binary.bzl +++ b/dotnet/private/rules/fsharp/binary.bzl @@ -2,15 +2,15 @@ Rule for compiling F# binaries. """ -load("//dotnet/private/rules/fsharp/actions:fsharp_assembly.bzl", "AssemblyAction") +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load( "//dotnet/private:common.bzl", "is_debug", ) -load("//dotnet/private/rules/common:binary.bzl", "build_binary") load("//dotnet/private/rules/common:attrs.bzl", "FSHARP_BINARY_COMMON_ATTRS") +load("//dotnet/private/rules/common:binary.bzl", "build_binary") +load("//dotnet/private/rules/fsharp/actions:fsharp_assembly.bzl", "AssemblyAction") load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") -load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") def _compile_action(ctx, tfm): toolchain = ctx.toolchains["@rules_dotnet//dotnet:toolchain_type"] diff --git a/dotnet/private/rules/fsharp/library.bzl b/dotnet/private/rules/fsharp/library.bzl index af9de994..9dc660eb 100644 --- a/dotnet/private/rules/fsharp/library.bzl +++ b/dotnet/private/rules/fsharp/library.bzl @@ -2,15 +2,15 @@ Rule for compiling F# libraries. """ -load("//dotnet/private/rules/fsharp/actions:fsharp_assembly.bzl", "AssemblyAction") -load("//dotnet/private/rules/common:library.bzl", "build_library") -load("//dotnet/private/rules/common:attrs.bzl", "FSHARP_LIBRARY_COMMON_ATTRS") -load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load( "//dotnet/private:common.bzl", "is_debug", ) -load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") +load("//dotnet/private/rules/common:attrs.bzl", "FSHARP_LIBRARY_COMMON_ATTRS") +load("//dotnet/private/rules/common:library.bzl", "build_library") +load("//dotnet/private/rules/fsharp/actions:fsharp_assembly.bzl", "AssemblyAction") +load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") def _compile_action(ctx, tfm): toolchain = ctx.toolchains["@rules_dotnet//dotnet:toolchain_type"] diff --git a/dotnet/private/rules/fsharp/test.bzl b/dotnet/private/rules/fsharp/test.bzl index 136096ce..524cd3a9 100644 --- a/dotnet/private/rules/fsharp/test.bzl +++ b/dotnet/private/rules/fsharp/test.bzl @@ -5,15 +5,15 @@ This rule can be used to compile and run any F# binary and run it as a Bazel test. """ -load("//dotnet/private/rules/fsharp/actions:fsharp_assembly.bzl", "AssemblyAction") +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load( "//dotnet/private:common.bzl", "is_debug", ) -load("//dotnet/private/rules/common:binary.bzl", "build_binary") load("//dotnet/private/rules/common:attrs.bzl", "FSHARP_BINARY_COMMON_ATTRS") +load("//dotnet/private/rules/common:binary.bzl", "build_binary") +load("//dotnet/private/rules/fsharp/actions:fsharp_assembly.bzl", "AssemblyAction") load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") -load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") def _compile_action(ctx, tfm): toolchain = ctx.toolchains["@rules_dotnet//dotnet:toolchain_type"] diff --git a/dotnet/private/rules/nuget/imports.bzl b/dotnet/private/rules/nuget/imports.bzl index bfa17688..248cd6c5 100644 --- a/dotnet/private/rules/nuget/imports.bzl +++ b/dotnet/private/rules/nuget/imports.bzl @@ -2,13 +2,13 @@ Rules for importing assemblies for .NET frameworks. """ +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load( "//dotnet/private:common.bzl", "collect_transitive_info", "transform_deps", ) load("//dotnet/private:providers.bzl", "DotnetAssemblyInfo", "NuGetInfo") -load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") def _import_library(ctx): ( diff --git a/dotnet/private/rules/nuget/nuget_archive.bzl b/dotnet/private/rules/nuget/nuget_archive.bzl index 71170cc7..f4d53009 100644 --- a/dotnet/private/rules/nuget/nuget_archive.bzl +++ b/dotnet/private/rules/nuget/nuget_archive.bzl @@ -51,7 +51,7 @@ def _read_dir(repository_ctx, src_dir): return result def _create_framework_select(name, group): - if not group: + if len(group) == 0: return None result = ["tfm_filegroup(\"%s\", {\n" % name] @@ -68,7 +68,7 @@ def _create_framework_select(name, group): return "".join(result) def _create_rid_native_select(name, group): - if not group: + if len(group) == 0: return None result = ["rid_filegroup(\"%s\", {\n" % name] @@ -77,7 +77,7 @@ def _create_rid_native_select(name, group): result.append(' "') result.append(rid) result.append('": [') - result.append(",".join(["\n \"{}\"".format(item) for item in items["native"]])) + result.append(",".join(["\n \"{}\"".format(item) for item in items])) result.append("],\n") result.append("})") @@ -91,50 +91,24 @@ def _sanitize_path(file_path): return file_path -def _process_lib_file(groups, file): - i = file.find("/") - tfm_start = i + 1 - tfm_end = file.find("/", i + 1) - tfm = file[tfm_start:tfm_end] - +# We have encountered some packages that have non-standard TFM names +# and we replace the non-standard names with the standard names here +def _replace_non_standard_tfm(tfm): if tfm == "netstandard20": - tfm = "netstandard2.0" + return "netstandard2.0" if tfm == "netstandard21": - tfm = "netstandard2.1" - - if tfm not in FRAMEWORK_COMPATIBILITY: - return - - # If the folder is empty we do nothing - if file.find("/", tfm_end + 1) != -1: - return - - if not groups.get("lib"): - groups["lib"] = {} - - group = groups["lib"] - - if not group.get(tfm): - group[tfm] = [] - - # If the folder contains a _._ file we create the group but do not add the file to it - # to indicate that there was an _._ file in the folder. - if file.endswith("_._"): - return - - if not file.endswith(".dll") or file.endswith(".resources.dll"): - return - - group[tfm].append(file) + return "netstandard2.1" - return + return tfm -def _process_ref_file(groups, file): +# This function processes a package file that has the following format: +# // +def _process_group_with_tfm(groups, group_name, file): i = file.find("/") tfm_start = i + 1 tfm_end = file.find("/", i + 1) - tfm = file[tfm_start:tfm_end] + tfm = _replace_non_standard_tfm(file[tfm_start:tfm_end]) if tfm not in FRAMEWORK_COMPATIBILITY: return @@ -143,16 +117,14 @@ def _process_ref_file(groups, file): if file.find("/", tfm_end + 1) != -1: return - if not groups.get("ref"): - groups["ref"] = {} - - group = groups["ref"] + group = groups[group_name] if not group.get(tfm): group[tfm] = [] # If the folder contains a _._ file we create the group but do not add the file to it - # to indicate that there was an _._ file in the folder. + # to indicate that there was an _._ file in the folder. The file indicates that the + # package is compatible with the TFM. if file.endswith("_._"): return @@ -164,7 +136,6 @@ def _process_ref_file(groups, file): return def _process_build_file(groups, file): - # See: https://learn.microsoft.com/en-us/nuget/concepts/msbuild-props-and-targets#framework-specific-build-folder i = file.find("/") tfm_start = i + 1 tfm_end = file.find("/", i + 1) @@ -177,48 +148,23 @@ def _process_build_file(groups, file): if not file.endswith(".dll") or file.endswith(".resources.dll"): return - if file_type == "ref": - if not groups.get("ref"): - groups["ref"] = {} - - group = groups["ref"] + group = groups["build"] - if not group.get(tfm): - group[tfm] = [] + if not group.get(tfm): + group[tfm] = { + "lib": [], + "ref": [], + } - group[tfm].append(file) + if file_type == "ref": + group[tfm]["ref"].append(file) if file_type == "lib": - if not groups.get("lib"): - groups["lib"] = {} - - group = groups["lib"] - - if not group.get(tfm): - group[tfm] = [] - - group[tfm].append(file) - - return - -def _process_analyzer_file(groups, file): - if (not file.endswith(".dll")) or file.endswith("resources.dll"): - return - - group = groups["analyzers"] - group["dotnet"].append(file) - - return - -def _process_content_file(groups, file): - group = groups["contentFiles"] - group["any"].append(file) + group[tfm]["lib"].append(file) return def _process_typeprovider_file(groups, file): - # See https://github.com/fsharp/fslang-design/blob/main/tooling/FST-1003-loading-type-provider-design-time-components.md - if not file.endswith(".dll"): return @@ -232,10 +178,7 @@ def _process_typeprovider_file(groups, file): if tfm not in FRAMEWORK_COMPATIBILITY: return - if not groups.get("lib"): - groups["lib"] = {} - - group = groups["lib"] + group = groups["typeproviders"] if not group.get(tfm): group[tfm] = [] @@ -244,8 +187,22 @@ def _process_typeprovider_file(groups, file): return +def _process_analyzer_file(groups, file): + if (not file.endswith(".dll")) or file.endswith("resources.dll"): + return + + group = groups["analyzers"] + group["dotnet"].append(file) + + return + +def _process_content_file(groups, file): + group = groups["contentFiles"] + group["any"].append(file) + + return + def _process_runtimes_file(groups, file): - # See https://docs.microsoft.com/en-us/nuget/create-packages/supporting-multiple-target-frameworks#architecture-specific-folders parts = file.split("/") if len(parts) < 2: @@ -271,44 +228,27 @@ def _process_runtimes_file(groups, file): group[rid]["native"].append(file) if parts[2] == "lib": - # If there are TFM specific folders under the runtimes folder - # we add the files in those folders to the lib group with the - # TFM set to a combination of the TFM and RID. That way the - # RID specific lib files will be picked if the RID is part of the - # TFM constraint. tfm = parts[3] if tfm not in FRAMEWORK_COMPATIBILITY: return - combined_tfm_and_rid = "{}_{}".format(tfm, rid) - - if not groups.get("lib"): - groups["lib"] = {} - - lib_group = groups["lib"] - - if not lib_group.get(combined_tfm_and_rid): - lib_group[combined_tfm_and_rid] = [] - - # If the folder contains a _._ file we create the group but do not add the file to it - # to indicate that there was an _._ file in the folder. - if file.endswith("_._"): - return - if not file.endswith(".dll") or file.endswith(".resources.dll"): return - lib_group[combined_tfm_and_rid].append(file) + if not group[rid]["lib"].get(tfm): + group[rid]["lib"][tfm] = [] + + group[rid]["lib"][tfm].append(file) return def _process_key_and_file(groups, key, file): # todo resource dlls if key == "lib": - _process_lib_file(groups, file) + _process_group_with_tfm(groups, key, file) elif key == "ref": - _process_ref_file(groups, file) + _process_group_with_tfm(groups, key, file) elif key == "analyzers": _process_analyzer_file(groups, file) elif key == "contentFiles": @@ -384,13 +324,36 @@ def _nuget_archive_impl(ctx): files = _read_dir(ctx, ".").replace(str(ctx.path(".")) + "/", "").splitlines() + # The NuGet package format groups = { + # See https://learn.microsoft.com/en-us/nuget/guides/analyzers-conventions + # Example: analyzers/dotnet/cs/System.Runtime.CSharp.Analyzers.dll + # NB: The analyzers supports is not fully implemented yet "analyzers": { "dotnet": [], }, + # See: https://devblogs.microsoft.com/nuget/nuget-contentfiles-demystified/ + # NB: Only the any group is supported at the moment "contentFiles": { "any": [], }, + # Format: lib//.dll + "lib": {}, + # Format: ref//.dll + "ref": {}, + # See https://github.com/fsharp/fslang-design/blob/main/tooling/FST-1003-loading-type-provider-design-time-components.md + # Format: typeproviders//.dll + "typeproviders": {}, + # See https://docs.microsoft.com/en-us/nuget/create-packages/supporting-multiple-target-frameworks#architecture-specific-folders + # Format: runtimes//native/.dll OR runtimes//lib//.dll + "runtimes": { + }, + # See: https://learn.microsoft.com/en-us/nuget/concepts/msbuild-props-and-targets#framework-specific-build-folder + # Format: build//ref/.dll OR build//lib/.dll + # NB: This folder could be tricky to support globally because packages can bring MSBuild targets with them and we don't support that + # currently we just blindly add the files to the lib or ref group depending on the folder name + "build": { + }, } for file in files: @@ -400,34 +363,88 @@ def _nuget_archive_impl(ctx): _process_key_and_file(groups, key, file) - if groups.get("ref") and groups.get("lib"): - libs = groups.get("lib") - refs = groups.get("ref") + # Now that we have processed all the files we need to make sure that they are correctly set to be + # either a runtime dependency or a compile time dependency. Dependency resolution in .Net is fun! - # in some runtime specific edge cases there exist certain tfm refs but the libs are not shipped - for (tfm, _) in groups.get("ref").items(): - if tfm not in libs: - libs[tfm] = [] - - # We add an empty entry for each tfm that does not exist in the refs - # because that way the generated select statement will contain all the - # supported tfms and thus we can omit an default case in the select which - # would make the build graph more fragile and harder to debug since instead - # of Bazel complaining that a some tfm is not supported for a package you would - # possibly get an error during compilation - for (tfm, _) in groups.get("lib").items(): + ########################################## + # Let's start with the compile time dlls + # The rules are like this: + # - If there are dlls for a TFM in the ref folder then they are a compile time dll + # - If there are dlls for a TFM in the lib folder but not in the ref folder we know that + # the package is compatible with that TFM and the lib entry should be used as a compile time dll + # - If there are dlls for a TFM in the ref folder under the build folder then they are a compile time dll + ########################################## + refs = {} + + for (tfm, files) in groups.get("ref").items(): + refs[tfm] = files + + for (tfm, files) in groups.get("lib").items(): + if tfm not in refs: + refs[tfm] = files + + for (tfm, ref_or_lib) in groups.get("build").items(): + if ref_or_lib.get("ref"): if tfm not in refs: - refs[tfm] = [] + refs[tfm] = ref_or_lib.get("ref") + else: + refs[tfm].extend(ref_or_lib.get("ref")) + + ###################################### + # Now we move on to the runtime dlls + # The rules are like this: + # - If there are dlls for a TFM in the lib folder they are a runtime dll + # - If there are dlls for a TFM in the ref folder but not in the lib folder we know that the + # package supports the TFM so we need to create an empty configuration for the TFM since + # files in the ref folder are never runtime dependencies + # - If there are dlls for a TFM in the the typeproviders folder they are a runtime dll + # - If there are dlls for a TFM in the the build folder they are a runtime dll + # - If there are dlls for a TFM in the runtimes folder they are a runtime dll and should + # replace the dll in the toplevel lib folder for a the TFM and RID combination + # - If there are files for an RID in the native folder under runtimes then they are runtime dependencies + # when the target platform is compatible with the RID + ##################################### + libs = {} + native = {} + + for (tfm, files) in groups.get("lib").items(): + libs[tfm] = files + + for (tfm, _) in groups.get("ref").items(): + if tfm not in libs: + libs[tfm] = [] + + for (tfm, files) in groups.get("typeproviders").items(): + if libs.get(tfm): + libs[tfm].extend(files) + else: + libs[tfm] = files + + for (tfm, ref_or_lib) in groups.get("build").items(): + if ref_or_lib.get("lib"): + if tfm not in libs: + libs[tfm] = ref_or_lib.get("lib") + else: + libs[tfm].extend(ref_or_lib.get("lib")) + + for (rid, files_for_rid) in groups.get("runtimes").items(): + native[rid] = files_for_rid.get("native") + for (tfm, tfm_files) in files_for_rid.get("lib").items(): + # We create a combined TFM and RID entry for the lib files + # This entry will only be matched when the Bazel configuration + # is configured to this exact RID and TFM combination + combined_tfm_and_rid = "{}_{}".format(tfm, rid) + libs[combined_tfm_and_rid] = tfm_files ctx.file("BUILD.bazel", r"""package(default_visibility = ["//visibility:public"]) exports_files(glob(["**"])) load("@rules_dotnet//dotnet/private/rules/nuget:nuget_archive.bzl", "tfm_filegroup", "rid_filegroup") """ + "\n".join([ - _create_framework_select("libs", groups.get("lib")) or "filegroup(name = \"libs\", srcs = [])", - _create_framework_select("refs", groups.get("ref")) or _create_framework_select("refs", groups.get("lib")) or "filegroup(name = \"refs\", srcs = [])", + _create_framework_select("libs", libs) or "filegroup(name = \"libs\", srcs = [])", + _create_framework_select("refs", refs) or "filegroup(name = \"refs\", srcs = [])", "filegroup(name = \"analyzers\", srcs = [%s])" % ",".join(["\n \"%s\"" % a for a in groups.get("analyzers")["dotnet"]]), "filegroup(name = \"data\", srcs = [])", - _create_rid_native_select("native", groups.get("runtimes")) or "filegroup(name = \"native\", srcs = [])", + _create_rid_native_select("native", native) or "filegroup(name = \"native\", srcs = [])", "filegroup(name = \"content_files\", srcs = [%s])" % ",".join(["\n \"%s\"" % a for a in groups.get("contentFiles")["any"]]), ])) diff --git a/dotnet/private/rules/publish_binary/publish_binary.bzl b/dotnet/private/rules/publish_binary/publish_binary.bzl index e923a1f2..d426c825 100644 --- a/dotnet/private/rules/publish_binary/publish_binary.bzl +++ b/dotnet/private/rules/publish_binary/publish_binary.bzl @@ -2,11 +2,11 @@ Rules for compiling F# binaries. """ +load("@aspect_bazel_lib//lib:paths.bzl", "to_manifest_path") load("@bazel_skylib//lib:shell.bzl", "shell") +load("//dotnet/private:common.bzl", "generate_depsjson", "generate_runtimeconfig") load("//dotnet/private:providers.bzl", "DotnetAssemblyInfo", "DotnetBinaryInfo", "DotnetPublishBinaryInfo") load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") -load("//dotnet/private:common.bzl", "generate_depsjson", "generate_runtimeconfig") -load("@aspect_bazel_lib//lib:paths.bzl", "to_manifest_path") def _publish_binary_impl(ctx): runtime_pack_infos = [] diff --git a/dotnet/private/tests/nuget_structure/BUILD.bazel b/dotnet/private/tests/nuget_structure/BUILD.bazel index 9e270d0e..72ca699a 100644 --- a/dotnet/private/tests/nuget_structure/BUILD.bazel +++ b/dotnet/private/tests/nuget_structure/BUILD.bazel @@ -1,7 +1,7 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load(":resolution.bzl", "resolution_structure") load(":runtimes.bzl", "runtimes_structure") load(":typeproviders.bzl", "typeproviders_structure") -load(":resolution.bzl", "resolution_structure") typeproviders_structure() diff --git a/dotnet/private/tests/nuget_structure/common.bzl b/dotnet/private/tests/nuget_structure/common.bzl index 7b9c0673..dc41dff2 100644 --- a/dotnet/private/tests/nuget_structure/common.bzl +++ b/dotnet/private/tests/nuget_structure/common.bzl @@ -3,13 +3,13 @@ load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") # buildifier: disable=bzl-visibility -load("//dotnet/private:providers.bzl", "DotnetAssemblyInfo", "NuGetInfo") +load("//dotnet/private:common.bzl", "get_nuget_relative_path") # buildifier: disable=bzl-visibility -load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") +load("//dotnet/private:providers.bzl", "DotnetAssemblyInfo", "NuGetInfo") # buildifier: disable=bzl-visibility -load("//dotnet/private:common.bzl", "get_nuget_relative_path") +load("//dotnet/private/transitions:tfm_transition.bzl", "tfm_transition") def _get_nuget_relative_paths(files): return [get_nuget_relative_path(file) for file in files] diff --git a/dotnet/private/tests/nuget_structure/resolution.bzl b/dotnet/private/tests/nuget_structure/resolution.bzl index df5911af..7954ff0f 100644 --- a/dotnet/private/tests/nuget_structure/resolution.bzl +++ b/dotnet/private/tests/nuget_structure/resolution.bzl @@ -16,5 +16,5 @@ def resolution_structure(): name = "should_resolve_system.memory_netstandard2.0_linux-x64_correctly", target_under_test = ":System.Memory.netstandard2.0", expected_libs = ["lib/netstandard2.0/System.Memory.dll"], - expected_refs = [], + expected_refs = ["lib/netstandard2.0/System.Memory.dll"], ) diff --git a/dotnet/private/tests/use_as_tool/csharp/BUILD.bazel b/dotnet/private/tests/use_as_tool/csharp/BUILD.bazel index ceb8f772..6fa4de13 100644 --- a/dotnet/private/tests/use_as_tool/csharp/BUILD.bazel +++ b/dotnet/private/tests/use_as_tool/csharp/BUILD.bazel @@ -2,6 +2,7 @@ """ +load("@aspect_bazel_lib//lib:testing.bzl", "assert_contains") load( "@rules_dotnet//dotnet:defs.bzl", "csharp_binary", @@ -10,7 +11,6 @@ load( ) load("@rules_dotnet//dotnet/private/tests/use_as_tool/run_rule:run_rule.bzl", "run_rule") load("@rules_dotnet//dotnet/private/tests/use_as_tool/run_shell_rule:run_shell_rule.bzl", "run_shell_rule") -load("@aspect_bazel_lib//lib:testing.bzl", "assert_contains") # Test that normal binary targets work as tools csharp_binary( diff --git a/dotnet/private/tests/use_as_tool/fsharp/BUILD.bazel b/dotnet/private/tests/use_as_tool/fsharp/BUILD.bazel index 9de2fe1a..98cbaf71 100644 --- a/dotnet/private/tests/use_as_tool/fsharp/BUILD.bazel +++ b/dotnet/private/tests/use_as_tool/fsharp/BUILD.bazel @@ -2,6 +2,7 @@ """ +load("@aspect_bazel_lib//lib:testing.bzl", "assert_contains") load( "@rules_dotnet//dotnet:defs.bzl", "fsharp_binary", @@ -10,7 +11,6 @@ load( ) load("@rules_dotnet//dotnet/private/tests/use_as_tool/run_rule:run_rule.bzl", "run_rule") load("@rules_dotnet//dotnet/private/tests/use_as_tool/run_shell_rule:run_shell_rule.bzl", "run_shell_rule") -load("@aspect_bazel_lib//lib:testing.bzl", "assert_contains") # Test that normal binary targets work as tools fsharp_binary( diff --git a/dotnet/private/tests/warning_settings/BUILD.bazel b/dotnet/private/tests/warning_settings/BUILD.bazel index 6fcb236e..b8250b24 100644 --- a/dotnet/private/tests/warning_settings/BUILD.bazel +++ b/dotnet/private/tests/warning_settings/BUILD.bazel @@ -1,6 +1,6 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") -load(":fsharp_warnings.bzl", "fsharp_warnings") load(":csharp_warnings.bzl", "csharp_warnings") +load(":fsharp_warnings.bzl", "fsharp_warnings") fsharp_warnings() diff --git a/dotnet/private/tests/warning_settings/csharp_warnings.bzl b/dotnet/private/tests/warning_settings/csharp_warnings.bzl index 636d9b02..1716068d 100644 --- a/dotnet/private/tests/warning_settings/csharp_warnings.bzl +++ b/dotnet/private/tests/warning_settings/csharp_warnings.bzl @@ -1,7 +1,7 @@ "C# Warning settings" -load("//dotnet:defs.bzl", "csharp_library") load("@bazel_skylib//lib:unittest.bzl", "analysistest") +load("//dotnet:defs.bzl", "csharp_library") load("//dotnet/private/tests:utils.bzl", "ACTION_ARGS_TEST_ARGS", "action_args_test", "action_args_test_impl") csharp_treat_warnings_as_errors_config_wrapper_test = analysistest.make( diff --git a/dotnet/private/tests/warning_settings/fsharp_warnings.bzl b/dotnet/private/tests/warning_settings/fsharp_warnings.bzl index 8bcd51ad..93ae0ef1 100644 --- a/dotnet/private/tests/warning_settings/fsharp_warnings.bzl +++ b/dotnet/private/tests/warning_settings/fsharp_warnings.bzl @@ -1,7 +1,7 @@ "F# Warning settings" -load("//dotnet:defs.bzl", "fsharp_library") load("@bazel_skylib//lib:unittest.bzl", "analysistest") +load("//dotnet:defs.bzl", "fsharp_library") load("//dotnet/private/tests:utils.bzl", "ACTION_ARGS_TEST_ARGS", "action_args_test", "action_args_test_impl") fsharp_treat_warnings_as_errors_config_wrapper_test = analysistest.make( diff --git a/dotnet/private/transitions/default_transition.bzl b/dotnet/private/transitions/default_transition.bzl index b6adcdeb..37f9dd67 100644 --- a/dotnet/private/transitions/default_transition.bzl +++ b/dotnet/private/transitions/default_transition.bzl @@ -15,8 +15,8 @@ load( "DEFAULT_TFM", "FRAMEWORK_COMPATIBILITY", ) -load("//dotnet/private/transitions:common.bzl", "FRAMEWORK_COMPATABILITY_TRANSITION_OUTPUTS", "RID_COMPATABILITY_TRANSITION_OUTPUTS") load("//dotnet/private:rids.bzl", "RUNTIME_GRAPH") +load("//dotnet/private/transitions:common.bzl", "FRAMEWORK_COMPATABILITY_TRANSITION_OUTPUTS", "RID_COMPATABILITY_TRANSITION_OUTPUTS") def _impl(settings, _attr): incoming_tfm = settings["@rules_dotnet//dotnet:target_framework"] diff --git a/dotnet/private/transitions/tfm_transition.bzl b/dotnet/private/transitions/tfm_transition.bzl index a191df85..83564348 100644 --- a/dotnet/private/transitions/tfm_transition.bzl +++ b/dotnet/private/transitions/tfm_transition.bzl @@ -6,8 +6,8 @@ load( "FRAMEWORK_COMPATIBILITY", "get_highest_compatible_target_framework", ) -load("//dotnet/private/transitions:common.bzl", "FRAMEWORK_COMPATABILITY_TRANSITION_OUTPUTS", "RID_COMPATABILITY_TRANSITION_OUTPUTS") load("//dotnet/private:rids.bzl", "RUNTIME_GRAPH") +load("//dotnet/private/transitions:common.bzl", "FRAMEWORK_COMPATABILITY_TRANSITION_OUTPUTS", "RID_COMPATABILITY_TRANSITION_OUTPUTS") def _impl(settings, attr): incoming_tfm = settings["@rules_dotnet//dotnet:target_framework"]