Skip to content

[ABI] Introduce an ABI checker that diffs symbols #69765

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 8 commits into from
Dec 11, 2023
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
13,882 changes: 13,882 additions & 0 deletions test/abi/Inputs/macOS/arm64/stdlib/baseline

Large diffs are not rendered by default.

13,978 changes: 13,978 additions & 0 deletions test/abi/Inputs/macOS/arm64/stdlib/baseline-asserts

Large diffs are not rendered by default.

13,741 changes: 13,741 additions & 0 deletions test/abi/Inputs/macOS/x86_64/stdlib/baseline

Large diffs are not rendered by default.

13,837 changes: 13,837 additions & 0 deletions test/abi/Inputs/macOS/x86_64/stdlib/baseline-asserts

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions test/abi/macOS/arm64/stdlib-asserts.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: %empty-directory(%t)
// RUN: %llvm-nm -g --defined-only -f just-symbols %stdlib_dir/arm64/libswiftCore.dylib > %t/symbols
// RUN: %abi-symbol-checker %s %t/symbols --base %S/stdlib.swift
// RUN: diff -u %S/../../Inputs/macOS/arm64/stdlib/baseline-asserts %t/symbols

// REQUIRES: swift_stdlib_asserts
// REQUIRES: STDLIB_VARIANT=macosx-arm64

// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment below.)

// Welcome, Build Wrangler!
//
// This file lists APIs that have recently changed in a way that potentially
// indicates an ABI- or source-breaking problem.
//
// A failure in this test indicates that there is a potential breaking change in
// the Standard Library. If you observe a failure outside of a PR test, please
// reach out to the Standard Library team directly to make sure this gets
// resolved quickly! If your own PR fails in this test, you probably have an
// ABI- or source-breaking change in your commits. Please go and fix it.
//
// Please DO NOT DISABLE THIS TEST. In addition to ignoring the current set of
// ABI breaks, XFAILing this test also silences any future ABI breaks that may
// land on this branch, which simply generates extra work for the next person
// that picks up the mess.
//
// Instead of disabling this test, you'll need to extend the list of expected
// changes at the bottom. (You'll also need to do this if your own PR triggers
// false positives, or if you have special permission to break things.) You can
// find a diff of what needs to be added in the output of the failed test run.
// The order of lines doesn't matter, and you can also include comments to refer
// to any bugs you filed.
//
// Thank you for your help ensuring the stdlib remains compatible with its past!
// -- Your friendly stdlib engineers

// *** NOTE: ***
// You will normally add new entries in 'abi/macOS/arm64/stdlib.swift' instead
// of this file. This file is dedicated for assert only symbols.

// Standard Library Symbols

// Runtime Symbols
48 changes: 48 additions & 0 deletions test/abi/macOS/arm64/stdlib.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// RUN: %empty-directory(%t)
// RUN: %llvm-nm -g --defined-only -f just-symbols %stdlib_dir/arm64/libswiftCore.dylib > %t/symbols
// RUN: %abi-symbol-checker %s %t/symbols
// RUN: diff -u %S/../../Inputs/macOS/arm64/stdlib/baseline %t/symbols

// REQUIRES: swift_stdlib_no_asserts
// REQUIRES: STDLIB_VARIANT=macosx-arm64

// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment below.)

// Welcome, Build Wrangler!
//
// This file lists APIs that have recently changed in a way that potentially
// indicates an ABI- or source-breaking problem.
//
// A failure in this test indicates that there is a potential breaking change in
// the Standard Library. If you observe a failure outside of a PR test, please
// reach out to the Standard Library team directly to make sure this gets
// resolved quickly! If your own PR fails in this test, you probably have an
// ABI- or source-breaking change in your commits. Please go and fix it.
//
// Please DO NOT DISABLE THIS TEST. In addition to ignoring the current set of
// ABI breaks, XFAILing this test also silences any future ABI breaks that may
// land on this branch, which simply generates extra work for the next person
// that picks up the mess.
//
// Instead of disabling this test, you'll need to extend the list of expected
// changes at the bottom. (You'll also need to do this if your own PR triggers
// false positives, or if you have special permission to break things.) You can
// find a diff of what needs to be added in the output of the failed test run.
// The order of lines doesn't matter, and you can also include comments to refer
// to any bugs you filed.
//
// Thank you for your help ensuring the stdlib remains compatible with its past!
// -- Your friendly stdlib engineers

// Standard Library Symbols

// Swift._getRetainCount(Swift.AnyObject) -> Swift.UInt
Added: _$ss15_getRetainCountySuyXlF

