Skip to content

[SymbolGraphGen] add flags to filter platforms out of availability metadata #80778

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1774,6 +1774,16 @@ def symbol_graph_minimum_access_level: Separate<["-"], "symbol-graph-minimum-acc
HelpText<"Include symbols with this access level or more when emitting a symbol graph">,
MetaVarName<"<level>">;

def symbol_graph_allow_availability_platforms: Separate<["-"], "symbol-graph-allow-availability-platforms">,
Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput, HelpHidden]>,
HelpText<"Restrict availability metadata to the given platforms, e.g. 'macOS,Swift'">,
MetaVarName<"<platforms>">;

def symbol_graph_block_availability_platforms: Separate<["-"], "symbol-graph-block-availability-platforms">,
Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput, HelpHidden]>,
HelpText<"Remove the given platforms from symbol graph availability metadata, e.g. 'macOS,Swift'">,
MetaVarName<"<platforms>">;

def pretty_print: Flag<["-"], "pretty-print">,
Flags<[SwiftSymbolGraphExtractOption]>,
HelpText<"Pretty-print the output JSON">;
Expand All @@ -1788,6 +1798,16 @@ def omit_extension_block_symbols: Flag<["-"], "omit-extension-block-symbols">,
NoInteractiveOption, SupplementaryOutput, HelpHidden]>,
HelpText<"Directly associate members and conformances with the extended nominal when generating symbol graphs instead of emitting 'swift.extension' symbols for extensions to external types">;

def allow_availability_platforms: Separate<["-"], "allow-availability-platforms">,
Flags<[SwiftSymbolGraphExtractOption]>,
HelpText<"Restrict availability metadata to the given platforms, e.g. 'macOS,Swift'">,
MetaVarName<"<platforms>">;

def block_availability_platforms: Separate<["-"], "block-availability-platforms">,
Flags<[SwiftSymbolGraphExtractOption]>,
HelpText<"Remove the given platforms from symbol graph availability metadata, e.g. 'macOS,Swift'">,
MetaVarName<"<platforms>">;

// swift-synthesize-interface-only options
def include_submodules : Flag<["-"], "include-submodules">,
Flags<[NoDriverOption, SwiftSynthesizeInterfaceOption]>,
Expand Down
10 changes: 9 additions & 1 deletion include/swift/SymbolGraphGen/SymbolGraphOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
//
//===----------------------------------------------------------------------===//

#include "llvm/TargetParser/Triple.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/TargetParser/Triple.h"

#include "swift/AST/AttrKind.h"

Expand Down Expand Up @@ -69,6 +70,13 @@ struct SymbolGraphOptions {
/// If this has a value specifies an explicit allow list of reexported module
/// names that should be included symbol graph.
std::optional<llvm::ArrayRef<StringRef>> AllowedReexportedModules = {};

/// If set, a list of availability platforms to restrict (or block) when
/// rendering symbol graphs.
std::optional<llvm::DenseSet<StringRef>> AvailabilityPlatforms = {};

/// Whether `AvailabilityPlatforms` is an allow list or a block list.
bool AvailabilityIsBlockList = false;
};

} // end namespace symbolgraphgen
Expand Down
19 changes: 19 additions & 0 deletions lib/DriverTool/swift_symbolgraph_extract_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,25 @@ int swift_symbolgraph_extract_main(ArrayRef<const char *> Args,
.Default(AccessLevel::Public);
}

if (auto *A = ParsedArgs.getLastArg(OPT_allow_availability_platforms)) {
llvm::SmallVector<StringRef> AvailabilityPlatforms;
StringRef(A->getValue())
.split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1,
/*KeepEmpty*/ false);
Options.AvailabilityPlatforms = llvm::DenseSet<StringRef>(
AvailabilityPlatforms.begin(), AvailabilityPlatforms.end());
Options.AvailabilityIsBlockList = false;
} else if (auto *A =
ParsedArgs.getLastArg(OPT_block_availability_platforms)) {
llvm::SmallVector<StringRef> AvailabilityPlatforms;
StringRef(A->getValue())
.split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1,
/*KeepEmpty*/ false);
Options.AvailabilityPlatforms = llvm::DenseSet<StringRef>(
AvailabilityPlatforms.begin(), AvailabilityPlatforms.end());
Options.AvailabilityIsBlockList = true;
}

Invocation.getLangOptions().setCxxInteropFromArgs(ParsedArgs, Diags);

std::string InstanceSetupError;
Expand Down
19 changes: 19 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2221,6 +2221,25 @@ static void ParseSymbolGraphArgs(symbolgraphgen::SymbolGraphOptions &Opts,
Opts.MinimumAccessLevel = AccessLevel::Public;
}

