Files
cibuildwheel/docs/diagram.html
T
79244d366c feet: general approach to auditing wheels with abi3audit default (#2805)
* WIP - initial punt at audit command

* Add `abi3audit` as a dependency

* Add helper functions to check stable ABI wheels

* Run `abi3audit` for macOS and Windows wheels

* Copy out of container for repairing?

* Add some notes that `cibuildwheel` runs `abi3audit`

* Add basic unit tests

* Add a basic C extension with `Py_LIMITED_API`

* Add a test project that violates Stable ABI

* Fix linux test

* Skip abi3 wheel tests for Pyodide

* Patch the correct subprocess module

* wrap cleanup of abi3audit dir

* Write the docs for the new options

* Move to above testing in docs

* Implement audit-requires and audit-command

* Some cleanups after self-review

* Add default value

* fix type errors

* the key is `audit-command`, not `audit`

* Add a variety of tests for audit requires options

* Add `test_audit_requires` similar to `test_test_requires`

* Add some configurability-related audit tests

* Fix parsing error with options docs leaving out commands

* Better way to extract version (maybe helps Pyodide?)

* Fix a case of unbound `use_uv`

* Standardise: rename to `abi3_wheel`

* Fix audit command run message

* Simplify custom audit command a bit

* Remove unnecessary skip for Pyodide

* Pyodide should have no default audit command

* More accurate skip messages for Pyodide skips

* Wheels are audited after they are repaired

* Regenerate constraints to include `abi3audit`

* Fix typos

* Some attempts for Windows fixes

* Check `pyvenv.cfg` instead of directory existence

* Add validation for lack of wheel placeholders

* Try yet another Windows `uv` fix

* Regenerate diagram and re-trigger Azure CI

* Add missing `import sys` for abi3 C extension tests

* Remove audit-command at the global level

* Clarify `abi3audit` pinning a little bit

* Regen constraints

* Discard changes to cibuildwheel/resources/constraints-pyodide312.txt

* Discard changes to cibuildwheel/resources/constraints-pyodide313.txt

* try opt-in uv again

* fix issue on windows on Python 3.13 related to nested venvs

On win / python 3.13, virtualenv creates a venv where the 'home'
points back to the venv that sys.executable was running in, rather
than the root install. that seemingly leads to problems with package
resolution, where pip.exe couldn't find the pip python package.
this appears to fix it!

* Update constraints

* chore: revert python-discovery bump

Assisted-by: OpenCode:glm-5.1
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* fix: restore workaround for graalpy

Assisted-by: OpenCode:glm-5.1
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

---------

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Co-authored-by: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryfs@princeton.edu>
2026-05-14 07:41:14 -07:00

563 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div id="flow-diagram">
<div class="diagram-grid" v-bind:style="gridStyle">
<!-- BEHIND THE GRID CONTENT -->
<!-- line for each platform -->
<div class="grid-line" style="grid-row-start: 3"></div>
<div class="grid-line" style="grid-row-start: 4"></div>
<div class="grid-line" style="grid-row-start: 5"></div>
<div class="grid-line" style="grid-row-start: 6"></div>
<div class="grid-line" style="grid-row-start: 7"></div>
<div class="grid-line" style="grid-row-start: 8"></div>
<!-- the docker container outline -->
<div class="grid-outline docker">
<div class="outline">
Manylinux container
</div>
</div>
<!-- the venv outlines -->
<div class="grid-outline testVenv" style="grid-row-start: 3">
<div class="outline">Test virtualenv</div>
</div>
<div class="grid-outline testVenv" style="grid-row-start: 4"><div class="outline"></div></div>
<div class="grid-outline testVenv" style="grid-row-start: 5"><div class="outline"></div></div>
<div class="grid-outline testVenv" style="grid-row-start: 6"><div class="outline"></div></div>
<div class="grid-outline testVenv" style="grid-row-start: 7"><div class="outline"></div></div>
<div class="grid-outline testVenv" style="grid-row-start: 8"><div class="outline"></div></div>
<!-- THE GRID CONTENT -->
<!-- the row labels -->
<div class="grid-row-label" style="grid-row-start: 3">Linux</div>
<div class="grid-row-label" style="grid-row-start: 4">macOS</div>
<div class="grid-row-label" style="grid-row-start: 5">Windows</div>
<div class="grid-row-label" style="grid-row-start: 6">Android</div>
<div class="grid-row-label" style="grid-row-start: 7">iOS</div>
<div class="grid-row-label" style="grid-row-start: 8">Pyodide</div>
<!-- the column labels -->
<div class="grid-column-label"
style="grid-row: 1 / span 1; grid-column: 5 / -3">
<div class="label">For each version of Python</div>
</div>
<div class="grid-column-label"
style="grid-row: 2 / span 1;
grid-column: 10 / -3;
margin-bottom: 0.5em;">
<div class="label">If tests are configured</div>
</div>
<!-- the steps -->
<div class="step" v-for="(step, sIndex) in diagram.steps">
<component v-for="action in step"
class="action"
v-bind:is="action.href ? 'a' : 'div'"
v-bind:href="action.href">
<div class="dots" v-if="action.style == 'dot'">
<div class="dot"
v-info="action"
v-for="(platform, pIndex) in platforms"
v-if="action.platforms.includes(platform)"
v-bind:style="{gridRowStart: `${pIndex+3}`,
gridColumnStart: `${sIndex+2}`}">
<div class="dot-graphic"></div>
</div>
</div>
<div class="block-container" v-if="action.style == 'block'">
<div class="block"
v-info="action"
v-bind:style="{...blockStyle(action),
gridColumnStart: `${sIndex+2}`}">
{{action.label}}
</div>
</div>
</a>
</div>
</div>
</div>
<noscript>
<img src="data/how-it-works.png" alt="Diagram showing the steps for each platform">
<p><strong>Enable JavaScript for an interactive version of this diagram.</strong></p>
</noscript>
<script type="module" async>
import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.esm.browser.js'
import 'https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js'
import 'https://unpkg.com/tippy.js@6/dist/tippy-bundle.umd.js'
const diagram = {
steps: [
[
{
label: 'copy project into container',
platforms: ['linux'],
style: 'block',
width: 2,
},
],
[], // empty column so that the manylinux outline intersects the previous column in the middle.
[
{
href: 'options/#before-all',
platforms: ['linux', 'macos', 'windows', 'android', 'ios', 'pyodide'],
style: 'dot',
tooltip: {
title: 'CIBW_BEFORE_ALL',
tag: 'Optional step',
description: 'Execute a shell command on the build system before any wheels are built.'
},
},
],
[
{
label: 'Install Python',
platforms: ['macos', 'windows'],
style: 'block',
tooltip: {
description: 'Install the version of Python required to build this wheel.'
},
},
{
label: 'Install Python & set up cross-compilation',
platforms: ['android', 'ios'],
style: 'block',
tooltip: {
description: 'Install Python for the build and target platforms, and set up a cross-compilation environment.'
},
},
{
label: 'Set up Pyodide xbuildenv',
platforms: ['pyodide'],
style: 'block',
tooltip: {
description: 'Install a base Python, set up a build virtualenv with pyodide-build, install the Pyodide cross-build environment and Emscripten toolchain.'
},
},
],
[
{
href: 'options/#before-build',
platforms: ['linux', 'macos', 'windows', 'android', 'ios', 'pyodide'],
style: 'dot',
tooltip: {
title: 'CIBW_BEFORE_BUILD',
tag: 'Optional step',
description: "Execute a shell command preparing each wheel's build.",
},
},
],
[
{
label: 'Build wheel',
href: 'options/#build-frontend',
platforms: ['linux', 'macos', 'windows', 'android', 'ios', 'pyodide'],
style: 'block',
tooltip: {
title: 'CIBW_BUILD_FRONTEND',
tag: 'Customisable step',
description: 'Build the wheel according to your package configuration, using the frontend of your choice pip, build, or uv.'
},
},
],
[
{
label: 'Repair using auditwheel',
href: 'options/#repair-wheel-command',
platforms: ['linux'],
style: 'block',
tooltip: {
title: 'CIBW_REPAIR_WHEEL_COMMAND',
tag: 'Customisable step',
description: 'Bundle shared libraries and ensure manylinux compliance by running auditwheel on each built wheel.'
},
},
{
label: 'Repair using delocate',
href: 'options/#repair-wheel-command',
platforms: ['macos'],
style: 'block',
tooltip: {
title: 'CIBW_REPAIR_WHEEL_COMMAND',
tag: 'Customisable step',
description: 'Bundle shared libraries by running delocate on each built wheel.'
},
},
{
label: 'Repair wheel',
href: 'options/#repair-wheel-command',
platforms: ['android'],
style: 'block',
tooltip: {
title: 'CIBW_REPAIR_WHEEL_COMMAND',
tag: 'Customisable step',
description: 'By default, bundles libc++ into the wheel if any shared library links against it, using patchelf. Can be overridden with a custom repair command.'
},
},
{
href: 'options/#repair-wheel-command',
platforms: ['windows', 'ios', 'pyodide'],
style: 'dot',
tooltip: {
title: 'CIBW_REPAIR_WHEEL_COMMAND',
tag: 'Optional step',
description: 'Execute a shell command to repair each built wheel.'
},
},
],
[
{
env: "CIBW_AUDIT_COMMAND",
href: 'options/#audit-command',
label: 'audit wheel',
platforms: ['linux', 'macos', 'windows'],
style: 'dot',
tooltip: {
title: 'CIBW_AUDIT_COMMAND',
tag: 'Optional step',
description: 'Runs a shell command to check each built wheel. By default this runs abi3audit if produced wheels are abi3.'
},
}
],
[
{
href: 'options/#before-test',
platforms: ['linux', 'macos', 'windows', 'android', 'ios', 'pyodide'],
style: 'dot',
tooltip: {
title: 'CIBW_BEFORE_TEST',
tag: 'Optional step',
description: 'Execute a shell command before testing each wheel'
},
},
],
[
{
label: 'Install wheel',
platforms: ['linux', 'macos', 'windows', 'android', 'ios', 'pyodide'],
style: 'block',
tooltip: {
description: 'Install the wheel we just built into the test environment.'
},
},
],
[
{
label: 'Test wheel',
href: 'options/#test-command',
platforms: ['linux', 'macos', 'windows'],
style: 'block',
tooltip: {
title: 'CIBW_TEST_COMMAND',
tag: 'Optional step',
description: 'Execute a shell command to test each built wheel.'
},
},
{
label: 'Test on emulator',
href: 'options/#test-command',
platforms: ['android'],
style: 'block',
tooltip: {
title: 'CIBW_TEST_COMMAND',
tag: 'Optional step',
description: 'Run the test command on an Android emulator using the CPython testbed.'
},
},
{
label: 'Test on simulator',
href: 'options/#test-command',
platforms: ['ios'],
style: 'block',
tooltip: {
title: 'CIBW_TEST_COMMAND',
tag: 'Optional step',
description: 'Run the test command on an iOS simulator using the testbed app.'
},
},
{
label: 'Test in Node.js',
href: 'options/#test-command',
platforms: ['pyodide'],
style: 'block',
tooltip: {
title: 'CIBW_TEST_COMMAND',
tag: 'Optional step',
description: 'Run the test command in a Pyodide virtualenv powered by Node.js.'
},
},
],
[
{
label: 'Copy wheels out of container',
platforms: ['linux'],
style: 'block',
width: 2,
},
],
[],
]
}
const diagramComponent = new Vue({
el: '#flow-diagram',
data() {
return {
diagram,
platforms: ['linux', 'macos', 'windows', 'android', 'ios', 'pyodide'],
}
},
methods: {
blockStyle(action) {
const platformOrder = ['linux', 'macos', 'windows', 'android', 'ios', 'pyodide']
const rowOffset = 3 // first platform starts at grid row 3
let start = null
let end = null
for (let i = 0; i < platformOrder.length; i++) {
if (action.platforms.includes(platformOrder[i])) {
if (start === null) start = rowOffset + i
end = rowOffset + i + 1
}
}
return {
gridRowStart: start.toString(),
gridRowEnd: end.toString(),
gridColumnEnd: `span ${action.width || 1}`,
}
}
},
computed: {
gridStyle() {
return {
gridTemplateRows: `auto auto repeat(${this.platforms.length}, 1fr)`,
gridTemplateColumns: `repeat(${this.diagram.steps.length+1}, auto)`,
}
},
},
directives: {
info: {
inserted(el, binding) {
const action = binding.value
const {env, label, optional=false, description='', href=''} = action
const tooltip = action.tooltip
if (tooltip) {
const tippyInstance = tippy(el, {
content: `
<a class="tooltip-contents" href="${href || ''}">
<div class="tooltip-title">
${tooltip.title || ''}
</div>
<div class="tooltip-tag">
${tooltip.tag || ''}
</div>
<div class="tooltip-description">
${tooltip.description}
</div>
</a>
`,
placement: 'right-start',
allowHTML: true,
maxWidth: 'none',
appendTo: document.getElementById('flow-diagram'),
offset: [0, 10],
onShow(instance) {
const stepEl = el.closest('.action')
stepEl.classList.add('tooltip-open')
instance.setProps({
interactive: tippy.currentInput.isTouch
})
},
onHide() {
const stepEl = el.closest('.action')
stepEl.classList.remove('tooltip-open')
}
})
el.addEventListener('click', e => {
// click event should just open the tooltip on touch devices
if (tippy.currentInput.isTouch) {
e.preventDefault()
}
})
}
}
}
}
})
</script>
<style>
#flow-diagram {
background: #fcfcfc;
/* font-family: Inter; */
/* font-size: 10px; */
font-size: 0.7em;
padding-bottom: 2em;
}
.no-js #flow-diagram {
display: none
}
.diagram-grid {
display: grid;
overflow-x: auto;
}
.platform, .step, .dots, .block-container, .action {
display: contents;
}
.block, .dot {
position: relative;
margin: 2em 0.5em;
}
.block {
background-color: #eee;
/* margin: 0.5em auto; */
padding: 0.5em;
max-width: 6em;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.dot {
/* margin: 0.5em; */
display: flex;
align-items: center;
justify-content: center;
}
.dot-graphic {
width: 1.3em;
height: 1.3em;
border-radius: 50%;
background: #DADADA;
}
a.action {
color: inherit;
}
.action.tooltip-open .dot-graphic {
background-color: #416EDA;
}
.action.tooltip-open .block {
background-color: #416EDA;
color: white;
}
.grid-column-label {
height: 2em;
position: relative;
display: flex;
align-items: center;
justify-content: center;
margin: 0 0.5em;
}
.grid-column-label::before {
content: "";
height: 1px;
background-color: currentColor;
position: absolute;
top: calc(50% - 0.5px);
left: 0;
right: 0;
}
.grid-column-label .label {
background: #fcfcfc;
padding: 0 1em;
position: relative;
}
.grid-row-label {
grid-column: 1 / span 1;
display: flex;
align-items: center;
justify-content: flex-end;
text-align: right;
background: #fcfcfc;
position: relative;
padding-right: 1em;
font-size: 1.1em;
}
.grid-line {
grid-column: 1 / -1;
grid-row-end: span 1;
position: relative;
}
.grid-line::after {
content: "";
height: 1px;
background-color: #979797;
position: absolute;
top: calc(50% - 1px);
left: 0;
right: 0;
}
.grid-outline {
grid-row-end: span 1;
position: relative;
color: #666666;
}
.grid-outline.docker {
grid-row: 3 / span 1;
grid-column: 3 / -2;
}
.grid-outline.testVenv {
grid-column: 10 / span 3;
}
.grid-outline .outline {
position: absolute;
border: 1px solid #e5e5e5;
padding: 1px 3px;
}
.grid-outline.docker .outline {
left: 0;
right: 0;
top: 0;
bottom: 0;
}
.grid-outline.testVenv .outline {
left: 0;
right: 0;
top: 0.5em;
bottom: 0.5em;
}
/* style the tooltip */
.tippy-box {
background: white;
box-shadow: 0 2px 6px -1px rgba(0,0,0,0.12);
border-radius: 0;
text-align: left;
font-size: 1em;
color: #1F1F1F;
border: 1px solid rgba(0, 0, 0, 0.02);
width: 14em;
min-width: min-content;
}
.tippy-box[data-placement^='top'] > .tippy-arrow::before {
border-top-color: white;
}
.tippy-box[data-placement^='bottom'] > .tippy-arrow::before {
border-bottom-color: white;
}
.tippy-box[data-placement^='left'] > .tippy-arrow::before {
border-left-color: white;
}
.tippy-box[data-placement^='right'] > .tippy-arrow::before {
border-right-color: white;
}
a.tooltip-contents {
color: inherit;
text-decoration: none;
display: block;
}
.tooltip-title {
font-weight: 600;
font-size: 1.1em;
color: #416EDA;
}
.tooltip-tag {
font-weight: 500;
text-transform: uppercase;
font-size: 0.9em;
color: #888888;
}
.tooltip-description {
margin-top: 1px;
margin-bottom: 2px;
}
</style>