// Swift._getWeakRetainCount(Swift.AnyObject) -> Swift.UInt
Added: _$ss19_getWeakRetainCountySuyXlF

// Swift._getUnownedRetainCount(Swift.AnyObject) -> Swift.UInt
Added: _$ss22_getUnownedRetainCountySuyXlF

// Runtime Symbols
43 changes: 43 additions & 0 deletions test/abi/macOS/x86_64/stdlib-asserts.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: %empty-directory(%t)
// RUN: %llvm-nm -g --defined-only -f just-symbols %stdlib_dir/x86_64/libswiftCore.dylib > %t/symbols
// RUN: %abi-symbol-checker %s %t/symbols --base %S/stdlib.swift
// RUN: diff -u %S/../../Inputs/macOS/x86_64/stdlib/baseline-asserts %t/symbols

// REQUIRES: swift_stdlib_asserts
// REQUIRES: STDLIB_VARIANT=macosx-x86_64

// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment below.)

// Welcome, Build Wrangler!
//
// This file lists APIs that have recently changed in a way that potentially
// indicates an ABI- or source-breaking problem.
//
// A failure in this test indicates that there is a potential breaking change in
// the Standard Library. If you observe a failure outside of a PR test, please
// reach out to the Standard Library team directly to make sure this gets
// resolved quickly! If your own PR fails in this test, you probably have an
// ABI- or source-breaking change in your commits. Please go and fix it.
//
// Please DO NOT DISABLE THIS TEST. In addition to ignoring the current set of
// ABI breaks, XFAILing this test also silences any future ABI breaks that may
// land on this branch, which simply generates extra work for the next person
// that picks up the mess.
//
// Instead of disabling this test, you'll need to extend the list of expected
// changes at the bottom. (You'll also need to do this if your own PR triggers
// false positives, or if you have special permission to break things.) You can
// find a diff of what needs to be added in the output of the failed test run.
// The order of lines doesn't matter, and you can also include comments to refer
// to any bugs you filed.
//
// Thank you for your help ensuring the stdlib remains compatible with its past!
// -- Your friendly stdlib engineers

// *** NOTE: ***
// You will normally add new entries in 'abi/macOS/x86_64/stdlib.swift' instead
// of this file. This file is dedicated for assert only symbols.

// Standard Library Symbols

// Runtime Symbols
48 changes: 48 additions & 0 deletions test/abi/macOS/x86_64/stdlib.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// RUN: %empty-directory(%t)
// RUN: %llvm-nm -g --defined-only -f just-symbols %stdlib_dir/x86_64/libswiftCore.dylib > %t/symbols
// RUN: %abi-symbol-checker %s %t/symbols
// RUN: diff -u %S/../../Inputs/macOS/x86_64/stdlib/baseline %t/symbols

// REQUIRES: swift_stdlib_no_asserts
// REQUIRES: STDLIB_VARIANT=macosx-x86_64

// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment below.)

// Welcome, Build Wrangler!
//
// This file lists APIs that have recently changed in a way that potentially
// indicates an ABI- or source-breaking problem.
//
// A failure in this test indicates that there is a potential breaking change in
// the Standard Library. If you observe a failure outside of a PR test, please
// reach out to the Standard Library team directly to make sure this gets
// resolved quickly! If your own PR fails in this test, you probably have an
// ABI- or source-breaking change in your commits. Please go and fix it.
//
// Please DO NOT DISABLE THIS TEST. In addition to ignoring the current set of
// ABI breaks, XFAILing this test also silences any future ABI breaks that may
// land on this branch, which simply generates extra work for the next person
// that picks up the mess.
//
// Instead of disabling this test, you'll need to extend the list of expected
// changes at the bottom. (You'll also need to do this if your own PR triggers
// false positives, or if you have special permission to break things.) You can
// find a diff of what needs to be added in the output of the failed test run.
// The order of lines doesn't matter, and you can also include comments to refer
// to any bugs you filed.
//
// Thank you for your help ensuring the stdlib remains compatible with its past!
// -- Your friendly stdlib engineers

// Standard Library Symbols

// Swift._getRetainCount(Swift.AnyObject) -> Swift.UInt
Added: _$ss15_getRetainCountySuyXlF

// Swift._getWeakRetainCount(Swift.AnyObject) -> Swift.UInt
Added: _$ss19_getWeakRetainCountySuyXlF

// Swift._getUnownedRetainCount(Swift.AnyObject) -> Swift.UInt
Added: _$ss22_getUnownedRetainCountySuyXlF

