mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Added title column. Make short parameter consistent. Made output option -c work. Fixed csv output for -l flag. Shortened too long lines. Fix linting errors. Remove comment.
224 lines
8.6 KiB
Python
224 lines
8.6 KiB
Python
#!/usr/bin/env python
|
|
# cmpcodesize/main.py - Command-line entry point for cmpcodesize -*- python -*-
|
|
#
|
|
# This source file is part of the Swift.org open source project
|
|
#
|
|
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
# Licensed under Apache License v2.0 with Runtime Library Exception
|
|
#
|
|
# See https://swift.org/LICENSE.txt for license information
|
|
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import collections
|
|
import csv
|
|
import glob
|
|
import os
|
|
import sys
|
|
|
|
from cmpcodesize.compare import \
|
|
compare_function_sizes, compare_sizes_of_file, list_function_sizes,\
|
|
read_sizes
|
|
|
|
|
|
SHORTCUTS = {
|
|
"O": "bin/Benchmark_O",
|
|
"Osize": "bin/Benchmark_Osize",
|
|
"Onone": "bin/Benchmark_Onone",
|
|
"dylib": "lib/swift/macosx/x86_64/libswiftCore.dylib",
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
description="""
|
|
Compares code sizes of "new" files, taking "old" files as a reference.
|
|
|
|
Environment variables:
|
|
SWIFT_NEW_BUILDDIR The new build-dir
|
|
E.g. .../swiftnew/build/Ninja-ReleaseAssert+stdlib-Release/swift-macosx-x86_64
|
|
SWIFT_OLD_BUILDDIR The old build-dir
|
|
E.g. .../swiftold/build/Ninja-ReleaseAssert+stdlib-Release/swift-macosx-x86_64
|
|
|
|
How to specify files:
|
|
1) No files:
|
|
Compares codesize of the Benchmark_* executables and the swiftCore dylib in
|
|
the new and old build-dirs.
|
|
Example:
|
|
cmpcodesize
|
|
|
|
2) One or more paths relative to the build-dirs (can be a pattern):
|
|
Compares the files in the new and old build-dirs.
|
|
Aliases:
|
|
O => bin/Benchmark_O
|
|
Osize => bin/Benchmark_Osize
|
|
Onone => bin/Benchmark_Onone
|
|
dylib => lib/swift/macosx/x86_64/libswiftCore.dylib
|
|
Examples:
|
|
cmpcodesize Onone
|
|
cmpcodesize benchmark/PerfTestSuite/O/*.o
|
|
|
|
3) Two files:
|
|
Compares these two files (the first is the old file).
|
|
Example:
|
|
cmpcodesize test.o newversion.o
|
|
|
|
4) Two lists of files, separated by '--':
|
|
Compares a set of files.
|
|
Example:
|
|
cmpcodesize olddir/*.o -- newdir/*.o
|
|
|
|
5) One file (only available with the -l option):
|
|
Lists function sizes for that file
|
|
Example:
|
|
cmpcodesize -l test.o""")
|
|
|
|
# Optional arguments.
|
|
parser.add_argument('-a', '--additional-sections',
|
|
help='Show sizes of additional sections.',
|
|
action='store_true',
|
|
dest='all_sections',
|
|
default=False)
|
|
parser.add_argument('-c', '--category',
|
|
help='Show functions by category.',
|
|
action='store_true',
|
|
dest='list_categories',
|
|
default=False)
|
|
parser.add_argument('-l', '--list',
|
|
help='List all functions (can be a very long list). ' +
|
|
'Cannot be used in conjunction with ' +
|
|
'--additional-sections or --category. ' +
|
|
'You must specify between one and two files ' +
|
|
'when using this option.',
|
|
action='store_true',
|
|
dest='list_functions',
|
|
default=False)
|
|
parser.add_argument('-s', '--summarize',
|
|
help='Summarize the sizes of multiple files instead ' +
|
|
'of listing each file separately.',
|
|
action='store_true',
|
|
dest='sum_sizes',
|
|
default=False)
|
|
parser.add_argument('-p', '--parseable',
|
|
help='Generate output as CSV that can be parsed by ' +
|
|
'other programs.',
|
|
action='store_true',
|
|
default=False)
|
|
|
|
# Positional arguments.
|
|
# These can be specified in means beyond what argparse supports,
|
|
# so we gather them in a list and parse them manually.
|
|
parser.add_argument('files', nargs='*',
|
|
help='A list of old and new files.')
|
|
|
|
# argparse can't handle an '--' argument, so we replace it with
|
|
# a custom identifier.
|
|
separator_token = '*-*-*'
|
|
parsed_arguments = parser.parse_args(
|
|
[separator_token if arg == '--' else arg for arg in sys.argv[1:]])
|
|
|
|
if parsed_arguments.list_functions:
|
|
# --list is mutually exclusive with both --additional-sections
|
|
# and --category. argparse is only capable of expressing mutual
|
|
# exclusivity among options, not among groups of options, so
|
|
# we detect this case manually.
|
|
assert (not parsed_arguments.all_sections and
|
|
not parsed_arguments.list_categories), \
|
|
'Incorrect usage: --list cannot be specified in conjunction ' + \
|
|
'with --additional-sections or --category.'
|
|
# A file must be specified when using --list.
|
|
assert parsed_arguments.files, \
|
|
'Incorrect usage: Must specify between one and two files when ' + \
|
|
'using --list, but you specified no files.'
|
|
|
|
csv_out = None
|
|
if parsed_arguments.parseable:
|
|
csv_out = csv.writer(sys.stdout)
|
|
|
|
if separator_token in parsed_arguments.files:
|
|
separator_index = parsed_arguments.files.index(separator_token)
|
|
old_files = parsed_arguments.files[:separator_index]
|
|
new_files = parsed_arguments.files[separator_index + 1:]
|
|
else:
|
|
old_file_args = parsed_arguments.files
|
|
|
|
old_build_dir = os.environ.get("SWIFT_OLD_BUILDDIR")
|
|
new_build_dir = os.environ.get("SWIFT_NEW_BUILDDIR")
|
|
|
|
if not parsed_arguments.files:
|
|
assert old_build_dir and new_build_dir, \
|
|
'Incorrect usage: You must specify either a list of ' + \
|
|
'files, or have both $SWIFT_OLD_BUILDDIR and ' + \
|
|
'$SWIFT_NEW_BUILDDIR environment variables set.\n' + \
|
|
'$SWIFT_OLD_BUILDDIR = {0}\n$SWIFT_NEW_BUILDDIR = {1}'.format(
|
|
old_build_dir, new_build_dir)
|
|
old_file_args = list(SHORTCUTS.keys())
|
|
|
|
old_files = []
|
|
new_files = []
|
|
num_expanded = 0
|
|
for file in old_file_args:
|
|
if file in SHORTCUTS:
|
|
file = SHORTCUTS[file]
|
|
|
|
if not file.startswith("./") and old_build_dir and new_build_dir:
|
|
old_expanded = glob.glob(os.path.join(old_build_dir, file))
|
|
new_expanded = glob.glob(os.path.join(new_build_dir, file))
|
|
if old_expanded and new_expanded:
|
|
old_files.extend(old_expanded)
|
|
new_files.extend(new_expanded)
|
|
num_expanded += 1
|
|
|
|
if num_expanded != 0 and num_expanded != len(old_file_args):
|
|
sys.exit("mix of expanded/not-expanded arguments")
|
|
if num_expanded == 0:
|
|
if len(old_file_args) > 2:
|
|
sys.exit("too many arguments")
|
|
old_files = old_file_args[0:1]
|
|
new_files = old_file_args[1:2]
|
|
|
|
for file in (old_files + new_files):
|
|
if not os.path.isfile(file):
|
|
sys.exit("file " + file + " not found")
|
|
|
|
if parsed_arguments.list_functions:
|
|
if not new_files:
|
|
sizes = collections.defaultdict(int)
|
|
for file in old_files:
|
|
read_sizes(sizes, file, True, False)
|
|
print(os.linesep.join(list_function_sizes(sizes.items())))
|
|
else:
|
|
compare_function_sizes(old_files, new_files, csv=csv_out)
|
|
else:
|
|
if csv_out:
|
|
csv_out.writerow(["Title", "Section", "Old", "Old Relative",
|
|
"New", "New Relative", "Percentage Change"])
|
|
else:
|
|
print("%-26s%16s %14s %14s %s" %
|
|
("Title", "Section", "Old", "New", "Percent"))
|
|
|
|
if parsed_arguments.sum_sizes:
|
|
compare_sizes_of_file(old_files, new_files,
|
|
parsed_arguments.all_sections,
|
|
parsed_arguments.list_categories,
|
|
csv=csv_out)
|
|
else:
|
|
if len(old_files) != len(new_files):
|
|
sys.exit("number of new files must be the same of old files")
|
|
|
|
old_files.sort()
|
|
new_files.sort()
|
|
|
|
for old_file, new_file in zip(old_files, new_files):
|
|
compare_sizes_of_file([old_file], [new_file],
|
|
parsed_arguments.all_sections,
|
|
parsed_arguments.list_categories,
|
|
csv=csv_out)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|