if (auto *A = Args.getLastArg(OPT_symbol_graph_allow_availability_platforms)) {
llvm::SmallVector<StringRef> AvailabilityPlatforms;
StringRef(A->getValue())
.split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1,
/*KeepEmpty*/ false);
Opts.AvailabilityPlatforms = llvm::DenseSet<StringRef>(
AvailabilityPlatforms.begin(), AvailabilityPlatforms.end());
Opts.AvailabilityIsBlockList = false;
} else if (auto *A = Args.getLastArg(
OPT_symbol_graph_block_availability_platforms)) {
llvm::SmallVector<StringRef> AvailabilityPlatforms;
StringRef(A->getValue())
.split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1,
/*KeepEmpty*/ false);
Opts.AvailabilityPlatforms = llvm::DenseSet<StringRef>(
AvailabilityPlatforms.begin(), AvailabilityPlatforms.end());
Opts.AvailabilityIsBlockList = true;
}

// default values for generating symbol graphs during a build
Opts.PrettyPrint = false;
Opts.EmitSynthesizedMembers = true;
Expand Down
20 changes: 20 additions & 0 deletions lib/SymbolGraphGen/Symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,26 @@ void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const {
llvm::StringMap<Availability> Availabilities;
getInheritedAvailabilities(D, Availabilities);

// If we were asked to filter the availability platforms for the output graph,
// perform that filtering here.
if (Graph->Walker.Options.AvailabilityPlatforms) {
auto AvailabilityPlatforms =
Graph->Walker.Options.AvailabilityPlatforms.value();
if (Graph->Walker.Options.AvailabilityIsBlockList) {
for (const auto Availability : Availabilities.keys()) {
if (Availability != "*" && AvailabilityPlatforms.contains(Availability)) {
Availabilities.erase(Availability);
}
}
} else {
for (const auto Availability : Availabilities.keys()) {
if (Availability != "*" && !AvailabilityPlatforms.contains(Availability)) {
Availabilities.erase(Availability);
}
}
}
}

if (Availabilities.empty()) {
return;
}
Expand Down
7 changes: 5 additions & 2 deletions stdlib/cmake/modules/AddSwiftStdlib.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -932,11 +932,14 @@ function(add_swift_target_library_single target name)
endif()
endif()

# FIXME: swiftDarwin currently trips an assertion in SymbolGraphGen
if (SWIFTLIB_IS_STDLIB AND SWIFT_STDLIB_BUILD_SYMBOL_GRAPHS AND NOT ${name} STREQUAL "swiftDarwin")
# FIXME: swiftDarwin and swiftDifferentiationUnittest currently trip an assertion in SymbolGraphGen
if (SWIFTLIB_IS_STDLIB AND SWIFT_STDLIB_BUILD_SYMBOL_GRAPHS AND NOT ${name} STREQUAL "swiftDarwin"
AND NOT ${name} STREQUAL "swiftDifferentiationUnittest")
list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS "-Xfrontend;-emit-symbol-graph")
list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS
"-Xfrontend;-emit-symbol-graph-dir;-Xfrontend;${out_lib_dir}/symbol-graph/${VARIANT_NAME}")
list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS
"-Xfrontend;-symbol-graph-allow-availability-platforms;-Xfrontend;Swift")
endif()

if(MODULE)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/
// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix DEFAULT

// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t
// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix DEFAULT

// Now checking the allowlist behavior...

// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -symbol-graph-allow-availability-platforms macOS,iOS
// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix ALLOWLIST

// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t -allow-availability-platforms macOS,iOS
// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix ALLOWLIST

// Now checking the blocklist behavior...

// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -symbol-graph-block-availability-platforms macOS,iOS
// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix BLOCKLIST

// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t -block-availability-platforms macOS,iOS
// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix BLOCKLIST

// Now test to ensure an empty allow list filters out all availability...

// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -symbol-graph-allow-availability-platforms ""
// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix EMPTY

// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t -allow-availability-platforms ""
// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix EMPTY

// REQUIRES: OS=macosx

@available(macOS 11.0, iOS 15.0, watchOS 15.0, *)
public struct S {}

/// Ensure that regardless of platforms being removed, that universal availability info,
/// like unconditional deprecation, still lands in the symbol graph.
@available(*, deprecated)
public class C {}

// DEFAULT-DAG: macOS
// DEFAULT-DAG: iOS
// DEFAULT-DAG: watchOS
// DEFAULT-DAG: "isUnconditionallyDeprecated":{{ ?}}true

// ALLOWLIST-NOT: watchOS
// ALLOWLIST-DAG: macOS
// ALLOWLIST-DAG: iOS
// ALLOWLIST-DAG: "isUnconditionallyDeprecated":{{ ?}}true

// BLOCKLIST-NOT: macOS
// BLOCKLIST-NOT: iOS
// BLOCKLIST-DAG: watchOS
// BLOCKLIST-DAG: "isUnconditionallyDeprecated":{{ ?}}true

// EMPTY-NOT: macOS
// EMPTY-NOT: iOS
// EMPTY-NOT: watchOS
// EMPTY-DAG: "isUnconditionallyDeprecated":{{ ?}}true