diff --git a/doc/COMPILING/COMPILER_SUPPORT.md b/doc/COMPILING/COMPILER_SUPPORT.md index 8ada19a3d3713..a2ddb2425c46e 100644 --- a/doc/COMPILING/COMPILER_SUPPORT.md +++ b/doc/COMPILING/COMPILER_SUPPORT.md @@ -8,6 +8,11 @@ To that end, we aim to support gcc and clang up to the newest stable versions and back to those shipping in any supported version of a popular distribution or relevant development environment, including Ubuntu, Debian, MSYS, and XCode. +Since macOS can be harder to update we have active developers and users on +unsupported versions of macOS we would like to support. Newer macOS cannot +compile for older macOS, so to support a reasonable number of users we aim to +support at least 95% of users by macOS market share. + At the time of writing: * Bionic is the oldest Ubuntu LTS, and [defaults to g++ 7.3 or 7.5](https://packages.ubuntu.com/bionic/g++), depending on the platform, and @@ -20,6 +25,9 @@ At the time of writing: * MSYS [offers gcc 10.2](https://packages.msys2.org/base). * Code::Blocks [offers g++ 8.1](https://www.codeblocks.org/downloads/binaries/). +* macOS 10.12+ has 95.3% [market + share](https://gs.statcounter.com/os-version-market-share/macos/desktop/worldwide) + and that corresponds to [XCode 8.3](https://xcodereleases.com/). In practice, compiler support is often determined by what is covered in our automated testing. @@ -29,9 +37,27 @@ Ubuntu Bionic LTS release. The default version is 7.3 only on platforms other than x86, and we deem it unlikely that potential users or developers would be using an Ubuntu LTS release on such a platform. -Ubuntu is likely to remain the limiting factor, so -the set of supported compilers should be reviewed when Bionic ceases support in -2023-05. +The limiting factor preventing us from using newer C++ features is currently +XCode, where we would like to require 10.0 to get [most C++17 +features](https://en.cppreference.com/w/cpp/compiler_support/17). That +[requires macOS 10.13](https://xcodereleases.com/) so we need to wait until +macOS versions up to 10.12 drop below 5% market share (currently 7.4%). + +To monitor macOS market share we have a helper script in +tools/macos-market-share.py. Download the CSV-formatted data from +[statcounter](https://gs.statcounter.com/os-version-market-share/macos/desktop/worldwide) +and process it with that script to get a summary of cumulative market share as +it varies across time. For example, this output: + +``` +2021-05 :: 10.11: 8.2 10.12: 11.0 10.13: 18.3 10.14: 27.0 10.15: 98.1 +2021-06 :: 10.11: 6.6 10.12: 9.3 10.13: 16.3 10.14: 24.6 10.15: 99.0 +2021-07 :: 10.11: 4.7 10.12: 7.4 10.13: 14.2 10.14: 22.1 10.15: 99.3 +``` + +shows that cumulative market share for versions up to 10.11 first dropped below +5% in 2021-07, at which point we can (following the above guidelines) allow +ourselves to drop support for 10.11. ## GCC @@ -60,4 +86,4 @@ We also support [Visual Studio](COMPILING-VS-VCPKG.md) 2015 Update 3 and above. ## XCode -The minimum supported version is 10.2, which is also what we test. +We support macOS 10.12 and above, which effectively means XCode 8.3+. diff --git a/tools/macos-market-share.py b/tools/macos-market-share.py new file mode 100755 index 0000000000000..cc5986557b90c --- /dev/null +++ b/tools/macos-market-share.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 + +# This script is intended to help monitor macOS market share to help us decide +# when it is reasonable to drop support for a particular version. +# +# The expected input file is a CSV-formatted data file downloaded from +# https://gs.statcounter.com/os-version-market-share/macos/desktop/worldwide +# +# The output shows cumulative market share by increasing version number for +# each month in the input data. The range of versions is limited to make the +# output more reasonable, but the cumulative tally does include smaller +# versions not listed. +# +# See also doc/COMPILING/COMPILER_SUPPORT.md + +import csv +import re +import sys + +in_filename, = sys.argv[1:] + +with open(in_filename) as in_file: + reader = csv.reader(in_file) + rows = list(reader) + +headers = rows[0] +rows = rows[1:] + +replacement = { + 'Date': 'Date', + 'macOS Catalina': (10, 15), + 'macOS Mojave': (10, 14), + 'macOS High Sierra': (10, 13), + 'macOS Sierra': (10, 12), + 'OS X El Capitan': (10, 11), + 'OS X Mavericks': (10, 9), + 'mac OS X Snow Leopard': (10, 6), + 'mac OS X Lion': (10, 7), + 'OS X Mountain Lion': (10, 8), + 'mac OS X Leopard': (10, 6), + 'Other': (0,) +} + +version_re = re.compile('([0-9]+)\\.([0-9]+)') + + +def get_version(name): + if name in replacement: + return replacement[name] + match = version_re.search(name) + assert match, f'name = {name!r}' + return tuple(int(x) for x in match.group(1, 2)) + + +headers = [get_version(h) for h in headers] + +dicts = [ + {field_name: field for field_name, field in zip(headers, row)} + for row in rows +] + +for dic in dicts: + date = dic.pop('Date') + stats = sorted(dic.items()) + tally = [] + total = 0 + for version, fraction in stats: + total += float(fraction) + if version >= (10, 11) and version <= (10, 15): + tally.append((version, total)) + tally_str = '' + for version, total in tally: + tally_str += f'{version[0]}.{version[1]}: {total:4.1f} ' + print(f'{date} :: {tally_str}')