// Runtime Symbols
4 changes: 4 additions & 0 deletions test/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ config.swift_libexec_dir = make_path(config.swift, '..', '..', 'libexec')
config.swift_share_dir = make_path(config.swift, '..', '..', 'share')
config.round_trip_syntax_test = make_path(config.swift_utils, 'round-trip-syntax-test')
config.refactor_check_compiles = make_path(config.swift_utils, 'refactor-check-compiles.py')
config.abi_symbol_checker = make_path(config.swift_utils, 'swift-abi-symbol-checker.py')

config.link = lit.util.which('link', config.environment.get('PATH', '')) or \
lit.util.which('lld-link', config.environment.get('PATH', ''))
Expand Down Expand Up @@ -778,6 +779,8 @@ config.available_features.add("VENDOR=" + run_vendor)

config.available_features.add("SWIFT_VERSION=" + swift_version)

config.available_features.add("STDLIB_VARIANT={}".format(config.variant_suffix[1:]))

if "optimized_stdlib" in config.available_features:
config.available_features.add("optimized_stdlib_" + run_cpu)

Expand Down Expand Up @@ -2596,6 +2599,7 @@ config.substitutions.append(('%scale-test',
'{} {} --swiftc-binary={} --tmpdir=%t --exclude-timers'.format(
shell_quote(sys.executable), config.scale_test,
config.swiftc)))
config.substitutions.append(('%abi-symbol-checker', '%s %s' % (shell_quote(sys.executable), config.abi_symbol_checker)))
config.substitutions.append(('%target-sil-opt\(mock-sdk:([^)]+)\)',
SubstituteCaptures(r'%s \1 %s' % (
escape_for_substitute_captures(subst_target_sil_opt_mock_sdk),
Expand Down
97 changes: 97 additions & 0 deletions utils/swift-abi-symbol-checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python3

import argparse
import sys


def getAdditionsAndRemovals(changesFile):
changesF = open(changesFile)
changes = changesF.read()
changesF.close()

# Get rid of lines that start with either '//' or a newline
changes = [c for c in changes.splitlines() if not c.startswith('//') and c != '']

# Filter the changes for lines that start with Added
additions = [a for a in changes if a.startswith('Added')]
# Filter the changes for lines that start with Removed
removals = [r for r in changes if r.startswith('Removed')]

# Map the additions by removing the 'Added: ' prefix to get just the symbol
additions = list(map(lambda a: a.removeprefix('Added: '), additions))
# Map the removals by removing the 'Removed: ' prefix to get just the symbol
removals = list(map(lambda r: r.removeprefix('Removed: '), removals))

return (additions, removals)


def checkSymbols(args):
# If we were passed a base file, read those changes first. This most likely
# occurs in assert configurations getting the non-assert base.
baseAdditions = []
baseRemovals = []

if args.base:
(baseAdditions, baseRemovals) = getAdditionsAndRemovals(args.base)

(additions, removals) = getAdditionsAndRemovals(args.changes)

# Append the base additions and removals to ours.
additions.extend(baseAdditions)
removals.extend(baseRemovals)

# We need to write back to the temporary symbol file for diffing
symbolsF = open(args.symbols, 'r+')

symbols = symbolsF.read().splitlines()

# Check for added symbols that are not actually in the just built dylib.
notInDylib = [a for a in additions if a not in symbols]

# If there were symbols marked as 'Added' in the changes file, but they didn't
# actually appear in the dylib then print those symbols out and fail.
if notInDylib:
for symbol in notInDylib:
print(('{} was marked as \'Added\', but it was not found in the '
'just built library').format(symbol))

sys.exit(-1)

# Filter the built symbols for the additions because we're removing them to
# get back to the baseline
symbols = [s for s in symbols if s not in additions]

# Append the removals into the symbol list to get back to the baseline
symbols.extend(removals)

# Sort the end result to write back
symbols.sort()

# Go back to beginning of the file and purge everything
symbolsF.seek(0)
symbolsF.truncate()

# Append a newline to each symbol (because writelines doesn't do that for us)
symbols = list(map(lambda s: s + '\n', symbols))

# Write all of our symbols back into the symbols file
symbolsF.writelines(symbols)

# Done
symbolsF.close()


def main(arguments):
parser = argparse.ArgumentParser(
description='Change absolute install names to use @rpath')

parser.add_argument('changes', help='the changes file')
parser.add_argument('symbols', help='the symbols file')
parser.add_argument('--base', help='the base changes file')

args = parser.parse_args(arguments)

checkSymbols(args)


sys.exit(main(sys.argv[1:]))