Skip to content
Draft
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
84 changes: 84 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,90 @@ jobs:
python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')"
python builder.pyz build -p ${{ env.PACKAGE_NAME }} --python "${{ steps.python38.outputs.python-path }}"

windows-arm64:
runs-on: windows-11-arm
strategy:
fail-fast: false
matrix:
python: ['3.11', '3.13']
permissions:
id-token: write # This is required for requesting the JWT
steps:
- uses: actions/setup-python@v5
id: python
with:
python-version: ${{ matrix.python }}
architecture: arm64
- name: configure AWS credentials (containers)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.CRT_CI_ROLE }}
aws-region: ${{ env.AWS_DEFAULT_REGION }}
- name: Build ${{ env.PACKAGE_NAME }} + consumers
run: |
python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')"
python builder.pyz build -p ${{ env.PACKAGE_NAME }} --python "${{ steps.python.outputs.python-path }}"

windows-arm64-wheel:
# Verify the wheel we'd ship is tagged for win_arm64, and that an abi3 wheel
# built on one python installs and imports on a newer one.
name: windows-arm64-wheel (${{ matrix.build-python }} → ${{ matrix.test-python }})
runs-on: windows-11-arm
permissions:
contents: read # only needs to check out the source; no AWS creds, no OIDC
strategy:
fail-fast: false
matrix:
include:
- build-python: '3.11'
test-python: '3.12'
expected-tag: 'cp311-abi3-win_arm64'
- build-python: '3.13'
test-python: '3.14'
expected-tag: 'cp313-abi3-win_arm64'
- build-python: '3.13t'
test-python: '3.13t'
expected-tag: 'cp313-cp313t-win_arm64'
- build-python: '3.14t'
test-python: '3.14t'
expected-tag: 'cp314-cp314t-win_arm64'
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.build-python }}
architecture: arm64
- name: Build wheel
shell: pwsh
run: |
python -m pip install --upgrade build
python -m build --wheel
$wheels = @(Get-Item dist\*.whl)
if ($wheels.Count -ne 1) { throw "Expected exactly one wheel, got $($wheels.Count)" }
$expected = 'Tag: ${{ matrix.expected-tag }}'
python -c "import sys, zipfile; z=zipfile.ZipFile(sys.argv[1]); w=z.read(next(n for n in z.namelist() if n.endswith('.dist-info/WHEEL'))).decode().splitlines(); assert sys.argv[2] in w, (sys.argv[2], w)" $wheels[0].FullName $expected
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.test-python }}
architecture: arm64
- name: Install wheel and import
shell: pwsh
run: |
python -m venv ${{ runner.temp }}\venv
$py = "${{ runner.temp }}\venv\Scripts\python.exe"
& $py -m pip install @(Get-Item dist\*.whl)[0].FullName
# Import from outside the checkout, else "python -c" resolves awscrt's
# python files from the source tree instead of the installed wheel.
Push-Location ${{ runner.temp }}
try {
& $py -c "import awscrt, awscrt.io, awscrt.http, awscrt.s3, awscrt.aio, awscrt.eventstream; print(awscrt.__version__, awscrt.__file__)"
} finally {
Pop-Location
}


macos:
runs-on: macos-14 # latest
permissions:
Expand Down
59 changes: 59 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@
# Regex to match a Windows SDK version directory name in the format of major.minor.build.revision
SDK_VERSION_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)\.(\d+)$')

# On Windows ARM64 we build with clang-cl, which accepts MSVC-only flags and then
# ignores them, warning once per translation unit. We get /MP from the CRT's CMake
# and /GL from distutils. Those are two independent compiler invocations that share
# no configuration, so these have to be passed to each of them separately.
#
# Keep this a list, one flag per entry: extra_compile_args entries each become a
# single argv element, so "-Wa -Wb" in one entry reaches clang-cl as one unknown
# option and is silently ignored.
#
# Note these only reach the CRT build via CMAKE_C_FLAGS, which CMake emits *before*
# a target's own options. So a flag here cannot turn off anything the CRT's /W4
# re-enables (i.e. anything in -Wall or -Wextra) -- that has to be fixed in
# aws-c-common's AwsCFlags.cmake instead.
CLANG_CL_IGNORED_FLAG_WARNINGS = [
'-Wno-unused-command-line-argument',
]


def parse_version(version_string):
return tuple(int(x) for x in version_string.split("."))
Expand All @@ -48,6 +65,10 @@ def is_32bit():
return is_64bit() == False


def is_windows_arm64():
return sys.platform == 'win32' and sysconfig.get_platform() == 'win-arm64'


# TODO: Fix this. Since adding pyproject.toml, it always returns False
def is_development_mode():
"""Return whether we're building in Development Mode (a.k.a. “Editable Installs”).
Expand Down Expand Up @@ -196,6 +217,28 @@ def determine_generator_args(cmake_version=None, windows_sdk_version=None):

print('Using Visual Studio', vs_version, vs_year)

if is_windows_arm64():
if not shutil.which('clang-cl'):
raise RuntimeError('clang-cl is required to build for Windows ARM64')
print('Using Ninja and clang-cl for Windows ARM64')
return [
'-G', 'Ninja',
f'-DCMAKE_SYSTEM_VERSION={windows_sdk_version}',
'-DCMAKE_C_COMPILER=clang-cl',
'-DCMAKE_CXX_COMPILER=clang-cl',
'-DCMAKE_ASM_COMPILER=clang-cl',
# The optimized ARM checksum sources do not compile with clang-cl.
'-DUSE_CPU_EXTENSIONS=OFF',
# Silences /MP, which the CRT's CMake adds. This lands ahead of the
# /W4 that aws_set_common_properties() adds, which is fine: /W4 maps
# to -Wall -Wextra, and neither group includes this warning, so they
# do not turn it back on.
# Appends to, rather than replaces, what CMake would have taken from
# the CFLAGS env var -- an explicit -D on the command line wins over
# that, and the extension half already honors CFLAGS (see awscrt_ext).
'-DCMAKE_C_FLAGS=' + ' '.join([os.environ.get('CFLAGS', '')] + CLANG_CL_IGNORED_FLAG_WARNINGS).strip(),
]

if vs_year <= 2017:
# For VS2017 and earlier, architecture goes at end of generator string
if is_64bit():
Expand Down Expand Up @@ -325,6 +368,16 @@ def __init__(self, name, extra_cmake_args=[], libname=None):


class awscrt_build_ext(setuptools.command.build_ext.build_ext):
def build_extensions(self):
if is_windows_arm64():
if not self.compiler.initialized:
self.compiler.initialize()
clang_cl = shutil.which('clang-cl')
if not clang_cl:
raise RuntimeError('clang-cl is required to build for Windows ARM64')
self.compiler.cc = clang_cl
super().build_extensions()

def _build_dependencies_impl(self, build_dir, install_path, osx_arch=None):
cmake = get_cmake_path()

Expand Down Expand Up @@ -540,6 +593,12 @@ def awscrt_ext():
extra_compile_args += ['/Z7']
extra_link_args += ['/DEBUG']

if is_windows_arm64():
# Silences /GL, which distutils hardcodes into its MSVC defaults
# ("/O2 /W3 /GL /DNDEBUG /MD"). This is a separate compiler invocation
# from the CMake one above, so it needs the flag of its own.
extra_compile_args += CLANG_CL_IGNORED_FLAG_WARNINGS

elif sys.platform == 'darwin':
extra_link_args += ['-framework', 'Security']

Expand Down
Loading