style: activate string normalization
This commit is contained in:
committed by
Henry Schreiner
parent
d1900f6a05
commit
bda76b21cb
+42
-42
@@ -17,14 +17,14 @@ import cibuildwheel
|
|||||||
|
|
||||||
config = [
|
config = [
|
||||||
# file path, version find/replace format
|
# file path, version find/replace format
|
||||||
('README.md', "cibuildwheel=={}"),
|
("README.md", "cibuildwheel=={}"),
|
||||||
('cibuildwheel/__init__.py', "__version__ = '{}'"),
|
("cibuildwheel/__init__.py", "__version__ = '{}'"),
|
||||||
('docs/faq.md', "cibuildwheel=={}"),
|
("docs/faq.md", "cibuildwheel=={}"),
|
||||||
('docs/faq.md', "cibuildwheel@v{}"),
|
("docs/faq.md", "cibuildwheel@v{}"),
|
||||||
('docs/setup.md', "cibuildwheel=={}"),
|
("docs/setup.md", "cibuildwheel=={}"),
|
||||||
('examples/*', "cibuildwheel=={}"),
|
("examples/*", "cibuildwheel=={}"),
|
||||||
('examples/*', "cibuildwheel@v{}"),
|
("examples/*", "cibuildwheel@v{}"),
|
||||||
('setup.cfg', "version = {}"),
|
("setup.cfg", "version = {}"),
|
||||||
]
|
]
|
||||||
|
|
||||||
RED = "\u001b[31m"
|
RED = "\u001b[31m"
|
||||||
@@ -39,32 +39,32 @@ def bump_version() -> None:
|
|||||||
try:
|
try:
|
||||||
commit_date_str = subprocess.run(
|
commit_date_str = subprocess.run(
|
||||||
[
|
[
|
||||||
'git',
|
"git",
|
||||||
'show',
|
"show",
|
||||||
'--no-patch',
|
"--no-patch",
|
||||||
'--pretty=format:%ci',
|
"--pretty=format:%ci",
|
||||||
f'v{current_version}^{{commit}}',
|
f"v{current_version}^{{commit}}",
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
encoding='utf8',
|
encoding="utf8",
|
||||||
).stdout
|
).stdout
|
||||||
cd_date, cd_time, cd_tz = commit_date_str.split(' ')
|
cd_date, cd_time, cd_tz = commit_date_str.split(" ")
|
||||||
|
|
||||||
url_opts = urllib.parse.urlencode({'q': f'is:pr merged:>{cd_date}T{cd_time}{cd_tz}'})
|
url_opts = urllib.parse.urlencode({"q": f"is:pr merged:>{cd_date}T{cd_time}{cd_tz}"})
|
||||||
url = f'https://github.com/joerick/cibuildwheel/pulls?{url_opts}'
|
url = f"https://github.com/joerick/cibuildwheel/pulls?{url_opts}"
|
||||||
|
|
||||||
print(f'PRs merged since last release:\n {url}')
|
print(f"PRs merged since last release:\n {url}")
|
||||||
print()
|
print()
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
print(e)
|
print(e)
|
||||||
print('Failed to get previous version tag information.')
|
print("Failed to get previous version tag information.")
|
||||||
|
|
||||||
git_changes_result = subprocess.run(['git diff-index --quiet HEAD --'], shell=True)
|
git_changes_result = subprocess.run(["git diff-index --quiet HEAD --"], shell=True)
|
||||||
repo_has_uncommitted_changes = git_changes_result.returncode != 0
|
repo_has_uncommitted_changes = git_changes_result.returncode != 0
|
||||||
|
|
||||||
if repo_has_uncommitted_changes:
|
if repo_has_uncommitted_changes:
|
||||||
print('error: Uncommitted changes detected.')
|
print("error: Uncommitted changes detected.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
@@ -76,7 +76,7 @@ def bump_version() -> None:
|
|||||||
Version(new_version)
|
Version(new_version)
|
||||||
except InvalidVersion:
|
except InvalidVersion:
|
||||||
print("error: This version doesn't conform to PEP440")
|
print("error: This version doesn't conform to PEP440")
|
||||||
print(' https://www.python.org/dev/peps/pep-0440/')
|
print(" https://www.python.org/dev/peps/pep-0440/")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
actions = []
|
actions = []
|
||||||
@@ -93,7 +93,7 @@ def bump_version() -> None:
|
|||||||
found_at_least_one_file_needing_update = False
|
found_at_least_one_file_needing_update = False
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
contents = path.read_text(encoding='utf8')
|
contents = path.read_text(encoding="utf8")
|
||||||
if find_pattern in contents:
|
if find_pattern in contents:
|
||||||
found_at_least_one_file_needing_update = True
|
found_at_least_one_file_needing_update = True
|
||||||
actions.append(
|
actions.append(
|
||||||
@@ -114,33 +114,33 @@ def bump_version() -> None:
|
|||||||
|
|
||||||
for action in actions:
|
for action in actions:
|
||||||
path, find, replace = action
|
path, find, replace = action
|
||||||
print(f'{path} {RED}{find}{OFF} → {GREEN}{replace}{OFF}')
|
print(f"{path} {RED}{find}{OFF} → {GREEN}{replace}{OFF}")
|
||||||
|
|
||||||
print(f'Then commit, and tag as v{new_version}')
|
print(f"Then commit, and tag as v{new_version}")
|
||||||
|
|
||||||
answer = input('Proceed? [y/N] ').strip()
|
answer = input("Proceed? [y/N] ").strip()
|
||||||
|
|
||||||
if answer != 'y':
|
if answer != "y":
|
||||||
print('Aborted')
|
print("Aborted")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
for path, find, replace in actions:
|
for path, find, replace in actions:
|
||||||
contents = path.read_text(encoding='utf8')
|
contents = path.read_text(encoding="utf8")
|
||||||
contents = contents.replace(find, replace)
|
contents = contents.replace(find, replace)
|
||||||
path.write_text(contents, encoding='utf8')
|
path.write_text(contents, encoding="utf8")
|
||||||
|
|
||||||
print('Files updated. If you want to update the changelog as part of this')
|
print("Files updated. If you want to update the changelog as part of this")
|
||||||
print('commit, do that now.')
|
print("commit, do that now.")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
while input('Type "done" to continue: ').strip().lower() != 'done':
|
while input('Type "done" to continue: ').strip().lower() != "done":
|
||||||
pass
|
pass
|
||||||
|
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
'git',
|
"git",
|
||||||
'commit',
|
"commit",
|
||||||
'--all',
|
"--all",
|
||||||
f"--message=Bump version: v{new_version}",
|
f"--message=Bump version: v{new_version}",
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
@@ -148,18 +148,18 @@ def bump_version() -> None:
|
|||||||
|
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
'git',
|
"git",
|
||||||
'tag',
|
"tag",
|
||||||
'--annotate',
|
"--annotate",
|
||||||
f"--message=v{new_version}",
|
f"--message=v{new_version}",
|
||||||
f'v{new_version}',
|
f"v{new_version}",
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
print('Done.')
|
print("Done.")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
os.chdir(Path(__file__).parent.parent.resolve())
|
os.chdir(Path(__file__).parent.parent.resolve())
|
||||||
bump_version()
|
bump_version()
|
||||||
|
|||||||
@@ -17,66 +17,66 @@ def shell(cmd, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def git_repo_has_changes():
|
def git_repo_has_changes():
|
||||||
unstaged_changes = shell('git diff-index --quiet HEAD --').returncode != 0
|
unstaged_changes = shell("git diff-index --quiet HEAD --").returncode != 0
|
||||||
staged_changes = shell('git diff-index --quiet --cached HEAD --').returncode != 0
|
staged_changes = shell("git diff-index --quiet --cached HEAD --").returncode != 0
|
||||||
return unstaged_changes or staged_changes
|
return unstaged_changes or staged_changes
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
def main():
|
def main():
|
||||||
project_root = Path(__file__).parent / '..'
|
project_root = Path(__file__).parent / ".."
|
||||||
os.chdir(project_root)
|
os.chdir(project_root)
|
||||||
|
|
||||||
if git_repo_has_changes():
|
if git_repo_has_changes():
|
||||||
print('Your git repo has uncommitted changes. Commit or stash before continuing.')
|
print("Your git repo has uncommitted changes. Commit or stash before continuing.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
previous_branch = shell(
|
previous_branch = shell(
|
||||||
'git rev-parse --abbrev-ref HEAD', check=True, capture_output=True, encoding='utf8'
|
"git rev-parse --abbrev-ref HEAD", check=True, capture_output=True, encoding="utf8"
|
||||||
).stdout.strip()
|
).stdout.strip()
|
||||||
|
|
||||||
shell('git fetch origin', check=True)
|
shell("git fetch origin", check=True)
|
||||||
|
|
||||||
timestamp = time.strftime('%Y-%m-%dT%H-%M-%S', time.gmtime())
|
timestamp = time.strftime("%Y-%m-%dT%H-%M-%S", time.gmtime())
|
||||||
branch_name = f'update-constraints-{timestamp}'
|
branch_name = f"update-constraints-{timestamp}"
|
||||||
|
|
||||||
shell(f'git checkout -b {branch_name} origin/master', check=True)
|
shell(f"git checkout -b {branch_name} origin/master", check=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shell('bin/update_dependencies.py', check=True)
|
shell("bin/update_dependencies.py", check=True)
|
||||||
|
|
||||||
if not git_repo_has_changes():
|
if not git_repo_has_changes():
|
||||||
print('Done: no constraint updates required.')
|
print("Done: no constraint updates required.")
|
||||||
return
|
return
|
||||||
|
|
||||||
shell('git commit -a -m "Update dependencies"', check=True)
|
shell('git commit -a -m "Update dependencies"', check=True)
|
||||||
body = textwrap.dedent(
|
body = textwrap.dedent(
|
||||||
f'''
|
f"""
|
||||||
Update the versions of our dependencies.
|
Update the versions of our dependencies.
|
||||||
|
|
||||||
PR generated by `{os.path.basename(__file__)}`.
|
PR generated by `{os.path.basename(__file__)}`.
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
run(
|
run(
|
||||||
[
|
[
|
||||||
'gh',
|
"gh",
|
||||||
'pr',
|
"pr",
|
||||||
'create',
|
"create",
|
||||||
'--repo=joerick/cibuildwheel',
|
"--repo=joerick/cibuildwheel",
|
||||||
'--base=master',
|
"--base=master",
|
||||||
"--title=Update dependencies",
|
"--title=Update dependencies",
|
||||||
f"--body='{body}'",
|
f"--body='{body}'",
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
print('Done.')
|
print("Done.")
|
||||||
finally:
|
finally:
|
||||||
# remove any local changes
|
# remove any local changes
|
||||||
shell('git checkout -- .')
|
shell("git checkout -- .")
|
||||||
shell(f'git checkout {previous_branch}', check=True)
|
shell(f"git checkout {previous_branch}", check=True)
|
||||||
shell(f'git branch -D --force {branch_name}', check=True)
|
shell(f"git branch -D --force {branch_name}", check=True)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main.main(standalone_mode=True)
|
main.main(standalone_mode=True)
|
||||||
|
|||||||
+8
-8
@@ -110,7 +110,7 @@ class Project:
|
|||||||
return " ".join(f"![{icon} icon][]" for icon in self.os)
|
return " ".join(f"![{icon} icon][]" for icon in self.os)
|
||||||
|
|
||||||
def table_row(self) -> str:
|
def table_row(self) -> str:
|
||||||
notes = self.notes.replace('\n', ' ')
|
notes = self.notes.replace("\n", " ")
|
||||||
return f"| {self.namelink: <{self.NAME}} | {self.ci_icons} | {self.os_icons} | {notes} |"
|
return f"| {self.namelink: <{self.NAME}} | {self.ci_icons} | {self.os_icons} | {notes} |"
|
||||||
|
|
||||||
def links(self) -> str:
|
def links(self) -> str:
|
||||||
@@ -122,25 +122,25 @@ class Project:
|
|||||||
|
|
||||||
|
|
||||||
def fetch_icon(icon_name: str) -> None:
|
def fetch_icon(icon_name: str) -> None:
|
||||||
url = f'https://cdn.jsdelivr.net/npm/simple-icons@v4/icons/{icon_name}.svg'
|
url = f"https://cdn.jsdelivr.net/npm/simple-icons@v4/icons/{icon_name}.svg"
|
||||||
with urllib.request.urlopen(url) as f:
|
with urllib.request.urlopen(url) as f:
|
||||||
original_svg_data = f.read()
|
original_svg_data = f.read()
|
||||||
|
|
||||||
document = xml.dom.minidom.parseString(original_svg_data)
|
document = xml.dom.minidom.parseString(original_svg_data)
|
||||||
svgElement = document.documentElement
|
svgElement = document.documentElement
|
||||||
assert svgElement.nodeName == 'svg'
|
assert svgElement.nodeName == "svg"
|
||||||
svgElement.setAttribute('width', '16px')
|
svgElement.setAttribute("width", "16px")
|
||||||
svgElement.setAttribute('fill', '#606060')
|
svgElement.setAttribute("fill", "#606060")
|
||||||
|
|
||||||
icon_path = path_for_icon(icon_name)
|
icon_path = path_for_icon(icon_name)
|
||||||
icon_path.parent.mkdir(parents=True, exist_ok=True)
|
icon_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with open(path_for_icon(icon_name), 'w') as f:
|
with open(path_for_icon(icon_name), "w") as f:
|
||||||
f.write(svgElement.toxml())
|
f.write(svgElement.toxml())
|
||||||
|
|
||||||
|
|
||||||
def path_for_icon(icon_name: str) -> Path:
|
def path_for_icon(icon_name: str) -> Path:
|
||||||
return Path('.') / 'docs' / 'data' / 'readme_icons' / f'{icon_name}.svg'
|
return Path(".") / "docs" / "data" / "readme_icons" / f"{icon_name}.svg"
|
||||||
|
|
||||||
|
|
||||||
def str_projects(
|
def str_projects(
|
||||||
@@ -200,7 +200,7 @@ def projects(
|
|||||||
start_str = "<!-- START bin/projects.py -->\n"
|
start_str = "<!-- START bin/projects.py -->\n"
|
||||||
start = text.find(start_str)
|
start = text.find(start_str)
|
||||||
end = text.find("<!-- END bin/projects.py -->\n")
|
end = text.find("<!-- END bin/projects.py -->\n")
|
||||||
generated_note = f'<!-- this section is generated by bin/projects.py. Don\'t edit it directly, instead, edit {input.name} -->'
|
generated_note = f"<!-- this section is generated by bin/projects.py. Don't edit it directly, instead, edit {input.name} -->"
|
||||||
new_text = f"{text[:start + len(start_str)]}\n{generated_note}\n\n{output}\n{text[end:]}"
|
new_text = f"{text[:start + len(start_str)]}\n{generated_note}\n\n{output}\n{text[end:]}"
|
||||||
|
|
||||||
readme.seek(0)
|
readme.seek(0)
|
||||||
|
|||||||
@@ -21,98 +21,98 @@ def shell(cmd, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def git_repo_has_changes():
|
def git_repo_has_changes():
|
||||||
unstaged_changes = shell('git diff-index --quiet HEAD --').returncode != 0
|
unstaged_changes = shell("git diff-index --quiet HEAD --").returncode != 0
|
||||||
staged_changes = shell('git diff-index --quiet --cached HEAD --').returncode != 0
|
staged_changes = shell("git diff-index --quiet --cached HEAD --").returncode != 0
|
||||||
return unstaged_changes or staged_changes
|
return unstaged_changes or staged_changes
|
||||||
|
|
||||||
|
|
||||||
def generate_basic_project(path):
|
def generate_basic_project(path):
|
||||||
sys.path.insert(0, '')
|
sys.path.insert(0, "")
|
||||||
from test.test_projects.c import new_c_project
|
from test.test_projects.c import new_c_project
|
||||||
|
|
||||||
project = new_c_project()
|
project = new_c_project()
|
||||||
project.generate(path)
|
project.generate(path)
|
||||||
|
|
||||||
|
|
||||||
CIService = namedtuple('CIService', 'name dst_config_path badge_md')
|
CIService = namedtuple("CIService", "name dst_config_path badge_md")
|
||||||
services = [
|
services = [
|
||||||
CIService(
|
CIService(
|
||||||
name='appveyor',
|
name="appveyor",
|
||||||
dst_config_path='appveyor.yml',
|
dst_config_path="appveyor.yml",
|
||||||
badge_md='[](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/{branch})',
|
badge_md="[](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/{branch})",
|
||||||
),
|
),
|
||||||
CIService(
|
CIService(
|
||||||
name='azure-pipelines',
|
name="azure-pipelines",
|
||||||
dst_config_path='azure-pipelines.yml',
|
dst_config_path="azure-pipelines.yml",
|
||||||
badge_md='[](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=2&branchName={branch})',
|
badge_md="[](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=2&branchName={branch})",
|
||||||
),
|
),
|
||||||
CIService(
|
CIService(
|
||||||
name='circleci',
|
name="circleci",
|
||||||
dst_config_path='.circleci/config.yml',
|
dst_config_path=".circleci/config.yml",
|
||||||
badge_md='[](https://circleci.com/gh/joerick/cibuildwheel/tree/{branch})',
|
badge_md="[](https://circleci.com/gh/joerick/cibuildwheel/tree/{branch})",
|
||||||
),
|
),
|
||||||
CIService(
|
CIService(
|
||||||
name='github',
|
name="github",
|
||||||
dst_config_path='.github/workflows/example.yml',
|
dst_config_path=".github/workflows/example.yml",
|
||||||
badge_md='[](https://github.com/joerick/cibuildwheel/actions)',
|
badge_md="[](https://github.com/joerick/cibuildwheel/actions)",
|
||||||
),
|
),
|
||||||
CIService(
|
CIService(
|
||||||
name='travis-ci',
|
name="travis-ci",
|
||||||
dst_config_path='.travis.yml',
|
dst_config_path=".travis.yml",
|
||||||
badge_md='[](https://travis-ci.org/joerick/cibuildwheel)',
|
badge_md="[](https://travis-ci.org/joerick/cibuildwheel)",
|
||||||
),
|
),
|
||||||
CIService(
|
CIService(
|
||||||
name='gitlab',
|
name="gitlab",
|
||||||
dst_config_path='.gitlab-ci.yml',
|
dst_config_path=".gitlab-ci.yml",
|
||||||
badge_md='[](https://gitlab.com/joerick/cibuildwheel/-/commits/{branch})',
|
badge_md="[](https://gitlab.com/joerick/cibuildwheel/-/commits/{branch})",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def ci_service_for_config_file(config_file):
|
def ci_service_for_config_file(config_file):
|
||||||
service_name = Path(config_file).name.rsplit('-', 1)[0]
|
service_name = Path(config_file).name.rsplit("-", 1)[0]
|
||||||
|
|
||||||
for service in services:
|
for service in services:
|
||||||
if service.name == service_name:
|
if service.name == service_name:
|
||||||
return service
|
return service
|
||||||
|
|
||||||
raise ValueError(f'unknown ci service for config file {config_file}')
|
raise ValueError(f"unknown ci service for config file {config_file}")
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.argument('config_files', nargs=-1, type=click.Path())
|
@click.argument("config_files", nargs=-1, type=click.Path())
|
||||||
def run_example_ci_configs(config_files=None):
|
def run_example_ci_configs(config_files=None):
|
||||||
'''
|
"""
|
||||||
Test the example configs. If no files are specified, will test
|
Test the example configs. If no files are specified, will test
|
||||||
examples/*-minimal.yml
|
examples/*-minimal.yml
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if len(config_files) == 0:
|
if len(config_files) == 0:
|
||||||
config_files = glob('examples/*-minimal.yml')
|
config_files = glob("examples/*-minimal.yml")
|
||||||
|
|
||||||
# check each CI service has at most 1 config file
|
# check each CI service has at most 1 config file
|
||||||
configs_by_service = {}
|
configs_by_service = {}
|
||||||
for config_file in config_files:
|
for config_file in config_files:
|
||||||
service = ci_service_for_config_file(config_file)
|
service = ci_service_for_config_file(config_file)
|
||||||
if service.name in configs_by_service:
|
if service.name in configs_by_service:
|
||||||
raise Exception('You cannot specify more than one config per CI service')
|
raise Exception("You cannot specify more than one config per CI service")
|
||||||
configs_by_service[service.name] = config_file
|
configs_by_service[service.name] = config_file
|
||||||
|
|
||||||
if git_repo_has_changes():
|
if git_repo_has_changes():
|
||||||
print('Your git repo has uncommitted changes. Commit or stash before continuing.')
|
print("Your git repo has uncommitted changes. Commit or stash before continuing.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
previous_branch = shell(
|
previous_branch = shell(
|
||||||
'git rev-parse --abbrev-ref HEAD', check=True, capture_output=True, encoding='utf8'
|
"git rev-parse --abbrev-ref HEAD", check=True, capture_output=True, encoding="utf8"
|
||||||
).stdout.strip()
|
).stdout.strip()
|
||||||
|
|
||||||
timestamp = time.strftime('%Y-%m-%dT%H-%M-%S', time.gmtime())
|
timestamp = time.strftime("%Y-%m-%dT%H-%M-%S", time.gmtime())
|
||||||
branch_name = f'example-config-test---{previous_branch}-{timestamp}'
|
branch_name = f"example-config-test---{previous_branch}-{timestamp}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shell(f'git checkout --orphan {branch_name}', check=True)
|
shell(f"git checkout --orphan {branch_name}", check=True)
|
||||||
|
|
||||||
example_project = Path('example_root')
|
example_project = Path("example_root")
|
||||||
generate_basic_project(example_project)
|
generate_basic_project(example_project)
|
||||||
|
|
||||||
for config_file in config_files:
|
for config_file in config_files:
|
||||||
@@ -123,48 +123,48 @@ def run_example_ci_configs(config_files=None):
|
|||||||
dst_config_file.parent.mkdir(parents=True, exist_ok=True)
|
dst_config_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
shutil.copyfile(src_config_file, dst_config_file)
|
shutil.copyfile(src_config_file, dst_config_file)
|
||||||
|
|
||||||
run(['git', 'add', example_project], check=True)
|
run(["git", "add", example_project], check=True)
|
||||||
message = textwrap.dedent(
|
message = textwrap.dedent(
|
||||||
f'''\
|
f"""\
|
||||||
Test example minimal configs
|
Test example minimal configs
|
||||||
|
|
||||||
Testing files: {config_files}
|
Testing files: {config_files}
|
||||||
Generated from branch: {previous_branch}
|
Generated from branch: {previous_branch}
|
||||||
Time: {timestamp}
|
Time: {timestamp}
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
run(['git', 'commit', '--no-verify', '--message', message], check=True)
|
run(["git", "commit", "--no-verify", "--message", message], check=True)
|
||||||
shell(f'git subtree --prefix={example_project} push origin {branch_name}', check=True)
|
shell(f"git subtree --prefix={example_project} push origin {branch_name}", check=True)
|
||||||
|
|
||||||
print('---')
|
print("---")
|
||||||
print()
|
print()
|
||||||
print('> **Examples test run**')
|
print("> **Examples test run**")
|
||||||
print('> ')
|
print("> ")
|
||||||
print(
|
print(
|
||||||
f'> Branch: [{branch_name}](https://github.com/joerick/cibuildwheel/tree/{branch_name})'
|
f"> Branch: [{branch_name}](https://github.com/joerick/cibuildwheel/tree/{branch_name})"
|
||||||
)
|
)
|
||||||
print('> ')
|
print("> ")
|
||||||
print('> | Service | Config | Status |')
|
print("> | Service | Config | Status |")
|
||||||
print('> |---|---|---|')
|
print("> |---|---|---|")
|
||||||
for config_file in config_files:
|
for config_file in config_files:
|
||||||
service = ci_service_for_config_file(config_file)
|
service = ci_service_for_config_file(config_file)
|
||||||
badge = service.badge_md.format(
|
badge = service.badge_md.format(
|
||||||
branch=branch_name, branch_escaped=quote(branch_name, safe='')
|
branch=branch_name, branch_escaped=quote(branch_name, safe="")
|
||||||
)
|
)
|
||||||
print(f'> | {service.name} | `{config_file}` | {badge} |')
|
print(f"> | {service.name} | `{config_file}` | {badge} |")
|
||||||
print('> ')
|
print("> ")
|
||||||
print('> Generated by `bin/run_example_ci_config.py`')
|
print("> Generated by `bin/run_example_ci_config.py`")
|
||||||
print()
|
print()
|
||||||
print('---')
|
print("---")
|
||||||
finally:
|
finally:
|
||||||
# remove any local changes
|
# remove any local changes
|
||||||
shutil.rmtree(example_project, ignore_errors=True)
|
shutil.rmtree(example_project, ignore_errors=True)
|
||||||
shell('git checkout -- .')
|
shell("git checkout -- .")
|
||||||
shell(f'git checkout {previous_branch}', check=True)
|
shell(f"git checkout {previous_branch}", check=True)
|
||||||
shell(f'git branch -D --force {branch_name}', check=True)
|
shell(f"git branch -D --force {branch_name}", check=True)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
os.chdir(os.path.dirname(__file__))
|
os.chdir(os.path.dirname(__file__))
|
||||||
os.chdir('..')
|
os.chdir("..")
|
||||||
run_example_ci_configs(standalone_mode=True)
|
run_example_ci_configs(standalone_mode=True)
|
||||||
|
|||||||
+5
-5
@@ -5,19 +5,19 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
# move cwd to the project root
|
# move cwd to the project root
|
||||||
os.chdir(Path(__file__).resolve().parents[1])
|
os.chdir(Path(__file__).resolve().parents[1])
|
||||||
|
|
||||||
# run the unit tests
|
# run the unit tests
|
||||||
unit_test_args = [sys.executable, '-m', 'pytest', 'unit_test']
|
unit_test_args = [sys.executable, "-m", "pytest", "unit_test"]
|
||||||
# run the docker unit tests only on Linux
|
# run the docker unit tests only on Linux
|
||||||
if sys.platform.startswith('linux'):
|
if sys.platform.startswith("linux"):
|
||||||
unit_test_args += ['--run-docker']
|
unit_test_args += ["--run-docker"]
|
||||||
subprocess.run(unit_test_args, check=True)
|
subprocess.run(unit_test_args, check=True)
|
||||||
|
|
||||||
# run the integration tests
|
# run the integration tests
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[sys.executable, '-m', 'pytest', '-x', '--durations', '0', '--timeout=2400', 'test'],
|
[sys.executable, "-m", "pytest", "-x", "--durations", "0", "--timeout=2400", "test"],
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
|
|||||||
+5
-5
@@ -9,19 +9,19 @@ import sys
|
|||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
# move cwd to the project root
|
# move cwd to the project root
|
||||||
os.chdir(Path(__file__).resolve().parents[1])
|
os.chdir(Path(__file__).resolve().parents[1])
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Runs a sample build')
|
parser = argparse.ArgumentParser(description="Runs a sample build")
|
||||||
parser.add_argument('project_python_path', nargs='?', default='test.test_0_basic.basic_project')
|
parser.add_argument("project_python_path", nargs="?", default="test.test_0_basic.basic_project")
|
||||||
|
|
||||||
options = parser.parse_args()
|
options = parser.parse_args()
|
||||||
|
|
||||||
project_dir = tempfile.mkdtemp()
|
project_dir = tempfile.mkdtemp()
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[sys.executable, '-m', 'test.test_projects', options.project_python_path, project_dir],
|
[sys.executable, "-m", "test.test_projects", options.project_python_path, project_dir],
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
sys.exit(subprocess.run([sys.executable, '-m', 'cibuildwheel'], cwd=project_dir).returncode)
|
sys.exit(subprocess.run([sys.executable, "-m", "cibuildwheel"], cwd=project_dir).returncode)
|
||||||
|
|||||||
+56
-56
@@ -11,50 +11,50 @@ from typing import NamedTuple
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
os.chdir(os.path.dirname(__file__))
|
os.chdir(os.path.dirname(__file__))
|
||||||
os.chdir('..')
|
os.chdir("..")
|
||||||
|
|
||||||
# CUSTOM_COMPILE_COMMAND is a pip-compile option that tells users how to
|
# CUSTOM_COMPILE_COMMAND is a pip-compile option that tells users how to
|
||||||
# regenerate the constraints files
|
# regenerate the constraints files
|
||||||
os.environ['CUSTOM_COMPILE_COMMAND'] = "bin/update_dependencies.py"
|
os.environ["CUSTOM_COMPILE_COMMAND"] = "bin/update_dependencies.py"
|
||||||
|
|
||||||
PYTHON_VERSIONS = ['27', '35', '36', '37', '38', '39']
|
PYTHON_VERSIONS = ["27", "35", "36", "37", "38", "39"]
|
||||||
|
|
||||||
if '--no-docker' in sys.argv:
|
if "--no-docker" in sys.argv:
|
||||||
for python_version in PYTHON_VERSIONS:
|
for python_version in PYTHON_VERSIONS:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
f'./env{python_version}/bin/pip-compile',
|
f"./env{python_version}/bin/pip-compile",
|
||||||
'--allow-unsafe',
|
"--allow-unsafe",
|
||||||
'--upgrade',
|
"--upgrade",
|
||||||
'cibuildwheel/resources/constraints.in',
|
"cibuildwheel/resources/constraints.in",
|
||||||
'--output-file=cibuildwheel/resources/constraints-python{python_version}.txt',
|
"--output-file=cibuildwheel/resources/constraints-python{python_version}.txt",
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# latest manylinux2010 image with cpython 2.7 support
|
# latest manylinux2010 image with cpython 2.7 support
|
||||||
image_runner = 'quay.io/pypa/manylinux2010_x86_64:2021-02-06-3d322a5'
|
image_runner = "quay.io/pypa/manylinux2010_x86_64:2021-02-06-3d322a5"
|
||||||
subprocess.run(['docker', 'pull', image_runner], check=True)
|
subprocess.run(["docker", "pull", image_runner], check=True)
|
||||||
for python_version in PYTHON_VERSIONS:
|
for python_version in PYTHON_VERSIONS:
|
||||||
abi_flags = '' if int(python_version) >= 38 else 'm'
|
abi_flags = "" if int(python_version) >= 38 else "m"
|
||||||
python_path = f'/opt/python/cp{python_version}-cp{python_version}{abi_flags}/bin/'
|
python_path = f"/opt/python/cp{python_version}-cp{python_version}{abi_flags}/bin/"
|
||||||
command = (
|
command = (
|
||||||
f'{python_path}pip install pip-tools && '
|
f"{python_path}pip install pip-tools && "
|
||||||
'{python_path}pip-compile --allow-unsafe --upgrade '
|
"{python_path}pip-compile --allow-unsafe --upgrade "
|
||||||
'cibuildwheel/resources/constraints.in '
|
"cibuildwheel/resources/constraints.in "
|
||||||
f'--output-file cibuildwheel/resources/constraints-python{python_version}.txt'
|
f"--output-file cibuildwheel/resources/constraints-python{python_version}.txt"
|
||||||
)
|
)
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
'docker',
|
"docker",
|
||||||
'run',
|
"run",
|
||||||
'--rm',
|
"--rm",
|
||||||
'--env=CUSTOM_COMPILE_COMMAND',
|
"--env=CUSTOM_COMPILE_COMMAND",
|
||||||
"--volume={os.getcwd()}:/volume",
|
"--volume={os.getcwd()}:/volume",
|
||||||
'--workdir=/volume',
|
"--workdir=/volume",
|
||||||
image_runner,
|
image_runner,
|
||||||
'bash',
|
"bash",
|
||||||
'-c',
|
"-c",
|
||||||
command,
|
command,
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
@@ -62,8 +62,8 @@ else:
|
|||||||
|
|
||||||
# default constraints.txt
|
# default constraints.txt
|
||||||
shutil.copyfile(
|
shutil.copyfile(
|
||||||
f'cibuildwheel/resources/constraints-python{PYTHON_VERSIONS[-1]}.txt',
|
f"cibuildwheel/resources/constraints-python{PYTHON_VERSIONS[-1]}.txt",
|
||||||
'cibuildwheel/resources/constraints.txt',
|
"cibuildwheel/resources/constraints.txt",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -75,24 +75,24 @@ class Image(NamedTuple):
|
|||||||
|
|
||||||
|
|
||||||
images = [
|
images = [
|
||||||
Image('manylinux1', 'x86_64', 'quay.io/pypa/manylinux1_x86_64', None),
|
Image("manylinux1", "x86_64", "quay.io/pypa/manylinux1_x86_64", None),
|
||||||
Image('manylinux1', 'i686', 'quay.io/pypa/manylinux1_i686', None),
|
Image("manylinux1", "i686", "quay.io/pypa/manylinux1_i686", None),
|
||||||
# Images for manylinux2010 are pinned to the latest tag supporting cp27
|
# Images for manylinux2010 are pinned to the latest tag supporting cp27
|
||||||
Image('manylinux2010', 'x86_64', 'quay.io/pypa/manylinux2010_x86_64', '2021-02-06-3d322a5'),
|
Image("manylinux2010", "x86_64", "quay.io/pypa/manylinux2010_x86_64", "2021-02-06-3d322a5"),
|
||||||
Image('manylinux2010', 'i686', 'quay.io/pypa/manylinux2010_i686', '2021-02-06-3d322a5'),
|
Image("manylinux2010", "i686", "quay.io/pypa/manylinux2010_i686", "2021-02-06-3d322a5"),
|
||||||
Image('manylinux2010', 'pypy_x86_64', 'pypywheels/manylinux2010-pypy_x86_64', None),
|
Image("manylinux2010", "pypy_x86_64", "pypywheels/manylinux2010-pypy_x86_64", None),
|
||||||
# 2014 images
|
# 2014 images
|
||||||
Image('manylinux2014', 'x86_64', 'quay.io/pypa/manylinux2014_x86_64', None),
|
Image("manylinux2014", "x86_64", "quay.io/pypa/manylinux2014_x86_64", None),
|
||||||
Image('manylinux2014', 'i686', 'quay.io/pypa/manylinux2014_i686', None),
|
Image("manylinux2014", "i686", "quay.io/pypa/manylinux2014_i686", None),
|
||||||
Image('manylinux2014', 'aarch64', 'quay.io/pypa/manylinux2014_aarch64', None),
|
Image("manylinux2014", "aarch64", "quay.io/pypa/manylinux2014_aarch64", None),
|
||||||
Image('manylinux2014', 'ppc64le', 'quay.io/pypa/manylinux2014_ppc64le', None),
|
Image("manylinux2014", "ppc64le", "quay.io/pypa/manylinux2014_ppc64le", None),
|
||||||
Image('manylinux2014', 's390x', 'quay.io/pypa/manylinux2014_s390x', None),
|
Image("manylinux2014", "s390x", "quay.io/pypa/manylinux2014_s390x", None),
|
||||||
# 2_24 images
|
# 2_24 images
|
||||||
Image('manylinux_2_24', 'x86_64', 'quay.io/pypa/manylinux_2_24_x86_64', None),
|
Image("manylinux_2_24", "x86_64", "quay.io/pypa/manylinux_2_24_x86_64", None),
|
||||||
Image('manylinux_2_24', 'i686', 'quay.io/pypa/manylinux_2_24_i686', None),
|
Image("manylinux_2_24", "i686", "quay.io/pypa/manylinux_2_24_i686", None),
|
||||||
Image('manylinux_2_24', 'aarch64', 'quay.io/pypa/manylinux_2_24_aarch64', None),
|
Image("manylinux_2_24", "aarch64", "quay.io/pypa/manylinux_2_24_aarch64", None),
|
||||||
Image('manylinux_2_24', 'ppc64le', 'quay.io/pypa/manylinux_2_24_ppc64le', None),
|
Image("manylinux_2_24", "ppc64le", "quay.io/pypa/manylinux_2_24_ppc64le", None),
|
||||||
Image('manylinux_2_24', 's390x', 'quay.io/pypa/manylinux_2_24_s390x', None),
|
Image("manylinux_2_24", "s390x", "quay.io/pypa/manylinux_2_24_s390x", None),
|
||||||
]
|
]
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
@@ -102,41 +102,41 @@ for image in images:
|
|||||||
if image.tag is not None:
|
if image.tag is not None:
|
||||||
# image has been pinned, do not update
|
# image has been pinned, do not update
|
||||||
tag_name = image.tag
|
tag_name = image.tag
|
||||||
elif image.image_name.startswith('quay.io/'):
|
elif image.image_name.startswith("quay.io/"):
|
||||||
_, _, repository_name = image.image_name.partition('/')
|
_, _, repository_name = image.image_name.partition("/")
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
f'https://quay.io/api/v1/repository/{repository_name}?includeTags=true'
|
f"https://quay.io/api/v1/repository/{repository_name}?includeTags=true"
|
||||||
)
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
repo_info = response.json()
|
repo_info = response.json()
|
||||||
tags_dict = repo_info['tags']
|
tags_dict = repo_info["tags"]
|
||||||
|
|
||||||
latest_tag = tags_dict.pop('latest')
|
latest_tag = tags_dict.pop("latest")
|
||||||
# find the tag whose manifest matches 'latest'
|
# find the tag whose manifest matches 'latest'
|
||||||
tag_name = next(
|
tag_name = next(
|
||||||
name
|
name
|
||||||
for (name, info) in tags_dict.items()
|
for (name, info) in tags_dict.items()
|
||||||
if info['manifest_digest'] == latest_tag['manifest_digest']
|
if info["manifest_digest"] == latest_tag["manifest_digest"]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
response = requests.get(f'https://hub.docker.com/v2/repositories/{image.image_name}/tags')
|
response = requests.get(f"https://hub.docker.com/v2/repositories/{image.image_name}/tags")
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
tags = response.json()['results']
|
tags = response.json()["results"]
|
||||||
|
|
||||||
latest_tag = next(tag for tag in tags if tag['name'] == 'latest')
|
latest_tag = next(tag for tag in tags if tag["name"] == "latest")
|
||||||
# i don't know what it would mean to have multiple images per tag
|
# i don't know what it would mean to have multiple images per tag
|
||||||
assert len(latest_tag['images']) == 1
|
assert len(latest_tag["images"]) == 1
|
||||||
digest = latest_tag['images'][0]['digest']
|
digest = latest_tag["images"][0]["digest"]
|
||||||
|
|
||||||
pinned_tag = next(
|
pinned_tag = next(
|
||||||
tag for tag in tags if tag != latest_tag and tag['images'][0]['digest'] == digest
|
tag for tag in tags if tag != latest_tag and tag["images"][0]["digest"] == digest
|
||||||
)
|
)
|
||||||
tag_name = pinned_tag['name']
|
tag_name = pinned_tag["name"]
|
||||||
|
|
||||||
if not config.has_section(image.platform):
|
if not config.has_section(image.platform):
|
||||||
config[image.platform] = {}
|
config[image.platform] = {}
|
||||||
|
|
||||||
config[image.platform][image.manylinux_version] = f'{image.image_name}:{tag_name}'
|
config[image.platform][image.manylinux_version] = f"{image.image_name}:{tag_name}"
|
||||||
|
|
||||||
with open('cibuildwheel/resources/pinned_docker_images.cfg', 'w') as f:
|
with open("cibuildwheel/resources/pinned_docker_images.cfg", "w") as f:
|
||||||
config.write(f)
|
config.write(f)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = '1.11.0'
|
__version__ = "1.11.0"
|
||||||
|
|||||||
+97
-97
@@ -46,7 +46,7 @@ def get_option_from_environment(
|
|||||||
def get_option_from_environment(
|
def get_option_from_environment(
|
||||||
option_name: str, *, platform: Optional[str] = None, default: Optional[str] = None
|
option_name: str, *, platform: Optional[str] = None, default: Optional[str] = None
|
||||||
) -> Optional[str]: # noqa: E302
|
) -> Optional[str]: # noqa: E302
|
||||||
'''
|
"""
|
||||||
Returns an option from the environment, optionally scoped by the platform.
|
Returns an option from the environment, optionally scoped by the platform.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@@ -54,9 +54,9 @@ def get_option_from_environment(
|
|||||||
|
|
||||||
This will return the value of CIBW_COLOR_MACOS if it exists, otherwise the value of
|
This will return the value of CIBW_COLOR_MACOS if it exists, otherwise the value of
|
||||||
CIBW_COLOR.
|
CIBW_COLOR.
|
||||||
'''
|
"""
|
||||||
if platform:
|
if platform:
|
||||||
option = os.environ.get(f'{option_name}_{platform.upper()}')
|
option = os.environ.get(f"{option_name}_{platform.upper()}")
|
||||||
if option is not None:
|
if option is not None:
|
||||||
return option
|
return option
|
||||||
|
|
||||||
@@ -67,158 +67,158 @@ def main() -> None:
|
|||||||
platform: PlatformName
|
platform: PlatformName
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Build wheels for all the platforms.',
|
description="Build wheels for all the platforms.",
|
||||||
epilog='''
|
epilog="""
|
||||||
Most options are supplied via environment variables.
|
Most options are supplied via environment variables.
|
||||||
See https://github.com/joerick/cibuildwheel#options for info.
|
See https://github.com/joerick/cibuildwheel#options for info.
|
||||||
''',
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--platform',
|
"--platform",
|
||||||
choices=['auto', 'linux', 'macos', 'windows'],
|
choices=["auto", "linux", "macos", "windows"],
|
||||||
default=os.environ.get('CIBW_PLATFORM', 'auto'),
|
default=os.environ.get("CIBW_PLATFORM", "auto"),
|
||||||
help='''
|
help="""
|
||||||
Platform to build for. For "linux" you need docker running, on Mac
|
Platform to build for. For "linux" you need docker running, on Mac
|
||||||
or Linux. For "macos", you need a Mac machine, and note that this
|
or Linux. For "macos", you need a Mac machine, and note that this
|
||||||
script is going to automatically install MacPython on your system,
|
script is going to automatically install MacPython on your system,
|
||||||
so don't run on your development machine. For "windows", you need to
|
so don't run on your development machine. For "windows", you need to
|
||||||
run in Windows, and it will build and test for all versions of
|
run in Windows, and it will build and test for all versions of
|
||||||
Python. Default: auto.
|
Python. Default: auto.
|
||||||
''',
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
arch_list_str = ", ".join(a.name for a in Architecture)
|
arch_list_str = ", ".join(a.name for a in Architecture)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--archs',
|
"--archs",
|
||||||
default=None,
|
default=None,
|
||||||
help=f'''
|
help=f"""
|
||||||
Comma-separated list of CPU architectures to build for.
|
Comma-separated list of CPU architectures to build for.
|
||||||
When set to 'auto', builds the architectures natively supported
|
When set to 'auto', builds the architectures natively supported
|
||||||
on this machine. Set this option to build an architecture
|
on this machine. Set this option to build an architecture
|
||||||
via emulation, for example, using binfmt_misc and QEMU.
|
via emulation, for example, using binfmt_misc and QEMU.
|
||||||
Default: auto.
|
Default: auto.
|
||||||
Choices: auto, auto64, auto32, native, all, {arch_list_str}
|
Choices: auto, auto64, auto32, native, all, {arch_list_str}
|
||||||
''',
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--output-dir',
|
"--output-dir",
|
||||||
default=os.environ.get('CIBW_OUTPUT_DIR', 'wheelhouse'),
|
default=os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse"),
|
||||||
help='Destination folder for the wheels.',
|
help="Destination folder for the wheels.",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'package_dir',
|
"package_dir",
|
||||||
default='.',
|
default=".",
|
||||||
nargs='?',
|
nargs="?",
|
||||||
help='''
|
help="""
|
||||||
Path to the package that you want wheels for. Must be a subdirectory of
|
Path to the package that you want wheels for. Must be a subdirectory of
|
||||||
the working directory. When set, the working directory is still
|
the working directory. When set, the working directory is still
|
||||||
considered the 'project' and is copied into the Docker container on
|
considered the 'project' and is copied into the Docker container on
|
||||||
Linux. Default: the working directory.
|
Linux. Default: the working directory.
|
||||||
''',
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--print-build-identifiers',
|
"--print-build-identifiers",
|
||||||
action='store_true',
|
action="store_true",
|
||||||
help='Print the build identifiers matched by the current invocation and exit.',
|
help="Print the build identifiers matched by the current invocation and exit.",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--allow-empty',
|
"--allow-empty",
|
||||||
action='store_true',
|
action="store_true",
|
||||||
help='Do not report an error code if the build does not match any wheels.',
|
help="Do not report an error code if the build does not match any wheels.",
|
||||||
)
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
detect_obsolete_options()
|
detect_obsolete_options()
|
||||||
|
|
||||||
if args.platform != 'auto':
|
if args.platform != "auto":
|
||||||
platform = args.platform
|
platform = args.platform
|
||||||
else:
|
else:
|
||||||
ci_provider = detect_ci_provider()
|
ci_provider = detect_ci_provider()
|
||||||
if ci_provider is None:
|
if ci_provider is None:
|
||||||
print(
|
print(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
'''
|
"""
|
||||||
cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server;
|
cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server;
|
||||||
Travis CI, AppVeyor, Azure Pipelines, GitHub Actions, CircleCI, and Gitlab are
|
Travis CI, AppVeyor, Azure Pipelines, GitHub Actions, CircleCI, and Gitlab are
|
||||||
supported. You can run on your development machine or other CI providers using the
|
supported. You can run on your development machine or other CI providers using the
|
||||||
--platform argument. Check --help output for more information.
|
--platform argument. Check --help output for more information.
|
||||||
'''
|
"""
|
||||||
),
|
),
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
if sys.platform.startswith('linux'):
|
if sys.platform.startswith("linux"):
|
||||||
platform = 'linux'
|
platform = "linux"
|
||||||
elif sys.platform == 'darwin':
|
elif sys.platform == "darwin":
|
||||||
platform = 'macos'
|
platform = "macos"
|
||||||
elif sys.platform == 'win32':
|
elif sys.platform == "win32":
|
||||||
platform = 'windows'
|
platform = "windows"
|
||||||
else:
|
else:
|
||||||
print(
|
print(
|
||||||
'cibuildwheel: Unable to detect platform from "sys.platform" in a CI environment. You can run '
|
'cibuildwheel: Unable to detect platform from "sys.platform" in a CI environment. You can run '
|
||||||
'cibuildwheel using the --platform argument. Check --help output for more information.',
|
"cibuildwheel using the --platform argument. Check --help output for more information.",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
if platform not in PLATFORMS:
|
if platform not in PLATFORMS:
|
||||||
print(f'cibuildwheel: Unsupported platform: {platform}', file=sys.stderr)
|
print(f"cibuildwheel: Unsupported platform: {platform}", file=sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
package_dir = Path(args.package_dir)
|
package_dir = Path(args.package_dir)
|
||||||
output_dir = Path(args.output_dir)
|
output_dir = Path(args.output_dir)
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
|
repair_command_default = "auditwheel repair -w {dest_dir} {wheel}"
|
||||||
elif platform == 'macos':
|
elif platform == "macos":
|
||||||
repair_command_default = 'delocate-listdeps {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}'
|
repair_command_default = "delocate-listdeps {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}"
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
repair_command_default = ''
|
repair_command_default = ""
|
||||||
else:
|
else:
|
||||||
assert_never(platform)
|
assert_never(platform)
|
||||||
|
|
||||||
build_config = os.environ.get('CIBW_BUILD') or '*'
|
build_config = os.environ.get("CIBW_BUILD") or "*"
|
||||||
skip_config = os.environ.get('CIBW_SKIP', '')
|
skip_config = os.environ.get("CIBW_SKIP", "")
|
||||||
test_skip = os.environ.get('CIBW_TEST_SKIP', '')
|
test_skip = os.environ.get("CIBW_TEST_SKIP", "")
|
||||||
environment_config = get_option_from_environment(
|
environment_config = get_option_from_environment(
|
||||||
'CIBW_ENVIRONMENT', platform=platform, default=''
|
"CIBW_ENVIRONMENT", platform=platform, default=""
|
||||||
)
|
)
|
||||||
before_all = get_option_from_environment('CIBW_BEFORE_ALL', platform=platform, default='')
|
before_all = get_option_from_environment("CIBW_BEFORE_ALL", platform=platform, default="")
|
||||||
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
|
before_build = get_option_from_environment("CIBW_BEFORE_BUILD", platform=platform)
|
||||||
repair_command = get_option_from_environment(
|
repair_command = get_option_from_environment(
|
||||||
'CIBW_REPAIR_WHEEL_COMMAND', platform=platform, default=repair_command_default
|
"CIBW_REPAIR_WHEEL_COMMAND", platform=platform, default=repair_command_default
|
||||||
)
|
)
|
||||||
dependency_versions = get_option_from_environment(
|
dependency_versions = get_option_from_environment(
|
||||||
'CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned'
|
"CIBW_DEPENDENCY_VERSIONS", platform=platform, default="pinned"
|
||||||
)
|
)
|
||||||
test_command = get_option_from_environment('CIBW_TEST_COMMAND', platform=platform)
|
test_command = get_option_from_environment("CIBW_TEST_COMMAND", platform=platform)
|
||||||
before_test = get_option_from_environment('CIBW_BEFORE_TEST', platform=platform)
|
before_test = get_option_from_environment("CIBW_BEFORE_TEST", platform=platform)
|
||||||
test_requires = get_option_from_environment(
|
test_requires = get_option_from_environment(
|
||||||
'CIBW_TEST_REQUIRES', platform=platform, default=''
|
"CIBW_TEST_REQUIRES", platform=platform, default=""
|
||||||
).split()
|
).split()
|
||||||
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
|
test_extras = get_option_from_environment("CIBW_TEST_EXTRAS", platform=platform, default="")
|
||||||
build_verbosity_str = get_option_from_environment(
|
build_verbosity_str = get_option_from_environment(
|
||||||
'CIBW_BUILD_VERBOSITY', platform=platform, default=''
|
"CIBW_BUILD_VERBOSITY", platform=platform, default=""
|
||||||
)
|
)
|
||||||
|
|
||||||
package_files = {'setup.py', 'setup.cfg', 'pyproject.toml'}
|
package_files = {"setup.py", "setup.cfg", "pyproject.toml"}
|
||||||
|
|
||||||
if not any(package_dir.joinpath(name).exists() for name in package_files):
|
if not any(package_dir.joinpath(name).exists() for name in package_files):
|
||||||
names = ', '.join(sorted(package_files, reverse=True))
|
names = ", ".join(sorted(package_files, reverse=True))
|
||||||
print(
|
print(
|
||||||
f'cibuildwheel: Could not find any of {{{names}}} at root of package', file=sys.stderr
|
f"cibuildwheel: Could not find any of {{{names}}} at root of package", file=sys.stderr
|
||||||
)
|
)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
# Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py
|
# Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py
|
||||||
requires_python_str: Optional[str] = os.environ.get(
|
requires_python_str: Optional[str] = os.environ.get(
|
||||||
'CIBW_PROJECT_REQUIRES_PYTHON'
|
"CIBW_PROJECT_REQUIRES_PYTHON"
|
||||||
) or get_requires_python_str(package_dir)
|
) or get_requires_python_str(package_dir)
|
||||||
requires_python = None if requires_python_str is None else SpecifierSet(requires_python_str)
|
requires_python = None if requires_python_str is None else SpecifierSet(requires_python_str)
|
||||||
|
|
||||||
@@ -234,18 +234,18 @@ def main() -> None:
|
|||||||
traceback.print_exc(None, sys.stderr)
|
traceback.print_exc(None, sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
if dependency_versions == 'pinned':
|
if dependency_versions == "pinned":
|
||||||
dependency_constraints: Optional[
|
dependency_constraints: Optional[
|
||||||
DependencyConstraints
|
DependencyConstraints
|
||||||
] = DependencyConstraints.with_defaults()
|
] = DependencyConstraints.with_defaults()
|
||||||
elif dependency_versions == 'latest':
|
elif dependency_versions == "latest":
|
||||||
dependency_constraints = None
|
dependency_constraints = None
|
||||||
else:
|
else:
|
||||||
dependency_versions_path = Path(dependency_versions)
|
dependency_versions_path = Path(dependency_versions)
|
||||||
dependency_constraints = DependencyConstraints(dependency_versions_path)
|
dependency_constraints = DependencyConstraints(dependency_versions_path)
|
||||||
|
|
||||||
if test_extras:
|
if test_extras:
|
||||||
test_extras = f'[{test_extras}]'
|
test_extras = f"[{test_extras}]"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
build_verbosity = min(3, max(-3, int(build_verbosity_str)))
|
build_verbosity = min(3, max(-3, int(build_verbosity_str)))
|
||||||
@@ -254,13 +254,13 @@ def main() -> None:
|
|||||||
|
|
||||||
# Add CIBUILDWHEEL environment variable
|
# Add CIBUILDWHEEL environment variable
|
||||||
# This needs to be passed on to the docker container in linux.py
|
# This needs to be passed on to the docker container in linux.py
|
||||||
os.environ['CIBUILDWHEEL'] = '1'
|
os.environ["CIBUILDWHEEL"] = "1"
|
||||||
|
|
||||||
if args.archs is not None:
|
if args.archs is not None:
|
||||||
archs_config_str = args.archs
|
archs_config_str = args.archs
|
||||||
else:
|
else:
|
||||||
archs_config_str = get_option_from_environment(
|
archs_config_str = get_option_from_environment(
|
||||||
'CIBW_ARCHS', platform=platform, default='auto'
|
"CIBW_ARCHS", platform=platform, default="auto"
|
||||||
)
|
)
|
||||||
|
|
||||||
archs = Architecture.parse_config(archs_config_str, platform=platform)
|
archs = Architecture.parse_config(archs_config_str, platform=platform)
|
||||||
@@ -273,8 +273,8 @@ def main() -> None:
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
manylinux_images: Optional[Dict[str, str]] = None
|
manylinux_images: Optional[Dict[str, str]] = None
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
pinned_docker_images_file = resources_dir / 'pinned_docker_images.cfg'
|
pinned_docker_images_file = resources_dir / "pinned_docker_images.cfg"
|
||||||
all_pinned_docker_images = ConfigParser()
|
all_pinned_docker_images = ConfigParser()
|
||||||
all_pinned_docker_images.read(pinned_docker_images_file)
|
all_pinned_docker_images.read(pinned_docker_images_file)
|
||||||
# all_pinned_docker_images looks like a dict of dicts, e.g.
|
# all_pinned_docker_images looks like a dict of dicts, e.g.
|
||||||
@@ -285,15 +285,15 @@ def main() -> None:
|
|||||||
|
|
||||||
manylinux_images = {}
|
manylinux_images = {}
|
||||||
|
|
||||||
for build_platform in ['x86_64', 'i686', 'pypy_x86_64', 'aarch64', 'ppc64le', 's390x']:
|
for build_platform in ["x86_64", "i686", "pypy_x86_64", "aarch64", "ppc64le", "s390x"]:
|
||||||
pinned_images = all_pinned_docker_images[build_platform]
|
pinned_images = all_pinned_docker_images[build_platform]
|
||||||
|
|
||||||
config_name = f'CIBW_MANYLINUX_{build_platform.upper()}_IMAGE'
|
config_name = f"CIBW_MANYLINUX_{build_platform.upper()}_IMAGE"
|
||||||
config_value = os.environ.get(config_name)
|
config_value = os.environ.get(config_name)
|
||||||
|
|
||||||
if config_value is None:
|
if config_value is None:
|
||||||
# default to manylinux2010 if it's available, otherwise manylinux2014
|
# default to manylinux2010 if it's available, otherwise manylinux2014
|
||||||
image = pinned_images.get('manylinux2010') or pinned_images.get('manylinux2014')
|
image = pinned_images.get("manylinux2010") or pinned_images.get("manylinux2014")
|
||||||
elif config_value in pinned_images:
|
elif config_value in pinned_images:
|
||||||
image = pinned_images[config_value]
|
image = pinned_images[config_value]
|
||||||
else:
|
else:
|
||||||
@@ -332,7 +332,7 @@ def main() -> None:
|
|||||||
sys.exit(4)
|
sys.exit(4)
|
||||||
|
|
||||||
if not identifiers:
|
if not identifiers:
|
||||||
print(f'cibuildwheel: No build identifiers selected: {build_selector}', file=sys.stderr)
|
print(f"cibuildwheel: No build identifiers selected: {build_selector}", file=sys.stderr)
|
||||||
if not args.allow_empty:
|
if not args.allow_empty:
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
|
|
||||||
@@ -342,11 +342,11 @@ def main() -> None:
|
|||||||
with cibuildwheel.util.print_new_wheels(
|
with cibuildwheel.util.print_new_wheels(
|
||||||
"\n{n} wheels produced in {m:.0f} minutes:", output_dir
|
"\n{n} wheels produced in {m:.0f} minutes:", output_dir
|
||||||
):
|
):
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
cibuildwheel.linux.build(build_options)
|
cibuildwheel.linux.build(build_options)
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
cibuildwheel.windows.build(build_options)
|
cibuildwheel.windows.build(build_options)
|
||||||
elif platform == 'macos':
|
elif platform == "macos":
|
||||||
cibuildwheel.macos.build(build_options)
|
cibuildwheel.macos.build(build_options)
|
||||||
else:
|
else:
|
||||||
assert_never(platform)
|
assert_never(platform)
|
||||||
@@ -355,8 +355,8 @@ def main() -> None:
|
|||||||
def detect_obsolete_options() -> None:
|
def detect_obsolete_options() -> None:
|
||||||
# Check the old 'MANYLINUX1_*_IMAGE' options
|
# Check the old 'MANYLINUX1_*_IMAGE' options
|
||||||
for (deprecated, alternative) in [
|
for (deprecated, alternative) in [
|
||||||
('CIBW_MANYLINUX1_X86_64_IMAGE', 'CIBW_MANYLINUX_X86_64_IMAGE'),
|
("CIBW_MANYLINUX1_X86_64_IMAGE", "CIBW_MANYLINUX_X86_64_IMAGE"),
|
||||||
('CIBW_MANYLINUX1_I686_IMAGE', 'CIBW_MANYLINUX_I686_IMAGE'),
|
("CIBW_MANYLINUX1_I686_IMAGE", "CIBW_MANYLINUX_I686_IMAGE"),
|
||||||
]:
|
]:
|
||||||
if deprecated in os.environ:
|
if deprecated in os.environ:
|
||||||
print(
|
print(
|
||||||
@@ -370,11 +370,11 @@ def detect_obsolete_options() -> None:
|
|||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
# Check for deprecated identifiers in 'CIBW_BUILD' and 'CIBW_SKIP' options
|
# Check for deprecated identifiers in 'CIBW_BUILD' and 'CIBW_SKIP' options
|
||||||
for option in ['CIBW_BUILD', 'CIBW_SKIP']:
|
for option in ["CIBW_BUILD", "CIBW_SKIP"]:
|
||||||
for deprecated, alternative in [
|
for deprecated, alternative in [
|
||||||
('manylinux1', 'manylinux'),
|
("manylinux1", "manylinux"),
|
||||||
('macosx_10_6_intel', 'macosx_x86_64'),
|
("macosx_10_6_intel", "macosx_x86_64"),
|
||||||
('macosx_10_9_x86_64', 'macosx_x86_64'),
|
("macosx_10_9_x86_64", "macosx_x86_64"),
|
||||||
]:
|
]:
|
||||||
if option in os.environ and deprecated in os.environ[option]:
|
if option in os.environ and deprecated in os.environ[option]:
|
||||||
print(
|
print(
|
||||||
@@ -386,29 +386,29 @@ def detect_obsolete_options() -> None:
|
|||||||
def print_preamble(platform: str, build_options: BuildOptions) -> None:
|
def print_preamble(platform: str, build_options: BuildOptions) -> None:
|
||||||
print(
|
print(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
'''
|
"""
|
||||||
_ _ _ _ _ _ _
|
_ _ _ _ _ _ _
|
||||||
___|_| |_ _ _|_| |_| |_ _ _| |_ ___ ___| |
|
___|_| |_ _ _|_| |_| |_ _ _| |_ ___ ___| |
|
||||||
| _| | . | | | | | . | | | | | -_| -_| |
|
| _| | . | | | | | . | | | | | -_| -_| |
|
||||||
|___|_|___|___|_|_|___|_____|_|_|___|___|_|
|
|___|_|___|___|_|_|___|_____|_|_|___|___|_|
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f'cibuildwheel version {cibuildwheel.__version__}\n')
|
print(f"cibuildwheel version {cibuildwheel.__version__}\n")
|
||||||
|
|
||||||
print('Build options:')
|
print("Build options:")
|
||||||
print(f' platform: {platform!r}')
|
print(f" platform: {platform!r}")
|
||||||
for option, value in sorted(build_options._asdict().items()):
|
for option, value in sorted(build_options._asdict().items()):
|
||||||
print(f' {option}: {value!r}')
|
print(f" {option}: {value!r}")
|
||||||
|
|
||||||
warnings = detect_warnings(platform, build_options)
|
warnings = detect_warnings(platform, build_options)
|
||||||
if warnings:
|
if warnings:
|
||||||
print('\nWarnings:')
|
print("\nWarnings:")
|
||||||
for warning in warnings:
|
for warning in warnings:
|
||||||
print(' ' + warning)
|
print(" " + warning)
|
||||||
|
|
||||||
print('\nHere we go!\n')
|
print("\nHere we go!\n")
|
||||||
|
|
||||||
|
|
||||||
def get_build_identifiers(
|
def get_build_identifiers(
|
||||||
@@ -420,15 +420,15 @@ def get_build_identifiers(
|
|||||||
List[cibuildwheel.macos.PythonConfiguration],
|
List[cibuildwheel.macos.PythonConfiguration],
|
||||||
]
|
]
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
python_configurations = cibuildwheel.linux.get_python_configurations(
|
python_configurations = cibuildwheel.linux.get_python_configurations(
|
||||||
build_selector, architectures
|
build_selector, architectures
|
||||||
)
|
)
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
python_configurations = cibuildwheel.windows.get_python_configurations(
|
python_configurations = cibuildwheel.windows.get_python_configurations(
|
||||||
build_selector, architectures
|
build_selector, architectures
|
||||||
)
|
)
|
||||||
elif platform == 'macos':
|
elif platform == "macos":
|
||||||
python_configurations = cibuildwheel.macos.get_python_configurations(
|
python_configurations = cibuildwheel.macos.get_python_configurations(
|
||||||
build_selector, architectures
|
build_selector, architectures
|
||||||
)
|
)
|
||||||
@@ -442,10 +442,10 @@ def detect_warnings(platform: str, build_options: BuildOptions) -> List[str]:
|
|||||||
warnings = []
|
warnings = []
|
||||||
|
|
||||||
# warn about deprecated {python} and {pip}
|
# warn about deprecated {python} and {pip}
|
||||||
for option_name in ['test_command', 'before_build']:
|
for option_name in ["test_command", "before_build"]:
|
||||||
option_value = getattr(build_options, option_name)
|
option_value = getattr(build_options, option_name)
|
||||||
|
|
||||||
if option_value and ('{python}' in option_value or '{pip}' in option_value):
|
if option_value and ("{python}" in option_value or "{pip}" in option_value):
|
||||||
# Reminder: in an f-string, double braces means literal single brace
|
# Reminder: in an f-string, double braces means literal single brace
|
||||||
msg = (
|
msg = (
|
||||||
f"{option_name}: '{{python}}' and '{{pip}}' are no longer needed, "
|
f"{option_name}: '{{python}}' and '{{pip}}' are no longer needed, "
|
||||||
@@ -456,5 +456,5 @@ def detect_warnings(platform: str, build_options: BuildOptions) -> List[str]:
|
|||||||
return warnings
|
return warnings
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from typing import Set
|
|||||||
|
|
||||||
from .typing import Literal, PlatformName, assert_never
|
from .typing import Literal, PlatformName, assert_never
|
||||||
|
|
||||||
PRETTY_NAMES = {'linux': 'Linux', 'macos': 'macOS', 'windows': 'Windows'}
|
PRETTY_NAMES = {"linux": "Linux", "macos": "macOS", "windows": "Windows"}
|
||||||
|
|
||||||
|
|
||||||
@functools.total_ordering
|
@functools.total_ordering
|
||||||
@@ -14,65 +14,65 @@ class Architecture(Enum):
|
|||||||
value: str
|
value: str
|
||||||
|
|
||||||
# mac/linux archs
|
# mac/linux archs
|
||||||
x86_64 = 'x86_64'
|
x86_64 = "x86_64"
|
||||||
|
|
||||||
# linux archs
|
# linux archs
|
||||||
i686 = 'i686'
|
i686 = "i686"
|
||||||
aarch64 = 'aarch64'
|
aarch64 = "aarch64"
|
||||||
ppc64le = 'ppc64le'
|
ppc64le = "ppc64le"
|
||||||
s390x = 's390x'
|
s390x = "s390x"
|
||||||
|
|
||||||
# mac archs
|
# mac archs
|
||||||
universal2 = 'universal2'
|
universal2 = "universal2"
|
||||||
arm64 = 'arm64'
|
arm64 = "arm64"
|
||||||
|
|
||||||
# windows archs
|
# windows archs
|
||||||
x86 = 'x86'
|
x86 = "x86"
|
||||||
AMD64 = 'AMD64'
|
AMD64 = "AMD64"
|
||||||
|
|
||||||
# Allow this to be sorted
|
# Allow this to be sorted
|
||||||
def __lt__(self, other: "Architecture") -> bool:
|
def __lt__(self, other: "Architecture") -> bool:
|
||||||
return self.value < other.value
|
return self.value < other.value
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_config(config: str, platform: PlatformName) -> 'Set[Architecture]':
|
def parse_config(config: str, platform: PlatformName) -> "Set[Architecture]":
|
||||||
result = set()
|
result = set()
|
||||||
for arch_str in re.split(r'[\s,]+', config):
|
for arch_str in re.split(r"[\s,]+", config):
|
||||||
if arch_str == 'auto':
|
if arch_str == "auto":
|
||||||
result |= Architecture.auto_archs(platform=platform)
|
result |= Architecture.auto_archs(platform=platform)
|
||||||
elif arch_str == 'native':
|
elif arch_str == "native":
|
||||||
result.add(Architecture(platform_module.machine()))
|
result.add(Architecture(platform_module.machine()))
|
||||||
elif arch_str == 'all':
|
elif arch_str == "all":
|
||||||
result |= Architecture.all_archs(platform=platform)
|
result |= Architecture.all_archs(platform=platform)
|
||||||
elif arch_str == 'auto64':
|
elif arch_str == "auto64":
|
||||||
result |= Architecture.bitness_archs(platform=platform, bitness="64")
|
result |= Architecture.bitness_archs(platform=platform, bitness="64")
|
||||||
elif arch_str == 'auto32':
|
elif arch_str == "auto32":
|
||||||
result |= Architecture.bitness_archs(platform=platform, bitness="32")
|
result |= Architecture.bitness_archs(platform=platform, bitness="32")
|
||||||
else:
|
else:
|
||||||
result.add(Architecture(arch_str))
|
result.add(Architecture(arch_str))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def auto_archs(platform: PlatformName) -> 'Set[Architecture]':
|
def auto_archs(platform: PlatformName) -> "Set[Architecture]":
|
||||||
native_architecture = Architecture(platform_module.machine())
|
native_architecture = Architecture(platform_module.machine())
|
||||||
result = {native_architecture}
|
result = {native_architecture}
|
||||||
|
|
||||||
if platform == 'linux' and native_architecture == Architecture.x86_64:
|
if platform == "linux" and native_architecture == Architecture.x86_64:
|
||||||
# x86_64 machines can run i686 docker containers
|
# x86_64 machines can run i686 docker containers
|
||||||
result.add(Architecture.i686)
|
result.add(Architecture.i686)
|
||||||
|
|
||||||
if platform == 'windows' and native_architecture == Architecture.AMD64:
|
if platform == "windows" and native_architecture == Architecture.AMD64:
|
||||||
result.add(Architecture.x86)
|
result.add(Architecture.x86)
|
||||||
|
|
||||||
if platform == 'macos' and native_architecture == Architecture.arm64:
|
if platform == "macos" and native_architecture == Architecture.arm64:
|
||||||
# arm64 can build and test both archs of a universal2 wheel.
|
# arm64 can build and test both archs of a universal2 wheel.
|
||||||
result.add(Architecture.universal2)
|
result.add(Architecture.universal2)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def all_archs(platform: PlatformName) -> 'Set[Architecture]':
|
def all_archs(platform: PlatformName) -> "Set[Architecture]":
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
return {
|
return {
|
||||||
Architecture.x86_64,
|
Architecture.x86_64,
|
||||||
Architecture.i686,
|
Architecture.i686,
|
||||||
@@ -80,21 +80,21 @@ class Architecture(Enum):
|
|||||||
Architecture.ppc64le,
|
Architecture.ppc64le,
|
||||||
Architecture.s390x,
|
Architecture.s390x,
|
||||||
}
|
}
|
||||||
elif platform == 'macos':
|
elif platform == "macos":
|
||||||
return {Architecture.x86_64, Architecture.arm64, Architecture.universal2}
|
return {Architecture.x86_64, Architecture.arm64, Architecture.universal2}
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
return {Architecture.x86, Architecture.AMD64}
|
return {Architecture.x86, Architecture.AMD64}
|
||||||
else:
|
else:
|
||||||
assert_never(platform)
|
assert_never(platform)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def bitness_archs(platform: PlatformName, bitness: Literal['64', '32']) -> 'Set[Architecture]':
|
def bitness_archs(platform: PlatformName, bitness: Literal["64", "32"]) -> "Set[Architecture]":
|
||||||
archs_32 = {Architecture.i686, Architecture.x86}
|
archs_32 = {Architecture.i686, Architecture.x86}
|
||||||
auto_archs = Architecture.auto_archs(platform)
|
auto_archs = Architecture.auto_archs(platform)
|
||||||
|
|
||||||
if bitness == '64':
|
if bitness == "64":
|
||||||
return auto_archs - archs_32
|
return auto_archs - archs_32
|
||||||
elif bitness == '32':
|
elif bitness == "32":
|
||||||
return auto_archs & archs_32
|
return auto_archs & archs_32
|
||||||
else:
|
else:
|
||||||
assert_never(bitness)
|
assert_never(bitness)
|
||||||
@@ -107,15 +107,15 @@ def allowed_architectures_check(
|
|||||||
|
|
||||||
allowed_architectures = Architecture.all_archs(platform)
|
allowed_architectures = Architecture.all_archs(platform)
|
||||||
|
|
||||||
msg = f'{PRETTY_NAMES[platform]} only supports {sorted(allowed_architectures)} at the moment.'
|
msg = f"{PRETTY_NAMES[platform]} only supports {sorted(allowed_architectures)} at the moment."
|
||||||
|
|
||||||
if platform != 'linux':
|
if platform != "linux":
|
||||||
msg += ' If you want to set emulation architectures on Linux, use CIBW_ARCHS_LINUX instead.'
|
msg += " If you want to set emulation architectures on Linux, use CIBW_ARCHS_LINUX instead."
|
||||||
|
|
||||||
if not architectures <= allowed_architectures:
|
if not architectures <= allowed_architectures:
|
||||||
msg = f'Invalid archs option {architectures}. ' + msg
|
msg = f"Invalid archs option {architectures}. " + msg
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
if not architectures:
|
if not architectures:
|
||||||
msg = 'Empty archs option set. ' + msg
|
msg = "Empty archs option set. " + msg
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ def evaluate(
|
|||||||
if not value:
|
if not value:
|
||||||
# empty string evaluates to empty string
|
# empty string evaluates to empty string
|
||||||
# (but trips up bashlex)
|
# (but trips up bashlex)
|
||||||
return ''
|
return ""
|
||||||
|
|
||||||
command_node = bashlex.parsesingle(value)
|
command_node = bashlex.parsesingle(value)
|
||||||
|
|
||||||
@@ -43,13 +43,13 @@ def evaluate(
|
|||||||
|
|
||||||
|
|
||||||
def evaluate_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
def evaluate_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||||
if node.kind == 'word':
|
if node.kind == "word":
|
||||||
return evaluate_word_node(node, context=context)
|
return evaluate_word_node(node, context=context)
|
||||||
elif node.kind == 'commandsubstitution':
|
elif node.kind == "commandsubstitution":
|
||||||
node_result = evaluate_command_node(node.command, context=context)
|
node_result = evaluate_command_node(node.command, context=context)
|
||||||
# bash removes training newlines in command substitution
|
# bash removes training newlines in command substitution
|
||||||
return node_result.rstrip()
|
return node_result.rstrip()
|
||||||
elif node.kind == 'parameter':
|
elif node.kind == "parameter":
|
||||||
return evaluate_parameter_node(node, context=context)
|
return evaluate_parameter_node(node, context=context)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f'Unsupported bash construct: "{node.kind}"')
|
raise ValueError(f'Unsupported bash construct: "{node.kind}"')
|
||||||
@@ -74,7 +74,7 @@ def evaluate_word_node(node: bashlex.ast.node, context: NodeExecutionContext) ->
|
|||||||
|
|
||||||
|
|
||||||
def evaluate_command_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
def evaluate_command_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||||
if any(n.kind == 'operator' for n in node.parts):
|
if any(n.kind == "operator" for n in node.parts):
|
||||||
return evaluate_nodes_as_compound_command(node.parts, context=context)
|
return evaluate_nodes_as_compound_command(node.parts, context=context)
|
||||||
else:
|
else:
|
||||||
return evaluate_nodes_as_simple_command(node.parts, context=context)
|
return evaluate_nodes_as_simple_command(node.parts, context=context)
|
||||||
@@ -87,12 +87,12 @@ def evaluate_nodes_as_compound_command(
|
|||||||
# substitutions, so we only need to handle that case. We do so assuming
|
# substitutions, so we only need to handle that case. We do so assuming
|
||||||
# that `set -o errexit` is on, because it's easier to code!
|
# that `set -o errexit` is on, because it's easier to code!
|
||||||
|
|
||||||
result = ''
|
result = ""
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
if node.kind == 'command':
|
if node.kind == "command":
|
||||||
result += evaluate_command_node(node, context=context)
|
result += evaluate_command_node(node, context=context)
|
||||||
elif node.kind == 'operator':
|
elif node.kind == "operator":
|
||||||
if node.op != ';':
|
if node.op != ";":
|
||||||
raise ValueError(f'Unsupported bash operator: "{node.op}"')
|
raise ValueError(f'Unsupported bash operator: "{node.op}"')
|
||||||
else:
|
else:
|
||||||
raise ValueError(f'Unsupported bash node in compound command: "{node.kind}"')
|
raise ValueError(f'Unsupported bash node in compound command: "{node.kind}"')
|
||||||
@@ -108,4 +108,4 @@ def evaluate_nodes_as_simple_command(
|
|||||||
|
|
||||||
|
|
||||||
def evaluate_parameter_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
def evaluate_parameter_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
|
||||||
return context.environment.get(node.value, '')
|
return context.environment.get(node.value, "")
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from .typing import PathOrStr, PopenBytes
|
|||||||
|
|
||||||
|
|
||||||
class DockerContainer:
|
class DockerContainer:
|
||||||
'''
|
"""
|
||||||
An object that represents a running Docker container.
|
An object that represents a running Docker container.
|
||||||
|
|
||||||
Intended for use as a context manager e.g.
|
Intended for use as a context manager e.g.
|
||||||
@@ -22,9 +22,9 @@ class DockerContainer:
|
|||||||
A bash shell is running in the remote container. When `call()` is invoked,
|
A bash shell is running in the remote container. When `call()` is invoked,
|
||||||
the command is relayed to the remote shell, and the results are streamed
|
the command is relayed to the remote shell, and the results are streamed
|
||||||
back to cibuildwheel.
|
back to cibuildwheel.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
UTILITY_PYTHON = '/opt/python/cp38-cp38/bin/python'
|
UTILITY_PYTHON = "/opt/python/cp38-cp38/bin/python"
|
||||||
|
|
||||||
process: PopenBytes
|
process: PopenBytes
|
||||||
bash_stdin: IO[bytes]
|
bash_stdin: IO[bytes]
|
||||||
@@ -41,18 +41,18 @@ class DockerContainer:
|
|||||||
self.cwd = cwd
|
self.cwd = cwd
|
||||||
self.name: Optional[str] = None
|
self.name: Optional[str] = None
|
||||||
|
|
||||||
def __enter__(self) -> 'DockerContainer':
|
def __enter__(self) -> "DockerContainer":
|
||||||
self.name = f'cibuildwheel-{uuid.uuid4()}'
|
self.name = f"cibuildwheel-{uuid.uuid4()}"
|
||||||
cwd_args = ['-w', str(self.cwd)] if self.cwd else []
|
cwd_args = ["-w", str(self.cwd)] if self.cwd else []
|
||||||
shell_args = ['linux32', '/bin/bash'] if self.simulate_32_bit else ['/bin/bash']
|
shell_args = ["linux32", "/bin/bash"] if self.simulate_32_bit else ["/bin/bash"]
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
'docker',
|
"docker",
|
||||||
'create',
|
"create",
|
||||||
'--env=CIBUILDWHEEL',
|
"--env=CIBUILDWHEEL",
|
||||||
f'--name={self.name}',
|
f"--name={self.name}",
|
||||||
'--interactive',
|
"--interactive",
|
||||||
'--volume=/:/host', # ignored on CircleCI
|
"--volume=/:/host", # ignored on CircleCI
|
||||||
*cwd_args,
|
*cwd_args,
|
||||||
self.docker_image,
|
self.docker_image,
|
||||||
*shell_args,
|
*shell_args,
|
||||||
@@ -61,10 +61,10 @@ class DockerContainer:
|
|||||||
)
|
)
|
||||||
self.process = subprocess.Popen(
|
self.process = subprocess.Popen(
|
||||||
[
|
[
|
||||||
'docker',
|
"docker",
|
||||||
'start',
|
"start",
|
||||||
'--attach',
|
"--attach",
|
||||||
'--interactive',
|
"--interactive",
|
||||||
self.name,
|
self.name,
|
||||||
],
|
],
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
@@ -76,7 +76,7 @@ class DockerContainer:
|
|||||||
self.bash_stdout = self.process.stdout
|
self.bash_stdout = self.process.stdout
|
||||||
|
|
||||||
# run a noop command to block until the container is responding
|
# run a noop command to block until the container is responding
|
||||||
self.call(['/bin/true'])
|
self.call(["/bin/true"])
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ class DockerContainer:
|
|||||||
|
|
||||||
assert isinstance(self.name, str)
|
assert isinstance(self.name, str)
|
||||||
|
|
||||||
subprocess.run(['docker', 'rm', '--force', '-v', self.name], stdout=subprocess.DEVNULL)
|
subprocess.run(["docker", "rm", "--force", "-v", self.name], stdout=subprocess.DEVNULL)
|
||||||
self.name = None
|
self.name = None
|
||||||
|
|
||||||
def copy_into(self, from_path: Path, to_path: PurePath) -> None:
|
def copy_into(self, from_path: Path, to_path: PurePath) -> None:
|
||||||
@@ -103,9 +103,9 @@ class DockerContainer:
|
|||||||
# Use `docker exec` instead.
|
# Use `docker exec` instead.
|
||||||
|
|
||||||
if from_path.is_dir():
|
if from_path.is_dir():
|
||||||
self.call(['mkdir', '-p', to_path])
|
self.call(["mkdir", "-p", to_path])
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
f'tar cf - . | docker exec -i {self.name} tar -xC {shell_quote(to_path)} -f -',
|
f"tar cf - . | docker exec -i {self.name} tar -xC {shell_quote(to_path)} -f -",
|
||||||
shell=True,
|
shell=True,
|
||||||
check=True,
|
check=True,
|
||||||
cwd=from_path,
|
cwd=from_path,
|
||||||
@@ -122,7 +122,7 @@ class DockerContainer:
|
|||||||
to_path.mkdir(parents=True, exist_ok=True)
|
to_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
f'docker exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -',
|
f"docker exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -",
|
||||||
shell=True,
|
shell=True,
|
||||||
check=True,
|
check=True,
|
||||||
cwd=to_path,
|
cwd=to_path,
|
||||||
@@ -135,8 +135,8 @@ class DockerContainer:
|
|||||||
self.call(
|
self.call(
|
||||||
[
|
[
|
||||||
self.UTILITY_PYTHON,
|
self.UTILITY_PYTHON,
|
||||||
'-c',
|
"-c",
|
||||||
f'import sys, json, glob; json.dump(glob.glob({glob_pattern!r}), sys.stdout)',
|
f"import sys, json, glob; json.dump(glob.glob({glob_pattern!r}), sys.stdout)",
|
||||||
],
|
],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
)
|
)
|
||||||
@@ -152,17 +152,17 @@ class DockerContainer:
|
|||||||
cwd: Optional[PathOrStr] = None,
|
cwd: Optional[PathOrStr] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
|
||||||
chdir = f'cd {cwd}' if cwd else ''
|
chdir = f"cd {cwd}" if cwd else ""
|
||||||
env_assignments = (
|
env_assignments = (
|
||||||
' '.join(f'{shlex.quote(k)}={shlex.quote(v)}' for k, v in env.items())
|
" ".join(f"{shlex.quote(k)}={shlex.quote(v)}" for k, v in env.items())
|
||||||
if env is not None
|
if env is not None
|
||||||
else ''
|
else ""
|
||||||
)
|
)
|
||||||
command = ' '.join(shlex.quote(str(a)) for a in args)
|
command = " ".join(shlex.quote(str(a)) for a in args)
|
||||||
end_of_message = str(uuid.uuid4())
|
end_of_message = str(uuid.uuid4())
|
||||||
|
|
||||||
# log the command we're executing
|
# log the command we're executing
|
||||||
print(f' + {command}')
|
print(f" + {command}")
|
||||||
|
|
||||||
# Write a command to the remote shell. First we change the
|
# Write a command to the remote shell. First we change the
|
||||||
# cwd, if that's required. Then, we use the `env` utility to run
|
# cwd, if that's required. Then, we use the `env` utility to run
|
||||||
@@ -173,14 +173,14 @@ class DockerContainer:
|
|||||||
# the returncode of `command`.
|
# the returncode of `command`.
|
||||||
self.bash_stdin.write(
|
self.bash_stdin.write(
|
||||||
bytes(
|
bytes(
|
||||||
f'''(
|
f"""(
|
||||||
{chdir}
|
{chdir}
|
||||||
env {env_assignments} {command}
|
env {env_assignments} {command}
|
||||||
printf "%04d%s\n" $? {end_of_message}
|
printf "%04d%s\n" $? {end_of_message}
|
||||||
)
|
)
|
||||||
''',
|
""",
|
||||||
encoding='utf8',
|
encoding="utf8",
|
||||||
errors='surrogateescape',
|
errors="surrogateescape",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.bash_stdin.flush()
|
self.bash_stdin.flush()
|
||||||
@@ -193,7 +193,7 @@ class DockerContainer:
|
|||||||
while True:
|
while True:
|
||||||
line = self.bash_stdout.readline()
|
line = self.bash_stdout.readline()
|
||||||
|
|
||||||
if line.endswith(bytes(end_of_message, encoding='utf8') + b'\n'):
|
if line.endswith(bytes(end_of_message, encoding="utf8") + b"\n"):
|
||||||
# fmt: off
|
# fmt: off
|
||||||
footer_offset = (
|
footer_offset = (
|
||||||
len(line)
|
len(line)
|
||||||
@@ -211,9 +211,9 @@ class DockerContainer:
|
|||||||
output_io.write(line)
|
output_io.write(line)
|
||||||
|
|
||||||
if isinstance(output_io, io.BytesIO):
|
if isinstance(output_io, io.BytesIO):
|
||||||
output = str(output_io.getvalue(), encoding='utf8', errors='surrogateescape')
|
output = str(output_io.getvalue(), encoding="utf8", errors="surrogateescape")
|
||||||
else:
|
else:
|
||||||
output = ''
|
output = ""
|
||||||
|
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
raise subprocess.CalledProcessError(returncode, args, output)
|
raise subprocess.CalledProcessError(returncode, args, output)
|
||||||
@@ -225,8 +225,8 @@ class DockerContainer:
|
|||||||
self.call(
|
self.call(
|
||||||
[
|
[
|
||||||
self.UTILITY_PYTHON,
|
self.UTILITY_PYTHON,
|
||||||
'-c',
|
"-c",
|
||||||
'import sys, json, os; json.dump(os.environ.copy(), sys.stdout)',
|
"import sys, json, os; json.dump(os.environ.copy(), sys.stdout)",
|
||||||
],
|
],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class EnvironmentParseError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
def split_env_items(env_string: str) -> List[str]:
|
def split_env_items(env_string: str) -> List[str]:
|
||||||
'''Splits space-separated variable assignments into a list of individual assignments.
|
"""Splits space-separated variable assignments into a list of individual assignments.
|
||||||
|
|
||||||
>>> split_env_items('VAR=abc')
|
>>> split_env_items('VAR=abc')
|
||||||
['VAR=abc']
|
['VAR=abc']
|
||||||
@@ -24,7 +24,7 @@ def split_env_items(env_string: str) -> List[str]:
|
|||||||
['PATH="$PATH;/opt/cibw_test_path"']
|
['PATH="$PATH;/opt/cibw_test_path"']
|
||||||
>>> split_env_items('PATH2="something with spaces"')
|
>>> split_env_items('PATH2="something with spaces"')
|
||||||
['PATH2="something with spaces"']
|
['PATH2="something with spaces"']
|
||||||
'''
|
"""
|
||||||
if not env_string:
|
if not env_string:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ def split_env_items(env_string: str) -> List[str]:
|
|||||||
|
|
||||||
class EnvironmentAssignment:
|
class EnvironmentAssignment:
|
||||||
def __init__(self, assignment: str):
|
def __init__(self, assignment: str):
|
||||||
name, equals, value = assignment.partition('=')
|
name, equals, value = assignment.partition("=")
|
||||||
if not equals:
|
if not equals:
|
||||||
raise EnvironmentParseError(assignment)
|
raise EnvironmentParseError(assignment)
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -51,14 +51,14 @@ class EnvironmentAssignment:
|
|||||||
environment: Dict[str, str],
|
environment: Dict[str, str],
|
||||||
executor: Optional[bashlex_eval.EnvironmentExecutor] = None,
|
executor: Optional[bashlex_eval.EnvironmentExecutor] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
'''Returns the value of this assignment, as evaluated in the environment'''
|
"""Returns the value of this assignment, as evaluated in the environment"""
|
||||||
return bashlex_eval.evaluate(self.value, environment=environment, executor=executor)
|
return bashlex_eval.evaluate(self.value, environment=environment, executor=executor)
|
||||||
|
|
||||||
def as_shell_assignment(self) -> str:
|
def as_shell_assignment(self) -> str:
|
||||||
return f'export {self.name}={self.value}'
|
return f"export {self.name}={self.value}"
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'{self.name}={self.value}'
|
return f"{self.name}={self.value}"
|
||||||
|
|
||||||
|
|
||||||
class ParsedEnvironment:
|
class ParsedEnvironment:
|
||||||
@@ -82,7 +82,7 @@ class ParsedEnvironment:
|
|||||||
return [a.as_shell_assignment() for a in self.assignments]
|
return [a.as_shell_assignment() for a in self.assignments]
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'ParsedEnvironment({[repr(a) for a in self.assignments]!r})'
|
return f"ParsedEnvironment({[repr(a) for a in self.assignments]!r})"
|
||||||
|
|
||||||
|
|
||||||
def parse_environment(env_string: str) -> ParsedEnvironment:
|
def parse_environment(env_string: str) -> ParsedEnvironment:
|
||||||
|
|||||||
+81
-81
@@ -34,7 +34,7 @@ def get_python_configurations(
|
|||||||
architectures: Set[Architecture],
|
architectures: Set[Architecture],
|
||||||
) -> List[PythonConfiguration]:
|
) -> List[PythonConfiguration]:
|
||||||
|
|
||||||
full_python_configs = read_python_configs('linux')
|
full_python_configs = read_python_configs("linux")
|
||||||
|
|
||||||
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
|
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
|
||||||
|
|
||||||
@@ -51,12 +51,12 @@ def get_python_configurations(
|
|||||||
def build(options: BuildOptions) -> None:
|
def build(options: BuildOptions) -> None:
|
||||||
try:
|
try:
|
||||||
# check docker is installed
|
# check docker is installed
|
||||||
subprocess.run(['docker', '--version'], check=True, stdout=subprocess.DEVNULL)
|
subprocess.run(["docker", "--version"], check=True, stdout=subprocess.DEVNULL)
|
||||||
except Exception:
|
except Exception:
|
||||||
print(
|
print(
|
||||||
'cibuildwheel: Docker not found. Docker is required to run Linux builds. '
|
"cibuildwheel: Docker not found. Docker is required to run Linux builds. "
|
||||||
'If you\'re building on Travis CI, add `services: [docker]` to your .travis.yml.'
|
"If you're building on Travis CI, add `services: [docker]` to your .travis.yml."
|
||||||
'If you\'re building on Circle CI in Linux, add a `setup_remote_docker` step to your .circleci/config.yml',
|
"If you're building on Circle CI in Linux, add a `setup_remote_docker` step to your .circleci/config.yml",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
@@ -64,22 +64,22 @@ def build(options: BuildOptions) -> None:
|
|||||||
assert options.manylinux_images is not None
|
assert options.manylinux_images is not None
|
||||||
python_configurations = get_python_configurations(options.build_selector, options.architectures)
|
python_configurations = get_python_configurations(options.build_selector, options.architectures)
|
||||||
platforms = [
|
platforms = [
|
||||||
('cp', 'manylinux_x86_64', options.manylinux_images['x86_64']),
|
("cp", "manylinux_x86_64", options.manylinux_images["x86_64"]),
|
||||||
('cp', 'manylinux_i686', options.manylinux_images['i686']),
|
("cp", "manylinux_i686", options.manylinux_images["i686"]),
|
||||||
('cp', 'manylinux_aarch64', options.manylinux_images['aarch64']),
|
("cp", "manylinux_aarch64", options.manylinux_images["aarch64"]),
|
||||||
('cp', 'manylinux_ppc64le', options.manylinux_images['ppc64le']),
|
("cp", "manylinux_ppc64le", options.manylinux_images["ppc64le"]),
|
||||||
('cp', 'manylinux_s390x', options.manylinux_images['s390x']),
|
("cp", "manylinux_s390x", options.manylinux_images["s390x"]),
|
||||||
('pp', 'manylinux_x86_64', options.manylinux_images['pypy_x86_64']),
|
("pp", "manylinux_x86_64", options.manylinux_images["pypy_x86_64"]),
|
||||||
]
|
]
|
||||||
|
|
||||||
cwd = Path.cwd()
|
cwd = Path.cwd()
|
||||||
abs_package_dir = options.package_dir.resolve()
|
abs_package_dir = options.package_dir.resolve()
|
||||||
if cwd != abs_package_dir and cwd not in abs_package_dir.parents:
|
if cwd != abs_package_dir and cwd not in abs_package_dir.parents:
|
||||||
raise Exception('package_dir must be inside the working directory')
|
raise Exception("package_dir must be inside the working directory")
|
||||||
|
|
||||||
container_project_path = PurePath('/project')
|
container_project_path = PurePath("/project")
|
||||||
container_package_dir = container_project_path / abs_package_dir.relative_to(cwd)
|
container_package_dir = container_project_path / abs_package_dir.relative_to(cwd)
|
||||||
container_output_dir = PurePath('/output')
|
container_output_dir = PurePath("/output")
|
||||||
|
|
||||||
for implementation, platform_tag, docker_image in platforms:
|
for implementation, platform_tag, docker_image in platforms:
|
||||||
platform_configs = [
|
platform_configs = [
|
||||||
@@ -91,22 +91,22 @@ def build(options: BuildOptions) -> None:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
log.step(f'Starting Docker image {docker_image}...')
|
log.step(f"Starting Docker image {docker_image}...")
|
||||||
with DockerContainer(
|
with DockerContainer(
|
||||||
docker_image,
|
docker_image,
|
||||||
simulate_32_bit=platform_tag.endswith('i686'),
|
simulate_32_bit=platform_tag.endswith("i686"),
|
||||||
cwd=container_project_path,
|
cwd=container_project_path,
|
||||||
) as docker:
|
) as docker:
|
||||||
|
|
||||||
log.step('Copying project into Docker...')
|
log.step("Copying project into Docker...")
|
||||||
docker.copy_into(Path.cwd(), container_project_path)
|
docker.copy_into(Path.cwd(), container_project_path)
|
||||||
|
|
||||||
if options.before_all:
|
if options.before_all:
|
||||||
log.step('Running before_all...')
|
log.step("Running before_all...")
|
||||||
|
|
||||||
env = docker.get_environment()
|
env = docker.get_environment()
|
||||||
env['PATH'] = f'/opt/python/cp38-cp38/bin:{env["PATH"]}'
|
env["PATH"] = f'/opt/python/cp38-cp38/bin:{env["PATH"]}'
|
||||||
env['PIP_DISABLE_PIP_VERSION_CHECK'] = '1'
|
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
|
||||||
env = options.environment.as_dictionary(
|
env = options.environment.as_dictionary(
|
||||||
env, executor=docker.environment_executor
|
env, executor=docker.environment_executor
|
||||||
)
|
)
|
||||||
@@ -116,7 +116,7 @@ def build(options: BuildOptions) -> None:
|
|||||||
project=container_project_path,
|
project=container_project_path,
|
||||||
package=container_package_dir,
|
package=container_package_dir,
|
||||||
)
|
)
|
||||||
docker.call(['sh', '-c', before_all_prepared], env=env)
|
docker.call(["sh", "-c", before_all_prepared], env=env)
|
||||||
|
|
||||||
for config in platform_configs:
|
for config in platform_configs:
|
||||||
log.build_start(config.identifier)
|
log.build_start(config.identifier)
|
||||||
@@ -124,19 +124,19 @@ def build(options: BuildOptions) -> None:
|
|||||||
dependency_constraint_flags: List[PathOrStr] = []
|
dependency_constraint_flags: List[PathOrStr] = []
|
||||||
if config.identifier.startswith("pp"):
|
if config.identifier.startswith("pp"):
|
||||||
# Patch PyPy to make sure headers get installed into a venv
|
# Patch PyPy to make sure headers get installed into a venv
|
||||||
patch_version = '_27' if config.version == '2.7' else ''
|
patch_version = "_27" if config.version == "2.7" else ""
|
||||||
patch_path = resources_dir / f'pypy_venv{patch_version}.patch'
|
patch_path = resources_dir / f"pypy_venv{patch_version}.patch"
|
||||||
patch_docker_path = PurePath('/pypy_venv.patch')
|
patch_docker_path = PurePath("/pypy_venv.patch")
|
||||||
docker.copy_into(patch_path, patch_docker_path)
|
docker.copy_into(patch_path, patch_docker_path)
|
||||||
try:
|
try:
|
||||||
docker.call(
|
docker.call(
|
||||||
[
|
[
|
||||||
'patch',
|
"patch",
|
||||||
'--force',
|
"--force",
|
||||||
'-p1',
|
"-p1",
|
||||||
'-d',
|
"-d",
|
||||||
config.path,
|
config.path,
|
||||||
'-i',
|
"-i",
|
||||||
patch_docker_path,
|
patch_docker_path,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -147,18 +147,18 @@ def build(options: BuildOptions) -> None:
|
|||||||
constraints_file = options.dependency_constraints.get_for_python_version(
|
constraints_file = options.dependency_constraints.get_for_python_version(
|
||||||
config.version
|
config.version
|
||||||
)
|
)
|
||||||
container_constraints_file = PurePath('/constraints.txt')
|
container_constraints_file = PurePath("/constraints.txt")
|
||||||
|
|
||||||
docker.copy_into(constraints_file, container_constraints_file)
|
docker.copy_into(constraints_file, container_constraints_file)
|
||||||
dependency_constraint_flags = ['-c', container_constraints_file]
|
dependency_constraint_flags = ["-c", container_constraints_file]
|
||||||
|
|
||||||
log.step('Setting up build environment...')
|
log.step("Setting up build environment...")
|
||||||
|
|
||||||
env = docker.get_environment()
|
env = docker.get_environment()
|
||||||
|
|
||||||
# put this config's python top of the list
|
# put this config's python top of the list
|
||||||
python_bin = config.path / 'bin'
|
python_bin = config.path / "bin"
|
||||||
env['PATH'] = f'{python_bin}:{env["PATH"]}'
|
env["PATH"] = f'{python_bin}:{env["PATH"]}'
|
||||||
|
|
||||||
env = options.environment.as_dictionary(
|
env = options.environment.as_dictionary(
|
||||||
env, executor=docker.environment_executor
|
env, executor=docker.environment_executor
|
||||||
@@ -166,17 +166,17 @@ def build(options: BuildOptions) -> None:
|
|||||||
|
|
||||||
# check config python and pip are still on PATH
|
# check config python and pip are still on PATH
|
||||||
which_python = docker.call(
|
which_python = docker.call(
|
||||||
['which', 'python'], env=env, capture_output=True
|
["which", "python"], env=env, capture_output=True
|
||||||
).strip()
|
).strip()
|
||||||
if PurePath(which_python) != python_bin / 'python':
|
if PurePath(which_python) != python_bin / "python":
|
||||||
print(
|
print(
|
||||||
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
which_pip = docker.call(['which', 'pip'], env=env, capture_output=True).strip()
|
which_pip = docker.call(["which", "pip"], env=env, capture_output=True).strip()
|
||||||
if PurePath(which_pip) != python_bin / 'pip':
|
if PurePath(which_pip) != python_bin / "pip":
|
||||||
print(
|
print(
|
||||||
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
@@ -184,73 +184,73 @@ def build(options: BuildOptions) -> None:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if options.before_build:
|
if options.before_build:
|
||||||
log.step('Running before_build...')
|
log.step("Running before_build...")
|
||||||
before_build_prepared = prepare_command(
|
before_build_prepared = prepare_command(
|
||||||
options.before_build,
|
options.before_build,
|
||||||
project=container_project_path,
|
project=container_project_path,
|
||||||
package=container_package_dir,
|
package=container_package_dir,
|
||||||
)
|
)
|
||||||
docker.call(['sh', '-c', before_build_prepared], env=env)
|
docker.call(["sh", "-c", before_build_prepared], env=env)
|
||||||
|
|
||||||
log.step('Building wheel...')
|
log.step("Building wheel...")
|
||||||
|
|
||||||
temp_dir = PurePath('/tmp/cibuildwheel')
|
temp_dir = PurePath("/tmp/cibuildwheel")
|
||||||
built_wheel_dir = temp_dir / 'built_wheel'
|
built_wheel_dir = temp_dir / "built_wheel"
|
||||||
docker.call(['rm', '-rf', built_wheel_dir])
|
docker.call(["rm", "-rf", built_wheel_dir])
|
||||||
docker.call(['mkdir', '-p', built_wheel_dir])
|
docker.call(["mkdir", "-p", built_wheel_dir])
|
||||||
|
|
||||||
docker.call(
|
docker.call(
|
||||||
[
|
[
|
||||||
'pip',
|
"pip",
|
||||||
'wheel',
|
"wheel",
|
||||||
container_package_dir,
|
container_package_dir,
|
||||||
'--wheel-dir',
|
"--wheel-dir",
|
||||||
built_wheel_dir,
|
built_wheel_dir,
|
||||||
'--no-deps',
|
"--no-deps",
|
||||||
*get_build_verbosity_extra_flags(options.build_verbosity),
|
*get_build_verbosity_extra_flags(options.build_verbosity),
|
||||||
],
|
],
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
|
|
||||||
built_wheel = docker.glob(built_wheel_dir, '*.whl')[0]
|
built_wheel = docker.glob(built_wheel_dir, "*.whl")[0]
|
||||||
|
|
||||||
repaired_wheel_dir = temp_dir / 'repaired_wheel'
|
repaired_wheel_dir = temp_dir / "repaired_wheel"
|
||||||
docker.call(['rm', '-rf', repaired_wheel_dir])
|
docker.call(["rm", "-rf", repaired_wheel_dir])
|
||||||
docker.call(['mkdir', '-p', repaired_wheel_dir])
|
docker.call(["mkdir", "-p", repaired_wheel_dir])
|
||||||
|
|
||||||
if built_wheel.name.endswith('none-any.whl'):
|
if built_wheel.name.endswith("none-any.whl"):
|
||||||
raise NonPlatformWheelError()
|
raise NonPlatformWheelError()
|
||||||
|
|
||||||
if options.repair_command:
|
if options.repair_command:
|
||||||
log.step('Repairing wheel...')
|
log.step("Repairing wheel...")
|
||||||
repair_command_prepared = prepare_command(
|
repair_command_prepared = prepare_command(
|
||||||
options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
|
options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
|
||||||
)
|
)
|
||||||
docker.call(['sh', '-c', repair_command_prepared], env=env)
|
docker.call(["sh", "-c", repair_command_prepared], env=env)
|
||||||
else:
|
else:
|
||||||
docker.call(['mv', built_wheel, repaired_wheel_dir])
|
docker.call(["mv", built_wheel, repaired_wheel_dir])
|
||||||
|
|
||||||
repaired_wheels = docker.glob(repaired_wheel_dir, '*.whl')
|
repaired_wheels = docker.glob(repaired_wheel_dir, "*.whl")
|
||||||
|
|
||||||
if options.test_command and options.test_selector(config.identifier):
|
if options.test_command and options.test_selector(config.identifier):
|
||||||
log.step('Testing wheel...')
|
log.step("Testing wheel...")
|
||||||
|
|
||||||
# set up a virtual environment to install and test from, to make sure
|
# set up a virtual environment to install and test from, to make sure
|
||||||
# there are no dependencies that were pulled in at build time.
|
# there are no dependencies that were pulled in at build time.
|
||||||
docker.call(
|
docker.call(
|
||||||
['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env
|
["pip", "install", "virtualenv", *dependency_constraint_flags], env=env
|
||||||
)
|
)
|
||||||
venv_dir = (
|
venv_dir = (
|
||||||
PurePath(docker.call(['mktemp', '-d'], capture_output=True).strip())
|
PurePath(docker.call(["mktemp", "-d"], capture_output=True).strip())
|
||||||
/ 'venv'
|
/ "venv"
|
||||||
)
|
)
|
||||||
|
|
||||||
docker.call(
|
docker.call(
|
||||||
['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env
|
["python", "-m", "virtualenv", "--no-download", venv_dir], env=env
|
||||||
)
|
)
|
||||||
|
|
||||||
virtualenv_env = env.copy()
|
virtualenv_env = env.copy()
|
||||||
virtualenv_env['PATH'] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
|
virtualenv_env["PATH"] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
|
||||||
|
|
||||||
if options.before_test:
|
if options.before_test:
|
||||||
before_test_prepared = prepare_command(
|
before_test_prepared = prepare_command(
|
||||||
@@ -258,7 +258,7 @@ def build(options: BuildOptions) -> None:
|
|||||||
project=container_project_path,
|
project=container_project_path,
|
||||||
package=container_package_dir,
|
package=container_package_dir,
|
||||||
)
|
)
|
||||||
docker.call(['sh', '-c', before_test_prepared], env=virtualenv_env)
|
docker.call(["sh", "-c", before_test_prepared], env=virtualenv_env)
|
||||||
|
|
||||||
# Install the wheel we just built
|
# Install the wheel we just built
|
||||||
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
|
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
|
||||||
@@ -268,14 +268,14 @@ def build(options: BuildOptions) -> None:
|
|||||||
# Let's just pick the first one.
|
# Let's just pick the first one.
|
||||||
wheel_to_test = repaired_wheels[0]
|
wheel_to_test = repaired_wheels[0]
|
||||||
docker.call(
|
docker.call(
|
||||||
['pip', 'install', str(wheel_to_test) + options.test_extras],
|
["pip", "install", str(wheel_to_test) + options.test_extras],
|
||||||
env=virtualenv_env,
|
env=virtualenv_env,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Install any requirements to run the tests
|
# Install any requirements to run the tests
|
||||||
if options.test_requires:
|
if options.test_requires:
|
||||||
docker.call(
|
docker.call(
|
||||||
['pip', 'install', *options.test_requires], env=virtualenv_env
|
["pip", "install", *options.test_requires], env=virtualenv_env
|
||||||
)
|
)
|
||||||
|
|
||||||
# Run the tests from a different directory
|
# Run the tests from a different directory
|
||||||
@@ -285,40 +285,40 @@ def build(options: BuildOptions) -> None:
|
|||||||
package=container_package_dir,
|
package=container_package_dir,
|
||||||
)
|
)
|
||||||
docker.call(
|
docker.call(
|
||||||
['sh', '-c', test_command_prepared], cwd='/root', env=virtualenv_env
|
["sh", "-c", test_command_prepared], cwd="/root", env=virtualenv_env
|
||||||
)
|
)
|
||||||
|
|
||||||
# clean up test environment
|
# clean up test environment
|
||||||
docker.call(['rm', '-rf', venv_dir])
|
docker.call(["rm", "-rf", venv_dir])
|
||||||
|
|
||||||
# move repaired wheels to output
|
# move repaired wheels to output
|
||||||
docker.call(['mkdir', '-p', container_output_dir])
|
docker.call(["mkdir", "-p", container_output_dir])
|
||||||
docker.call(['mv', *repaired_wheels, container_output_dir])
|
docker.call(["mv", *repaired_wheels, container_output_dir])
|
||||||
|
|
||||||
log.build_end()
|
log.build_end()
|
||||||
|
|
||||||
log.step('Copying wheels back to host...')
|
log.step("Copying wheels back to host...")
|
||||||
# copy the output back into the host
|
# copy the output back into the host
|
||||||
docker.copy_out(container_output_dir, options.output_dir)
|
docker.copy_out(container_output_dir, options.output_dir)
|
||||||
log.step_end()
|
log.step_end()
|
||||||
except subprocess.CalledProcessError as error:
|
except subprocess.CalledProcessError as error:
|
||||||
log.step_end_with_error(
|
log.step_end_with_error(
|
||||||
f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}'
|
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
|
||||||
)
|
)
|
||||||
troubleshoot(options.package_dir, error)
|
troubleshoot(options.package_dir, error)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def troubleshoot(package_dir: Path, error: Exception) -> None:
|
def troubleshoot(package_dir: Path, error: Exception) -> None:
|
||||||
if isinstance(error, subprocess.CalledProcessError) and error.cmd[0:2] == ['pip', 'wheel']:
|
if isinstance(error, subprocess.CalledProcessError) and error.cmd[0:2] == ["pip", "wheel"]:
|
||||||
# the 'pip wheel' step failed.
|
# the 'pip wheel' step failed.
|
||||||
print('Checking for common errors...')
|
print("Checking for common errors...")
|
||||||
so_files = list(package_dir.glob('**/*.so'))
|
so_files = list(package_dir.glob("**/*.so"))
|
||||||
|
|
||||||
if so_files:
|
if so_files:
|
||||||
print(
|
print(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
'''
|
"""
|
||||||
NOTE: Shared object (.so) files found in this project.
|
NOTE: Shared object (.so) files found in this project.
|
||||||
|
|
||||||
These files might be built against the wrong OS, causing problems with
|
These files might be built against the wrong OS, causing problems with
|
||||||
@@ -326,11 +326,11 @@ def troubleshoot(package_dir: Path, error: Exception) -> None:
|
|||||||
|
|
||||||
If you're using Cython and have previously done an in-place build,
|
If you're using Cython and have previously done an in-place build,
|
||||||
remove those build files (*.so and *.c) before starting cibuildwheel.
|
remove those build files (*.so and *.c) before starting cibuildwheel.
|
||||||
'''
|
"""
|
||||||
),
|
),
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
|
|
||||||
print(' Files detected:')
|
print(" Files detected:")
|
||||||
print('\n'.join(f' {f}' for f in so_files))
|
print("\n".join(f" {f}" for f in so_files))
|
||||||
print('')
|
print("")
|
||||||
|
|||||||
+66
-66
@@ -7,24 +7,24 @@ from typing import IO, AnyStr, Optional, Union
|
|||||||
|
|
||||||
from cibuildwheel.util import CIProvider, detect_ci_provider
|
from cibuildwheel.util import CIProvider, detect_ci_provider
|
||||||
|
|
||||||
DEFAULT_FOLD_PATTERN = ('{name}', '')
|
DEFAULT_FOLD_PATTERN = ("{name}", "")
|
||||||
FOLD_PATTERNS = {
|
FOLD_PATTERNS = {
|
||||||
'azure': ('##[group]{name}', '##[endgroup]'),
|
"azure": ("##[group]{name}", "##[endgroup]"),
|
||||||
'travis': ('travis_fold:start:{identifier}\n{name}', 'travis_fold:end:{identifier}'),
|
"travis": ("travis_fold:start:{identifier}\n{name}", "travis_fold:end:{identifier}"),
|
||||||
'github': ('::group::{name}', '::endgroup::{name}'),
|
"github": ("::group::{name}", "::endgroup::{name}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
PLATFORM_IDENTIFIER_DESCIPTIONS = {
|
PLATFORM_IDENTIFIER_DESCIPTIONS = {
|
||||||
'manylinux_x86_64': 'manylinux x86_64',
|
"manylinux_x86_64": "manylinux x86_64",
|
||||||
'manylinux_i686': 'manylinux i686',
|
"manylinux_i686": "manylinux i686",
|
||||||
'manylinux_aarch64': 'manylinux aarch64',
|
"manylinux_aarch64": "manylinux aarch64",
|
||||||
'manylinux_ppc64le': 'manylinux ppc64le',
|
"manylinux_ppc64le": "manylinux ppc64le",
|
||||||
'manylinux_s390x': 'manylinux s390x',
|
"manylinux_s390x": "manylinux s390x",
|
||||||
'win32': 'Windows 32bit',
|
"win32": "Windows 32bit",
|
||||||
'win_amd64': 'Windows 64bit',
|
"win_amd64": "Windows 64bit",
|
||||||
'macosx_x86_64': 'macOS x86_64',
|
"macosx_x86_64": "macOS x86_64",
|
||||||
'macosx_universal2': 'macOS Universal 2 - x86_64 and arm64',
|
"macosx_universal2": "macOS Universal 2 - x86_64 and arm64",
|
||||||
'macosx_arm64': 'macOS arm64 - Apple Silicon',
|
"macosx_arm64": "macOS arm64 - Apple Silicon",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -38,33 +38,33 @@ class Logger:
|
|||||||
active_fold_group_name: Optional[str] = None
|
active_fold_group_name: Optional[str] = None
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
if sys.platform == 'win32' and hasattr(sys.stdout, 'reconfigure'):
|
if sys.platform == "win32" and hasattr(sys.stdout, "reconfigure"):
|
||||||
# the encoding on Windows can be a 1-byte charmap, but all CIs
|
# the encoding on Windows can be a 1-byte charmap, but all CIs
|
||||||
# support utf8, so we hardcode that
|
# support utf8, so we hardcode that
|
||||||
sys.stdout.reconfigure(encoding='utf8')
|
sys.stdout.reconfigure(encoding="utf8")
|
||||||
|
|
||||||
self.unicode_enabled = file_supports_unicode(sys.stdout)
|
self.unicode_enabled = file_supports_unicode(sys.stdout)
|
||||||
|
|
||||||
ci_provider = detect_ci_provider()
|
ci_provider = detect_ci_provider()
|
||||||
|
|
||||||
if ci_provider == CIProvider.azure_pipelines:
|
if ci_provider == CIProvider.azure_pipelines:
|
||||||
self.fold_mode = 'azure'
|
self.fold_mode = "azure"
|
||||||
self.colors_enabled = True
|
self.colors_enabled = True
|
||||||
|
|
||||||
elif ci_provider == CIProvider.github_actions:
|
elif ci_provider == CIProvider.github_actions:
|
||||||
self.fold_mode = 'github'
|
self.fold_mode = "github"
|
||||||
self.colors_enabled = True
|
self.colors_enabled = True
|
||||||
|
|
||||||
elif ci_provider == CIProvider.travis_ci:
|
elif ci_provider == CIProvider.travis_ci:
|
||||||
self.fold_mode = 'travis'
|
self.fold_mode = "travis"
|
||||||
self.colors_enabled = True
|
self.colors_enabled = True
|
||||||
|
|
||||||
elif ci_provider == CIProvider.appveyor:
|
elif ci_provider == CIProvider.appveyor:
|
||||||
self.fold_mode = 'disabled'
|
self.fold_mode = "disabled"
|
||||||
self.colors_enabled = True
|
self.colors_enabled = True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.fold_mode = 'disabled'
|
self.fold_mode = "disabled"
|
||||||
self.colors_enabled = file_supports_color(sys.stdout)
|
self.colors_enabled = file_supports_color(sys.stdout)
|
||||||
|
|
||||||
def build_start(self, identifier: str) -> None:
|
def build_start(self, identifier: str) -> None:
|
||||||
@@ -72,8 +72,8 @@ class Logger:
|
|||||||
c = self.colors
|
c = self.colors
|
||||||
description = build_description_from_identifier(identifier)
|
description = build_description_from_identifier(identifier)
|
||||||
print()
|
print()
|
||||||
print(f'{c.bold}{c.blue}Building {identifier} wheel{c.end}')
|
print(f"{c.bold}{c.blue}Building {identifier} wheel{c.end}")
|
||||||
print(f'{description}')
|
print(f"{description}")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
self.build_start_time = time.time()
|
self.build_start_time = time.time()
|
||||||
@@ -90,7 +90,7 @@ class Logger:
|
|||||||
|
|
||||||
print()
|
print()
|
||||||
print(
|
print(
|
||||||
f'{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration:.2f}s'
|
f"{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration:.2f}s"
|
||||||
)
|
)
|
||||||
self.build_start_time = None
|
self.build_start_time = None
|
||||||
self.active_build_identifier = None
|
self.active_build_identifier = None
|
||||||
@@ -107,9 +107,9 @@ class Logger:
|
|||||||
s = self.symbols
|
s = self.symbols
|
||||||
duration = time.time() - self.step_start_time
|
duration = time.time() - self.step_start_time
|
||||||
if success:
|
if success:
|
||||||
print(f'{c.green}{s.done} {c.end}{duration:.2f}s'.rjust(78))
|
print(f"{c.green}{s.done} {c.end}{duration:.2f}s".rjust(78))
|
||||||
else:
|
else:
|
||||||
print(f'{c.red}{s.error} {c.end}{duration:.2f}s'.rjust(78))
|
print(f"{c.red}{s.error} {c.end}{duration:.2f}s".rjust(78))
|
||||||
|
|
||||||
self.step_start_time = None
|
self.step_start_time = None
|
||||||
|
|
||||||
@@ -118,18 +118,18 @@ class Logger:
|
|||||||
self.error(error)
|
self.error(error)
|
||||||
|
|
||||||
def warning(self, message: str) -> None:
|
def warning(self, message: str) -> None:
|
||||||
if self.fold_mode == 'github':
|
if self.fold_mode == "github":
|
||||||
print(f'::warning::{message}\n', file=sys.stderr)
|
print(f"::warning::{message}\n", file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
c = self.colors
|
c = self.colors
|
||||||
print(f'{c.yellow}Warning{c.end}: {message}\n', file=sys.stderr)
|
print(f"{c.yellow}Warning{c.end}: {message}\n", file=sys.stderr)
|
||||||
|
|
||||||
def error(self, error: Union[BaseException, str]) -> None:
|
def error(self, error: Union[BaseException, str]) -> None:
|
||||||
if self.fold_mode == 'github':
|
if self.fold_mode == "github":
|
||||||
print(f'::error::{error}\n', file=sys.stderr)
|
print(f"::error::{error}\n", file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
c = self.colors
|
c = self.colors
|
||||||
print(f'{c.bright_red}Error{c.end}: {error}\n', file=sys.stderr)
|
print(f"{c.bright_red}Error{c.end}: {error}\n", file=sys.stderr)
|
||||||
|
|
||||||
def _start_fold_group(self, name: str) -> None:
|
def _start_fold_group(self, name: str) -> None:
|
||||||
self._end_fold_group()
|
self._end_fold_group()
|
||||||
@@ -152,16 +152,16 @@ class Logger:
|
|||||||
self.active_fold_group_name = None
|
self.active_fold_group_name = None
|
||||||
|
|
||||||
def _fold_group_identifier(self, name: str) -> str:
|
def _fold_group_identifier(self, name: str) -> str:
|
||||||
'''
|
"""
|
||||||
Travis doesn't like fold groups identifiers that have spaces in. This
|
Travis doesn't like fold groups identifiers that have spaces in. This
|
||||||
method converts them to ascii identifiers
|
method converts them to ascii identifiers
|
||||||
'''
|
"""
|
||||||
# whitespace to underscores
|
# whitespace to underscores
|
||||||
identifier = re.sub(r'\s+', '_', name)
|
identifier = re.sub(r"\s+", "_", name)
|
||||||
# remove non-alphanum
|
# remove non-alphanum
|
||||||
identifier = re.sub(r'[^A-Za-z\d_]+', '', identifier)
|
identifier = re.sub(r"[^A-Za-z\d_]+", "", identifier)
|
||||||
# trim underscores
|
# trim underscores
|
||||||
identifier = identifier.strip('_')
|
identifier = identifier.strip("_")
|
||||||
# lowercase, shorten
|
# lowercase, shorten
|
||||||
return identifier.lower()[:20]
|
return identifier.lower()[:20]
|
||||||
|
|
||||||
@@ -181,53 +181,53 @@ class Logger:
|
|||||||
|
|
||||||
|
|
||||||
def build_description_from_identifier(identifier: str) -> str:
|
def build_description_from_identifier(identifier: str) -> str:
|
||||||
python_identifier, _, platform_identifier = identifier.partition('-')
|
python_identifier, _, platform_identifier = identifier.partition("-")
|
||||||
|
|
||||||
build_description = ''
|
build_description = ""
|
||||||
|
|
||||||
python_interpreter = python_identifier[0:2]
|
python_interpreter = python_identifier[0:2]
|
||||||
python_version = python_identifier[2:4]
|
python_version = python_identifier[2:4]
|
||||||
|
|
||||||
if python_interpreter == 'cp':
|
if python_interpreter == "cp":
|
||||||
build_description += 'CPython'
|
build_description += "CPython"
|
||||||
elif python_interpreter == 'pp':
|
elif python_interpreter == "pp":
|
||||||
build_description += 'PyPy'
|
build_description += "PyPy"
|
||||||
else:
|
else:
|
||||||
raise Exception('unknown python')
|
raise Exception("unknown python")
|
||||||
|
|
||||||
build_description += f' {python_version[0]}.{python_version[1]} '
|
build_description += f" {python_version[0]}.{python_version[1]} "
|
||||||
|
|
||||||
try:
|
try:
|
||||||
build_description += PLATFORM_IDENTIFIER_DESCIPTIONS[platform_identifier]
|
build_description += PLATFORM_IDENTIFIER_DESCIPTIONS[platform_identifier]
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise Exception('unknown platform') from e
|
raise Exception("unknown platform") from e
|
||||||
|
|
||||||
return build_description
|
return build_description
|
||||||
|
|
||||||
|
|
||||||
class Colors:
|
class Colors:
|
||||||
def __init__(self, *, enabled: bool) -> None:
|
def __init__(self, *, enabled: bool) -> None:
|
||||||
self.red = '\033[31m' if enabled else ''
|
self.red = "\033[31m" if enabled else ""
|
||||||
self.green = '\033[32m' if enabled else ''
|
self.green = "\033[32m" if enabled else ""
|
||||||
self.yellow = '\033[33m' if enabled else ''
|
self.yellow = "\033[33m" if enabled else ""
|
||||||
self.blue = '\033[34m' if enabled else ''
|
self.blue = "\033[34m" if enabled else ""
|
||||||
self.cyan = '\033[36m' if enabled else ''
|
self.cyan = "\033[36m" if enabled else ""
|
||||||
self.bright_red = '\033[91m' if enabled else ''
|
self.bright_red = "\033[91m" if enabled else ""
|
||||||
self.bright_green = '\033[92m' if enabled else ''
|
self.bright_green = "\033[92m" if enabled else ""
|
||||||
self.white = '\033[37m\033[97m' if enabled else ''
|
self.white = "\033[37m\033[97m" if enabled else ""
|
||||||
|
|
||||||
self.bg_grey = '\033[48;5;235m' if enabled else ''
|
self.bg_grey = "\033[48;5;235m" if enabled else ""
|
||||||
|
|
||||||
self.bold = '\033[1m' if enabled else ''
|
self.bold = "\033[1m" if enabled else ""
|
||||||
self.faint = '\033[2m' if enabled else ''
|
self.faint = "\033[2m" if enabled else ""
|
||||||
|
|
||||||
self.end = '\033[0m' if enabled else ''
|
self.end = "\033[0m" if enabled else ""
|
||||||
|
|
||||||
|
|
||||||
class Symbols:
|
class Symbols:
|
||||||
def __init__(self, *, unicode: bool) -> None:
|
def __init__(self, *, unicode: bool) -> None:
|
||||||
self.done = '✓' if unicode else 'done'
|
self.done = "✓" if unicode else "done"
|
||||||
self.error = '✕' if unicode else 'failed'
|
self.error = "✕" if unicode else "failed"
|
||||||
|
|
||||||
|
|
||||||
def file_supports_color(file_obj: IO[AnyStr]) -> bool:
|
def file_supports_color(file_obj: IO[AnyStr]) -> bool:
|
||||||
@@ -235,7 +235,7 @@ def file_supports_color(file_obj: IO[AnyStr]) -> bool:
|
|||||||
Returns True if the running system's terminal supports color.
|
Returns True if the running system's terminal supports color.
|
||||||
"""
|
"""
|
||||||
plat = sys.platform
|
plat = sys.platform
|
||||||
supported_platform = plat != 'win32' or 'ANSICON' in os.environ
|
supported_platform = plat != "win32" or "ANSICON" in os.environ
|
||||||
|
|
||||||
is_a_tty = file_is_a_tty(file_obj)
|
is_a_tty = file_is_a_tty(file_obj)
|
||||||
|
|
||||||
@@ -243,21 +243,21 @@ def file_supports_color(file_obj: IO[AnyStr]) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def file_is_a_tty(file_obj: IO[AnyStr]) -> bool:
|
def file_is_a_tty(file_obj: IO[AnyStr]) -> bool:
|
||||||
return hasattr(file_obj, 'isatty') and file_obj.isatty()
|
return hasattr(file_obj, "isatty") and file_obj.isatty()
|
||||||
|
|
||||||
|
|
||||||
def file_supports_unicode(file_obj: IO[AnyStr]) -> bool:
|
def file_supports_unicode(file_obj: IO[AnyStr]) -> bool:
|
||||||
encoding = getattr(file_obj, 'encoding', None)
|
encoding = getattr(file_obj, "encoding", None)
|
||||||
if not encoding:
|
if not encoding:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
codec_info = codecs.lookup(encoding)
|
codec_info = codecs.lookup(encoding)
|
||||||
|
|
||||||
return 'utf' in codec_info.name
|
return "utf" in codec_info.name
|
||||||
|
|
||||||
|
|
||||||
'''
|
"""
|
||||||
Global instance of the Logger.
|
Global instance of the Logger.
|
||||||
'''
|
"""
|
||||||
# (there's only one stdout per-process, so a global instance is justified)
|
# (there's only one stdout per-process, so a global instance is justified)
|
||||||
log = Logger()
|
log = Logger()
|
||||||
|
|||||||
+139
-139
@@ -36,22 +36,22 @@ def call(
|
|||||||
) -> None:
|
) -> None:
|
||||||
# print the command executing for the logs
|
# print the command executing for the logs
|
||||||
if shell:
|
if shell:
|
||||||
print(f'+ {args}')
|
print(f"+ {args}")
|
||||||
else:
|
else:
|
||||||
print('+ ' + ' '.join(shlex.quote(str(a)) for a in args))
|
print("+ " + " ".join(shlex.quote(str(a)) for a in args))
|
||||||
|
|
||||||
subprocess.run(args, env=env, cwd=cwd, shell=shell, check=True)
|
subprocess.run(args, env=env, cwd=cwd, shell=shell, check=True)
|
||||||
|
|
||||||
|
|
||||||
def get_macos_version() -> Tuple[int, int]:
|
def get_macos_version() -> Tuple[int, int]:
|
||||||
'''
|
"""
|
||||||
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
|
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
|
||||||
|
|
||||||
These tuples can be used in comparisons, e.g.
|
These tuples can be used in comparisons, e.g.
|
||||||
(10, 14) <= (11, 0) == True
|
(10, 14) <= (11, 0) == True
|
||||||
(10, 14) <= (10, 16) == True
|
(10, 14) <= (10, 16) == True
|
||||||
(11, 2) <= (11, 0) != True
|
(11, 2) <= (11, 0) != True
|
||||||
'''
|
"""
|
||||||
version_str, _, _ = platform.mac_ver()
|
version_str, _, _ = platform.mac_ver()
|
||||||
version = tuple(map(int, version_str.split(".")[:2]))
|
version = tuple(map(int, version_str.split(".")[:2]))
|
||||||
return cast(Tuple[int, int], version)
|
return cast(Tuple[int, int], version)
|
||||||
@@ -59,13 +59,13 @@ def get_macos_version() -> Tuple[int, int]:
|
|||||||
|
|
||||||
def get_macos_sdks() -> List[str]:
|
def get_macos_sdks() -> List[str]:
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
['xcodebuild', '-showsdks'],
|
["xcodebuild", "-showsdks"],
|
||||||
universal_newlines=True,
|
universal_newlines=True,
|
||||||
check=True,
|
check=True,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
).stdout
|
).stdout
|
||||||
|
|
||||||
return [m.group(1) for m in re.finditer(r'-sdk (macosx\S+)', output)]
|
return [m.group(1) for m in re.finditer(r"-sdk (macosx\S+)", output)]
|
||||||
|
|
||||||
|
|
||||||
class PythonConfiguration(NamedTuple):
|
class PythonConfiguration(NamedTuple):
|
||||||
@@ -78,7 +78,7 @@ def get_python_configurations(
|
|||||||
build_selector: BuildSelector, architectures: Set[Architecture]
|
build_selector: BuildSelector, architectures: Set[Architecture]
|
||||||
) -> List[PythonConfiguration]:
|
) -> List[PythonConfiguration]:
|
||||||
|
|
||||||
full_python_configs = read_python_configs('macos')
|
full_python_configs = read_python_configs("macos")
|
||||||
|
|
||||||
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
|
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
|
||||||
|
|
||||||
@@ -95,41 +95,41 @@ def get_python_configurations(
|
|||||||
# When running on macOS 11 and x86_64, the reported OS is '10.16', but
|
# When running on macOS 11 and x86_64, the reported OS is '10.16', but
|
||||||
# there is no such OS - it really means macOS 11.
|
# there is no such OS - it really means macOS 11.
|
||||||
if get_macos_version() >= (10, 16):
|
if get_macos_version() >= (10, 16):
|
||||||
if any(c.identifier.startswith('pp') for c in python_configurations):
|
if any(c.identifier.startswith("pp") for c in python_configurations):
|
||||||
# pypy doesn't work on macOS 11 yet
|
# pypy doesn't work on macOS 11 yet
|
||||||
# See https://foss.heptapod.net/pypy/pypy/-/issues/3314
|
# See https://foss.heptapod.net/pypy/pypy/-/issues/3314
|
||||||
log.warning(
|
log.warning(
|
||||||
unwrap(
|
unwrap(
|
||||||
'''
|
"""
|
||||||
PyPy is currently unsupported when building on macOS 11. To build macOS PyPy
|
PyPy is currently unsupported when building on macOS 11. To build macOS PyPy
|
||||||
wheels, build on an older OS, such as macOS 10.15. To silence this warning,
|
wheels, build on an older OS, such as macOS 10.15. To silence this warning,
|
||||||
deselect PyPy by adding "pp*-macosx*" to your CIBW_SKIP option.
|
deselect PyPy by adding "pp*-macosx*" to your CIBW_SKIP option.
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
python_configurations = [
|
python_configurations = [
|
||||||
c for c in python_configurations if not c.identifier.startswith('pp')
|
c for c in python_configurations if not c.identifier.startswith("pp")
|
||||||
]
|
]
|
||||||
|
|
||||||
if any(c.identifier.startswith('cp35') for c in python_configurations):
|
if any(c.identifier.startswith("cp35") for c in python_configurations):
|
||||||
# CPython 3.5 doesn't work on macOS 11
|
# CPython 3.5 doesn't work on macOS 11
|
||||||
log.warning(
|
log.warning(
|
||||||
unwrap(
|
unwrap(
|
||||||
'''
|
"""
|
||||||
CPython 3.5 is unsupported when building on macOS 11. To build CPython 3.5
|
CPython 3.5 is unsupported when building on macOS 11. To build CPython 3.5
|
||||||
wheels, build on an older OS, such as macOS 10.15. To silence this warning,
|
wheels, build on an older OS, such as macOS 10.15. To silence this warning,
|
||||||
deselect CPython 3.5 by adding "cp35-macosx_x86_64" to your CIBW_SKIP option.
|
deselect CPython 3.5 by adding "cp35-macosx_x86_64" to your CIBW_SKIP option.
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
python_configurations = [
|
python_configurations = [
|
||||||
c for c in python_configurations if not c.identifier.startswith('cp35')
|
c for c in python_configurations if not c.identifier.startswith("cp35")
|
||||||
]
|
]
|
||||||
|
|
||||||
return python_configurations
|
return python_configurations
|
||||||
|
|
||||||
|
|
||||||
SYMLINKS_DIR = Path('/tmp/cibw_bin')
|
SYMLINKS_DIR = Path("/tmp/cibw_bin")
|
||||||
|
|
||||||
|
|
||||||
def make_symlinks(installation_bin_path: Path, python_executable: str, pip_executable: str) -> None:
|
def make_symlinks(installation_bin_path: Path, python_executable: str, pip_executable: str) -> None:
|
||||||
@@ -142,69 +142,69 @@ def make_symlinks(installation_bin_path: Path, python_executable: str, pip_execu
|
|||||||
shutil.rmtree(SYMLINKS_DIR)
|
shutil.rmtree(SYMLINKS_DIR)
|
||||||
SYMLINKS_DIR.mkdir(parents=True)
|
SYMLINKS_DIR.mkdir(parents=True)
|
||||||
|
|
||||||
(SYMLINKS_DIR / 'python').symlink_to(installation_bin_path / python_executable)
|
(SYMLINKS_DIR / "python").symlink_to(installation_bin_path / python_executable)
|
||||||
(SYMLINKS_DIR / 'python-config').symlink_to(
|
(SYMLINKS_DIR / "python-config").symlink_to(
|
||||||
installation_bin_path / (python_executable + '-config')
|
installation_bin_path / (python_executable + "-config")
|
||||||
)
|
)
|
||||||
(SYMLINKS_DIR / 'pip').symlink_to(installation_bin_path / pip_executable)
|
(SYMLINKS_DIR / "pip").symlink_to(installation_bin_path / pip_executable)
|
||||||
|
|
||||||
|
|
||||||
def install_cpython(version: str, url: str) -> Path:
|
def install_cpython(version: str, url: str) -> Path:
|
||||||
installed_system_packages = subprocess.run(
|
installed_system_packages = subprocess.run(
|
||||||
['pkgutil', '--pkgs'], universal_newlines=True, check=True, stdout=subprocess.PIPE
|
["pkgutil", "--pkgs"], universal_newlines=True, check=True, stdout=subprocess.PIPE
|
||||||
).stdout.splitlines()
|
).stdout.splitlines()
|
||||||
|
|
||||||
# if this version of python isn't installed, get it from python.org and install
|
# if this version of python isn't installed, get it from python.org and install
|
||||||
python_package_identifier = f'org.python.Python.PythonFramework-{version}'
|
python_package_identifier = f"org.python.Python.PythonFramework-{version}"
|
||||||
python_executable = 'python3' if version[0] == '3' else 'python'
|
python_executable = "python3" if version[0] == "3" else "python"
|
||||||
installation_bin_path = Path(f'/Library/Frameworks/Python.framework/Versions/{version}/bin')
|
installation_bin_path = Path(f"/Library/Frameworks/Python.framework/Versions/{version}/bin")
|
||||||
|
|
||||||
if python_package_identifier not in installed_system_packages:
|
if python_package_identifier not in installed_system_packages:
|
||||||
# download the pkg
|
# download the pkg
|
||||||
download(url, Path('/tmp/Python.pkg'))
|
download(url, Path("/tmp/Python.pkg"))
|
||||||
# install
|
# install
|
||||||
call(['sudo', 'installer', '-pkg', '/tmp/Python.pkg', '-target', '/'])
|
call(["sudo", "installer", "-pkg", "/tmp/Python.pkg", "-target", "/"])
|
||||||
# patch open ssl
|
# patch open ssl
|
||||||
if version == '3.5':
|
if version == "3.5":
|
||||||
open_ssl_patch_url = f'https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.1.1h/patch-macos-python-{version}-openssl-v1.1.1h.tar.gz'
|
open_ssl_patch_url = f"https://github.com/mayeut/patch-macos-python-openssl/releases/download/v1.1.1h/patch-macos-python-{version}-openssl-v1.1.1h.tar.gz"
|
||||||
download(open_ssl_patch_url, Path('/tmp/python-patch.tar.gz'))
|
download(open_ssl_patch_url, Path("/tmp/python-patch.tar.gz"))
|
||||||
call(
|
call(
|
||||||
[
|
[
|
||||||
'sudo',
|
"sudo",
|
||||||
'tar',
|
"tar",
|
||||||
'-C',
|
"-C",
|
||||||
f'/Library/Frameworks/Python.framework/Versions/{version}/',
|
f"/Library/Frameworks/Python.framework/Versions/{version}/",
|
||||||
'-xmf',
|
"-xmf",
|
||||||
'/tmp/python-patch.tar.gz',
|
"/tmp/python-patch.tar.gz",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
call(["sudo", str(installation_bin_path / python_executable), str(install_certifi_script)])
|
call(["sudo", str(installation_bin_path / python_executable), str(install_certifi_script)])
|
||||||
|
|
||||||
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
pip_executable = "pip3" if version[0] == "3" else "pip"
|
||||||
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
||||||
|
|
||||||
return installation_bin_path
|
return installation_bin_path
|
||||||
|
|
||||||
|
|
||||||
def install_pypy(version: str, url: str) -> Path:
|
def install_pypy(version: str, url: str) -> Path:
|
||||||
pypy_tar_bz2 = url.rsplit('/', 1)[-1]
|
pypy_tar_bz2 = url.rsplit("/", 1)[-1]
|
||||||
extension = ".tar.bz2"
|
extension = ".tar.bz2"
|
||||||
assert pypy_tar_bz2.endswith(extension)
|
assert pypy_tar_bz2.endswith(extension)
|
||||||
pypy_base_filename = pypy_tar_bz2[: -len(extension)]
|
pypy_base_filename = pypy_tar_bz2[: -len(extension)]
|
||||||
installation_path = Path('/tmp') / pypy_base_filename
|
installation_path = Path("/tmp") / pypy_base_filename
|
||||||
if not installation_path.exists():
|
if not installation_path.exists():
|
||||||
downloaded_tar_bz2 = Path("/tmp") / pypy_tar_bz2
|
downloaded_tar_bz2 = Path("/tmp") / pypy_tar_bz2
|
||||||
download(url, downloaded_tar_bz2)
|
download(url, downloaded_tar_bz2)
|
||||||
call(['tar', '-C', '/tmp', '-xf', downloaded_tar_bz2])
|
call(["tar", "-C", "/tmp", "-xf", downloaded_tar_bz2])
|
||||||
# Patch PyPy to make sure headers get installed into a venv
|
# Patch PyPy to make sure headers get installed into a venv
|
||||||
patch_version = '_27' if version == '2.7' else ''
|
patch_version = "_27" if version == "2.7" else ""
|
||||||
patch_path = resources_dir / f'pypy_venv{patch_version}.patch'
|
patch_path = resources_dir / f"pypy_venv{patch_version}.patch"
|
||||||
call(['patch', '--force', '-p1', '-d', installation_path, '-i', patch_path])
|
call(["patch", "--force", "-p1", "-d", installation_path, "-i", patch_path])
|
||||||
|
|
||||||
installation_bin_path = installation_path / 'bin'
|
installation_bin_path = installation_path / "bin"
|
||||||
python_executable = 'pypy3' if version[0] == '3' else 'pypy'
|
python_executable = "pypy3" if version[0] == "3" else "pypy"
|
||||||
pip_executable = 'pip3' if version[0] == '3' else 'pip'
|
pip_executable = "pip3" if version[0] == "3" else "pip"
|
||||||
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
make_symlinks(installation_bin_path, python_executable, pip_executable)
|
||||||
|
|
||||||
return installation_bin_path
|
return installation_bin_path
|
||||||
@@ -216,25 +216,25 @@ def setup_python(
|
|||||||
environment: ParsedEnvironment,
|
environment: ParsedEnvironment,
|
||||||
) -> Dict[str, str]:
|
) -> Dict[str, str]:
|
||||||
implementation_id = python_configuration.identifier.split("-")[0]
|
implementation_id = python_configuration.identifier.split("-")[0]
|
||||||
log.step(f'Installing Python {implementation_id}...')
|
log.step(f"Installing Python {implementation_id}...")
|
||||||
|
|
||||||
if implementation_id.startswith('cp'):
|
if implementation_id.startswith("cp"):
|
||||||
installation_bin_path = install_cpython(
|
installation_bin_path = install_cpython(
|
||||||
python_configuration.version, python_configuration.url
|
python_configuration.version, python_configuration.url
|
||||||
)
|
)
|
||||||
elif implementation_id.startswith('pp'):
|
elif implementation_id.startswith("pp"):
|
||||||
installation_bin_path = install_pypy(python_configuration.version, python_configuration.url)
|
installation_bin_path = install_pypy(python_configuration.version, python_configuration.url)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unknown Python implementation")
|
raise ValueError("Unknown Python implementation")
|
||||||
|
|
||||||
log.step('Setting up build environment...')
|
log.step("Setting up build environment...")
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env['PATH'] = os.pathsep.join(
|
env["PATH"] = os.pathsep.join(
|
||||||
[
|
[
|
||||||
str(SYMLINKS_DIR),
|
str(SYMLINKS_DIR),
|
||||||
str(installation_bin_path),
|
str(installation_bin_path),
|
||||||
env['PATH'],
|
env["PATH"],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -244,19 +244,19 @@ def setup_python(
|
|||||||
# testing virtualenv- see https://github.com/theacodes/nox/issues/44 and
|
# testing virtualenv- see https://github.com/theacodes/nox/issues/44 and
|
||||||
# https://github.com/pypa/virtualenv/issues/620
|
# https://github.com/pypa/virtualenv/issues/620
|
||||||
# Also see https://github.com/python/cpython/pull/9516
|
# Also see https://github.com/python/cpython/pull/9516
|
||||||
env.pop('__PYVENV_LAUNCHER__', None)
|
env.pop("__PYVENV_LAUNCHER__", None)
|
||||||
env = environment.as_dictionary(prev_environment=env)
|
env = environment.as_dictionary(prev_environment=env)
|
||||||
|
|
||||||
# we version pip ourselves, so we don't care about pip version checking
|
# we version pip ourselves, so we don't care about pip version checking
|
||||||
env['PIP_DISABLE_PIP_VERSION_CHECK'] = '1'
|
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
|
||||||
|
|
||||||
# check what version we're on
|
# check what version we're on
|
||||||
call(['which', 'python'], env=env)
|
call(["which", "python"], env=env)
|
||||||
call(['python', '--version'], env=env)
|
call(["python", "--version"], env=env)
|
||||||
which_python = subprocess.run(
|
which_python = subprocess.run(
|
||||||
['which', 'python'], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE
|
["which", "python"], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE
|
||||||
).stdout.strip()
|
).stdout.strip()
|
||||||
if which_python != '/tmp/cibw_bin/python':
|
if which_python != "/tmp/cibw_bin/python":
|
||||||
print(
|
print(
|
||||||
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
@@ -264,14 +264,14 @@ def setup_python(
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# install pip & wheel
|
# install pip & wheel
|
||||||
call(['python', get_pip_script, *dependency_constraint_flags], env=env, cwd="/tmp")
|
call(["python", get_pip_script, *dependency_constraint_flags], env=env, cwd="/tmp")
|
||||||
assert (installation_bin_path / 'pip').exists()
|
assert (installation_bin_path / "pip").exists()
|
||||||
call(['which', 'pip'], env=env)
|
call(["which", "pip"], env=env)
|
||||||
call(['pip', '--version'], env=env)
|
call(["pip", "--version"], env=env)
|
||||||
which_pip = subprocess.run(
|
which_pip = subprocess.run(
|
||||||
['which', 'pip'], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE
|
["which", "pip"], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE
|
||||||
).stdout.strip()
|
).stdout.strip()
|
||||||
if which_pip != '/tmp/cibw_bin/pip':
|
if which_pip != "/tmp/cibw_bin/pip":
|
||||||
print(
|
print(
|
||||||
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
@@ -281,66 +281,66 @@ def setup_python(
|
|||||||
# Set MACOSX_DEPLOYMENT_TARGET to 10.9, if the user didn't set it.
|
# Set MACOSX_DEPLOYMENT_TARGET to 10.9, if the user didn't set it.
|
||||||
# CPython 3.5 defaults to 10.6, and pypy defaults to 10.7, causing
|
# CPython 3.5 defaults to 10.6, and pypy defaults to 10.7, causing
|
||||||
# inconsistencies if it's left unset.
|
# inconsistencies if it's left unset.
|
||||||
env.setdefault('MACOSX_DEPLOYMENT_TARGET', '10.9')
|
env.setdefault("MACOSX_DEPLOYMENT_TARGET", "10.9")
|
||||||
|
|
||||||
config_is_arm64 = python_configuration.identifier.endswith('arm64')
|
config_is_arm64 = python_configuration.identifier.endswith("arm64")
|
||||||
config_is_universal2 = python_configuration.identifier.endswith('universal2')
|
config_is_universal2 = python_configuration.identifier.endswith("universal2")
|
||||||
|
|
||||||
if python_configuration.version == '3.5':
|
if python_configuration.version == "3.5":
|
||||||
# Cross-compilation platform override - CPython 3.5 has an
|
# Cross-compilation platform override - CPython 3.5 has an
|
||||||
# i386/x86_64 version of Python, but we only want a x64_64 build
|
# i386/x86_64 version of Python, but we only want a x64_64 build
|
||||||
env.setdefault('_PYTHON_HOST_PLATFORM', 'macosx-10.9-x86_64')
|
env.setdefault("_PYTHON_HOST_PLATFORM", "macosx-10.9-x86_64")
|
||||||
# https://github.com/python/cpython/blob/a5ed2fe0eedefa1649aa93ee74a0bafc8e628a10/Lib/_osx_support.py#L260
|
# https://github.com/python/cpython/blob/a5ed2fe0eedefa1649aa93ee74a0bafc8e628a10/Lib/_osx_support.py#L260
|
||||||
env.setdefault('ARCHFLAGS', '-arch x86_64')
|
env.setdefault("ARCHFLAGS", "-arch x86_64")
|
||||||
|
|
||||||
if python_configuration.version == '3.9':
|
if python_configuration.version == "3.9":
|
||||||
if python_configuration.identifier.endswith('x86_64'):
|
if python_configuration.identifier.endswith("x86_64"):
|
||||||
# even on the macos11.0 Python installer, on the x86_64 side it's
|
# even on the macos11.0 Python installer, on the x86_64 side it's
|
||||||
# compatible back to 10.9.
|
# compatible back to 10.9.
|
||||||
env.setdefault('_PYTHON_HOST_PLATFORM', 'macosx-10.9-x86_64')
|
env.setdefault("_PYTHON_HOST_PLATFORM", "macosx-10.9-x86_64")
|
||||||
env.setdefault('ARCHFLAGS', '-arch x86_64')
|
env.setdefault("ARCHFLAGS", "-arch x86_64")
|
||||||
elif config_is_arm64:
|
elif config_is_arm64:
|
||||||
# macOS 11 is the first OS with arm64 support, so the wheels
|
# macOS 11 is the first OS with arm64 support, so the wheels
|
||||||
# have that as a minimum.
|
# have that as a minimum.
|
||||||
env.setdefault('_PYTHON_HOST_PLATFORM', 'macosx-11.0-arm64')
|
env.setdefault("_PYTHON_HOST_PLATFORM", "macosx-11.0-arm64")
|
||||||
env.setdefault('ARCHFLAGS', '-arch arm64')
|
env.setdefault("ARCHFLAGS", "-arch arm64")
|
||||||
elif config_is_universal2:
|
elif config_is_universal2:
|
||||||
if get_macos_version() < (10, 16):
|
if get_macos_version() < (10, 16):
|
||||||
# we can do universal2 builds on macos 10.15, but we need to
|
# we can do universal2 builds on macos 10.15, but we need to
|
||||||
# set ARCHFLAGS otherwise CPython sets it to `-arch x86_64`
|
# set ARCHFLAGS otherwise CPython sets it to `-arch x86_64`
|
||||||
env.setdefault('ARCHFLAGS', '-arch arm64 -arch x86_64')
|
env.setdefault("ARCHFLAGS", "-arch arm64 -arch x86_64")
|
||||||
|
|
||||||
building_arm64 = config_is_arm64 or config_is_universal2
|
building_arm64 = config_is_arm64 or config_is_universal2
|
||||||
if building_arm64 and get_macos_version() < (10, 16) and 'SDKROOT' not in env:
|
if building_arm64 and get_macos_version() < (10, 16) and "SDKROOT" not in env:
|
||||||
# xcode 12.2 or higher can build arm64 on macos 10.15 or below, but
|
# xcode 12.2 or higher can build arm64 on macos 10.15 or below, but
|
||||||
# needs the correct SDK selected.
|
# needs the correct SDK selected.
|
||||||
sdks = get_macos_sdks()
|
sdks = get_macos_sdks()
|
||||||
|
|
||||||
# Different versions of Xcode contain different SDK versions...
|
# Different versions of Xcode contain different SDK versions...
|
||||||
# we're happy with anything newer than macOS 11.0
|
# we're happy with anything newer than macOS 11.0
|
||||||
arm64_compatible_sdks = [s for s in sdks if not s.startswith('macosx10.')]
|
arm64_compatible_sdks = [s for s in sdks if not s.startswith("macosx10.")]
|
||||||
|
|
||||||
if not arm64_compatible_sdks:
|
if not arm64_compatible_sdks:
|
||||||
log.warning(
|
log.warning(
|
||||||
unwrap(
|
unwrap(
|
||||||
'''
|
"""
|
||||||
SDK for building arm64-compatible wheels not found. You need Xcode 12.2 or later
|
SDK for building arm64-compatible wheels not found. You need Xcode 12.2 or later
|
||||||
to build universal2 or arm64 wheels.
|
to build universal2 or arm64 wheels.
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
env.setdefault('SDKROOT', arm64_compatible_sdks[0])
|
env.setdefault("SDKROOT", arm64_compatible_sdks[0])
|
||||||
|
|
||||||
log.step('Installing build tools...')
|
log.step("Installing build tools...")
|
||||||
call(
|
call(
|
||||||
[
|
[
|
||||||
'pip',
|
"pip",
|
||||||
'install',
|
"install",
|
||||||
'--upgrade',
|
"--upgrade",
|
||||||
'setuptools',
|
"setuptools",
|
||||||
'wheel',
|
"wheel",
|
||||||
'delocate',
|
"delocate",
|
||||||
*dependency_constraint_flags,
|
*dependency_constraint_flags,
|
||||||
],
|
],
|
||||||
env=env,
|
env=env,
|
||||||
@@ -350,17 +350,17 @@ def setup_python(
|
|||||||
|
|
||||||
|
|
||||||
def build(options: BuildOptions) -> None:
|
def build(options: BuildOptions) -> None:
|
||||||
temp_dir = Path(tempfile.mkdtemp(prefix='cibuildwheel'))
|
temp_dir = Path(tempfile.mkdtemp(prefix="cibuildwheel"))
|
||||||
built_wheel_dir = temp_dir / 'built_wheel'
|
built_wheel_dir = temp_dir / "built_wheel"
|
||||||
repaired_wheel_dir = temp_dir / 'repaired_wheel'
|
repaired_wheel_dir = temp_dir / "repaired_wheel"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if options.before_all:
|
if options.before_all:
|
||||||
log.step('Running before_all...')
|
log.step("Running before_all...")
|
||||||
env = options.environment.as_dictionary(prev_environment=os.environ)
|
env = options.environment.as_dictionary(prev_environment=os.environ)
|
||||||
env.setdefault('MACOSX_DEPLOYMENT_TARGET', '10.9')
|
env.setdefault("MACOSX_DEPLOYMENT_TARGET", "10.9")
|
||||||
before_all_prepared = prepare_command(
|
before_all_prepared = prepare_command(
|
||||||
options.before_all, project='.', package=options.package_dir
|
options.before_all, project=".", package=options.package_dir
|
||||||
)
|
)
|
||||||
call([before_all_prepared], shell=True, env=env)
|
call([before_all_prepared], shell=True, env=env)
|
||||||
|
|
||||||
@@ -371,26 +371,26 @@ def build(options: BuildOptions) -> None:
|
|||||||
for config in python_configurations:
|
for config in python_configurations:
|
||||||
log.build_start(config.identifier)
|
log.build_start(config.identifier)
|
||||||
|
|
||||||
config_is_arm64 = config.identifier.endswith('arm64')
|
config_is_arm64 = config.identifier.endswith("arm64")
|
||||||
config_is_universal2 = config.identifier.endswith('universal2')
|
config_is_universal2 = config.identifier.endswith("universal2")
|
||||||
|
|
||||||
dependency_constraint_flags: Sequence[PathOrStr] = []
|
dependency_constraint_flags: Sequence[PathOrStr] = []
|
||||||
if options.dependency_constraints:
|
if options.dependency_constraints:
|
||||||
dependency_constraint_flags = [
|
dependency_constraint_flags = [
|
||||||
'-c',
|
"-c",
|
||||||
options.dependency_constraints.get_for_python_version(config.version),
|
options.dependency_constraints.get_for_python_version(config.version),
|
||||||
]
|
]
|
||||||
|
|
||||||
env = setup_python(config, dependency_constraint_flags, options.environment)
|
env = setup_python(config, dependency_constraint_flags, options.environment)
|
||||||
|
|
||||||
if options.before_build:
|
if options.before_build:
|
||||||
log.step('Running before_build...')
|
log.step("Running before_build...")
|
||||||
before_build_prepared = prepare_command(
|
before_build_prepared = prepare_command(
|
||||||
options.before_build, project='.', package=options.package_dir
|
options.before_build, project=".", package=options.package_dir
|
||||||
)
|
)
|
||||||
call(before_build_prepared, env=env, shell=True)
|
call(before_build_prepared, env=env, shell=True)
|
||||||
|
|
||||||
log.step('Building wheel...')
|
log.step("Building wheel...")
|
||||||
if built_wheel_dir.exists():
|
if built_wheel_dir.exists():
|
||||||
shutil.rmtree(built_wheel_dir)
|
shutil.rmtree(built_wheel_dir)
|
||||||
built_wheel_dir.mkdir(parents=True)
|
built_wheel_dir.mkdir(parents=True)
|
||||||
@@ -399,35 +399,35 @@ def build(options: BuildOptions) -> None:
|
|||||||
# see https://github.com/joerick/cibuildwheel/pull/369
|
# see https://github.com/joerick/cibuildwheel/pull/369
|
||||||
call(
|
call(
|
||||||
[
|
[
|
||||||
'pip',
|
"pip",
|
||||||
'wheel',
|
"wheel",
|
||||||
options.package_dir.resolve(),
|
options.package_dir.resolve(),
|
||||||
'--wheel-dir',
|
"--wheel-dir",
|
||||||
built_wheel_dir,
|
built_wheel_dir,
|
||||||
'--no-deps',
|
"--no-deps",
|
||||||
*get_build_verbosity_extra_flags(options.build_verbosity),
|
*get_build_verbosity_extra_flags(options.build_verbosity),
|
||||||
],
|
],
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
|
|
||||||
built_wheel = next(built_wheel_dir.glob('*.whl'))
|
built_wheel = next(built_wheel_dir.glob("*.whl"))
|
||||||
|
|
||||||
if repaired_wheel_dir.exists():
|
if repaired_wheel_dir.exists():
|
||||||
shutil.rmtree(repaired_wheel_dir)
|
shutil.rmtree(repaired_wheel_dir)
|
||||||
repaired_wheel_dir.mkdir(parents=True)
|
repaired_wheel_dir.mkdir(parents=True)
|
||||||
|
|
||||||
if built_wheel.name.endswith('none-any.whl'):
|
if built_wheel.name.endswith("none-any.whl"):
|
||||||
raise NonPlatformWheelError()
|
raise NonPlatformWheelError()
|
||||||
|
|
||||||
if options.repair_command:
|
if options.repair_command:
|
||||||
log.step('Repairing wheel...')
|
log.step("Repairing wheel...")
|
||||||
|
|
||||||
if config_is_universal2:
|
if config_is_universal2:
|
||||||
delocate_archs = 'x86_64,arm64'
|
delocate_archs = "x86_64,arm64"
|
||||||
elif config_is_arm64:
|
elif config_is_arm64:
|
||||||
delocate_archs = 'arm64'
|
delocate_archs = "arm64"
|
||||||
else:
|
else:
|
||||||
delocate_archs = 'x86_64'
|
delocate_archs = "x86_64"
|
||||||
|
|
||||||
repair_command_prepared = prepare_command(
|
repair_command_prepared = prepare_command(
|
||||||
options.repair_command,
|
options.repair_command,
|
||||||
@@ -439,75 +439,75 @@ def build(options: BuildOptions) -> None:
|
|||||||
else:
|
else:
|
||||||
shutil.move(str(built_wheel), repaired_wheel_dir)
|
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||||
|
|
||||||
repaired_wheel = next(repaired_wheel_dir.glob('*.whl'))
|
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
||||||
|
|
||||||
log.step_end()
|
log.step_end()
|
||||||
|
|
||||||
if options.test_command and options.test_selector(config.identifier):
|
if options.test_command and options.test_selector(config.identifier):
|
||||||
machine_arch = platform.machine()
|
machine_arch = platform.machine()
|
||||||
testing_archs: List[Literal['x86_64', 'arm64']] = []
|
testing_archs: List[Literal["x86_64", "arm64"]] = []
|
||||||
|
|
||||||
if config_is_arm64:
|
if config_is_arm64:
|
||||||
testing_archs = ['arm64']
|
testing_archs = ["arm64"]
|
||||||
elif config_is_universal2:
|
elif config_is_universal2:
|
||||||
testing_archs = ['x86_64', 'arm64']
|
testing_archs = ["x86_64", "arm64"]
|
||||||
else:
|
else:
|
||||||
testing_archs = ['x86_64']
|
testing_archs = ["x86_64"]
|
||||||
|
|
||||||
for testing_arch in testing_archs:
|
for testing_arch in testing_archs:
|
||||||
if config_is_universal2:
|
if config_is_universal2:
|
||||||
arch_specific_identifier = f'{config.identifier}:{testing_arch}'
|
arch_specific_identifier = f"{config.identifier}:{testing_arch}"
|
||||||
if not options.test_selector(arch_specific_identifier):
|
if not options.test_selector(arch_specific_identifier):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if machine_arch == 'x86_64' and testing_arch == 'arm64':
|
if machine_arch == "x86_64" and testing_arch == "arm64":
|
||||||
if config_is_arm64:
|
if config_is_arm64:
|
||||||
log.warning(
|
log.warning(
|
||||||
unwrap(
|
unwrap(
|
||||||
'''
|
"""
|
||||||
While arm64 wheels can be built on x86_64, they cannot be
|
While arm64 wheels can be built on x86_64, they cannot be
|
||||||
tested. The ability to test the arm64 wheels will be added in a
|
tested. The ability to test the arm64 wheels will be added in a
|
||||||
future release of cibuildwheel, once Apple Silicon CI runners
|
future release of cibuildwheel, once Apple Silicon CI runners
|
||||||
are widely available. To silence this warning, set
|
are widely available. To silence this warning, set
|
||||||
`CIBW_TEST_SKIP: *-macosx_arm64`.
|
`CIBW_TEST_SKIP: *-macosx_arm64`.
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif config_is_universal2:
|
elif config_is_universal2:
|
||||||
log.warning(
|
log.warning(
|
||||||
unwrap(
|
unwrap(
|
||||||
'''
|
"""
|
||||||
While universal2 wheels can be built on x86_64, the arm64 part
|
While universal2 wheels can be built on x86_64, the arm64 part
|
||||||
of them cannot currently be tested. The ability to test the
|
of them cannot currently be tested. The ability to test the
|
||||||
arm64 part of a universal2 wheel will be added in a future
|
arm64 part of a universal2 wheel will be added in a future
|
||||||
release of cibuildwheel, once Apple Silicon CI runners are
|
release of cibuildwheel, once Apple Silicon CI runners are
|
||||||
widely available. To silence this warning, set
|
widely available. To silence this warning, set
|
||||||
`CIBW_TEST_SKIP: *-macosx_universal2:arm64`.
|
`CIBW_TEST_SKIP: *-macosx_universal2:arm64`.
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('unreachable')
|
raise RuntimeError("unreachable")
|
||||||
|
|
||||||
# skip this test
|
# skip this test
|
||||||
continue
|
continue
|
||||||
|
|
||||||
log.step(
|
log.step(
|
||||||
'Testing wheel...'
|
"Testing wheel..."
|
||||||
if testing_arch == machine_arch
|
if testing_arch == machine_arch
|
||||||
else f'Testing wheel on {testing_arch}...'
|
else f"Testing wheel on {testing_arch}..."
|
||||||
)
|
)
|
||||||
|
|
||||||
# set up a virtual environment to install and test from, to make sure
|
# set up a virtual environment to install and test from, to make sure
|
||||||
# there are no dependencies that were pulled in at build time.
|
# there are no dependencies that were pulled in at build time.
|
||||||
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
|
call(["pip", "install", "virtualenv", *dependency_constraint_flags], env=env)
|
||||||
venv_dir = Path(tempfile.mkdtemp())
|
venv_dir = Path(tempfile.mkdtemp())
|
||||||
|
|
||||||
arch_prefix = []
|
arch_prefix = []
|
||||||
if testing_arch != machine_arch:
|
if testing_arch != machine_arch:
|
||||||
if machine_arch == 'arm64' and testing_arch == 'x86_64':
|
if machine_arch == "arm64" and testing_arch == "x86_64":
|
||||||
# rosetta2 will provide the emulation with just the arch prefix.
|
# rosetta2 will provide the emulation with just the arch prefix.
|
||||||
arch_prefix = ['arch', '-x86_64']
|
arch_prefix = ["arch", "-x86_64"]
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"don't know how to emulate {testing_arch} on {machine_arch}"
|
"don't know how to emulate {testing_arch} on {machine_arch}"
|
||||||
@@ -516,7 +516,7 @@ def build(options: BuildOptions) -> None:
|
|||||||
# define a custom 'call' function that adds the arch prefix each time
|
# define a custom 'call' function that adds the arch prefix each time
|
||||||
def call_with_arch(args: Sequence[PathOrStr], **kwargs: Any) -> None:
|
def call_with_arch(args: Sequence[PathOrStr], **kwargs: Any) -> None:
|
||||||
if isinstance(args, str):
|
if isinstance(args, str):
|
||||||
args = ' '.join(arch_prefix) + ' ' + args
|
args = " ".join(arch_prefix) + " " + args
|
||||||
else:
|
else:
|
||||||
args = [*arch_prefix, *args]
|
args = [*arch_prefix, *args]
|
||||||
call(args, **kwargs)
|
call(args, **kwargs)
|
||||||
@@ -524,36 +524,36 @@ def build(options: BuildOptions) -> None:
|
|||||||
# Use --no-download to ensure determinism by using seed libraries
|
# Use --no-download to ensure determinism by using seed libraries
|
||||||
# built into virtualenv
|
# built into virtualenv
|
||||||
call_with_arch(
|
call_with_arch(
|
||||||
['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env
|
["python", "-m", "virtualenv", "--no-download", venv_dir], env=env
|
||||||
)
|
)
|
||||||
|
|
||||||
virtualenv_env = env.copy()
|
virtualenv_env = env.copy()
|
||||||
virtualenv_env['PATH'] = os.pathsep.join(
|
virtualenv_env["PATH"] = os.pathsep.join(
|
||||||
[
|
[
|
||||||
str(venv_dir / 'bin'),
|
str(venv_dir / "bin"),
|
||||||
virtualenv_env['PATH'],
|
virtualenv_env["PATH"],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that we are using the Python from the virtual environment
|
# check that we are using the Python from the virtual environment
|
||||||
call_with_arch(['which', 'python'], env=virtualenv_env)
|
call_with_arch(["which", "python"], env=virtualenv_env)
|
||||||
|
|
||||||
if options.before_test:
|
if options.before_test:
|
||||||
before_test_prepared = prepare_command(
|
before_test_prepared = prepare_command(
|
||||||
options.before_test, project='.', package=options.package_dir
|
options.before_test, project=".", package=options.package_dir
|
||||||
)
|
)
|
||||||
call_with_arch(before_test_prepared, env=virtualenv_env, shell=True)
|
call_with_arch(before_test_prepared, env=virtualenv_env, shell=True)
|
||||||
|
|
||||||
# install the wheel
|
# install the wheel
|
||||||
call_with_arch(
|
call_with_arch(
|
||||||
['pip', 'install', f"{repaired_wheel}{options.test_extras}"],
|
["pip", "install", f"{repaired_wheel}{options.test_extras}"],
|
||||||
env=virtualenv_env,
|
env=virtualenv_env,
|
||||||
)
|
)
|
||||||
|
|
||||||
# test the wheel
|
# test the wheel
|
||||||
if options.test_requires:
|
if options.test_requires:
|
||||||
call_with_arch(
|
call_with_arch(
|
||||||
['pip', 'install'] + options.test_requires, env=virtualenv_env
|
["pip", "install"] + options.test_requires, env=virtualenv_env
|
||||||
)
|
)
|
||||||
|
|
||||||
# run the tests from $HOME, with an absolute path in the command
|
# run the tests from $HOME, with an absolute path in the command
|
||||||
@@ -561,12 +561,12 @@ def build(options: BuildOptions) -> None:
|
|||||||
# and not the repo code)
|
# and not the repo code)
|
||||||
test_command_prepared = prepare_command(
|
test_command_prepared = prepare_command(
|
||||||
options.test_command,
|
options.test_command,
|
||||||
project=Path('.').resolve(),
|
project=Path(".").resolve(),
|
||||||
package=options.package_dir.resolve(),
|
package=options.package_dir.resolve(),
|
||||||
)
|
)
|
||||||
call_with_arch(
|
call_with_arch(
|
||||||
test_command_prepared,
|
test_command_prepared,
|
||||||
cwd=os.environ['HOME'],
|
cwd=os.environ["HOME"],
|
||||||
env=virtualenv_env,
|
env=virtualenv_env,
|
||||||
shell=True,
|
shell=True,
|
||||||
)
|
)
|
||||||
@@ -579,6 +579,6 @@ def build(options: BuildOptions) -> None:
|
|||||||
log.build_end()
|
log.build_end()
|
||||||
except subprocess.CalledProcessError as error:
|
except subprocess.CalledProcessError as error:
|
||||||
log.step_end_with_error(
|
log.step_end_with_error(
|
||||||
f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}'
|
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -56,21 +56,21 @@ def get_requires_python_str(package_dir: Path) -> Optional[str]:
|
|||||||
|
|
||||||
# Read in from pyproject.toml:project.requires-python
|
# Read in from pyproject.toml:project.requires-python
|
||||||
try:
|
try:
|
||||||
info = toml.load(package_dir / 'pyproject.toml')
|
info = toml.load(package_dir / "pyproject.toml")
|
||||||
return str(info['project']['requires-python'])
|
return str(info["project"]["requires-python"])
|
||||||
except (FileNotFoundError, KeyError, IndexError, TypeError):
|
except (FileNotFoundError, KeyError, IndexError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Read in from setup.cfg:options.python_requires
|
# Read in from setup.cfg:options.python_requires
|
||||||
try:
|
try:
|
||||||
config = ConfigParser()
|
config = ConfigParser()
|
||||||
config.read(package_dir / 'setup.cfg')
|
config.read(package_dir / "setup.cfg")
|
||||||
return str(config['options']['python_requires'])
|
return str(config["options"]["python_requires"])
|
||||||
except (FileNotFoundError, KeyError, IndexError, TypeError):
|
except (FileNotFoundError, KeyError, IndexError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(package_dir / 'setup.py') as f:
|
with open(package_dir / "setup.py") as f:
|
||||||
return setup_py_python_requires(f.read())
|
return setup_py_python_requires(f.read())
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -52,9 +52,9 @@ except ImportError:
|
|||||||
_b85dec[c] = i
|
_b85dec[c] = i
|
||||||
|
|
||||||
padding = (-len(b)) % 5
|
padding = (-len(b)) % 5
|
||||||
b = b + b'~' * padding
|
b = b + b"~" * padding
|
||||||
out = []
|
out = []
|
||||||
packI = struct.Struct('!I').pack
|
packI = struct.Struct("!I").pack
|
||||||
for i in range(0, len(b), 5):
|
for i in range(0, len(b), 5):
|
||||||
chunk = b[i : i + 5]
|
chunk = b[i : i + 5]
|
||||||
acc = 0
|
acc = 0
|
||||||
@@ -64,14 +64,14 @@ except ImportError:
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
for j, c in enumerate(iterbytes(chunk)):
|
for j, c in enumerate(iterbytes(chunk)):
|
||||||
if _b85dec[c] is None:
|
if _b85dec[c] is None:
|
||||||
raise ValueError('bad base85 character at position %d' % (i + j))
|
raise ValueError("bad base85 character at position %d" % (i + j))
|
||||||
raise
|
raise
|
||||||
try:
|
try:
|
||||||
out.append(packI(acc))
|
out.append(packI(acc))
|
||||||
except struct.error:
|
except struct.error:
|
||||||
raise ValueError('base85 overflow in hunk starting at byte %d' % i)
|
raise ValueError("base85 overflow in hunk starting at byte %d" % i)
|
||||||
|
|
||||||
result = b''.join(out)
|
result = b"".join(out)
|
||||||
if padding:
|
if padding:
|
||||||
result = result[:-padding]
|
result = result[:-padding]
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -54,5 +54,5 @@ def main():
|
|||||||
print(" -- update complete")
|
print(" -- update complete")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -36,4 +36,4 @@ PLATFORMS: Final[Set[PlatformName]] = {"linux", "macos", "windows"}
|
|||||||
|
|
||||||
|
|
||||||
def assert_never(value: NoReturn) -> NoReturn:
|
def assert_never(value: NoReturn) -> NoReturn:
|
||||||
assert False, f'Unhandled value: {value} ({type(value).__name__})' # noqa: B011
|
assert False, f"Unhandled value: {value} ({type(value).__name__})" # noqa: B011
|
||||||
|
|||||||
+39
-39
@@ -22,35 +22,35 @@ from .architecture import Architecture
|
|||||||
from .environment import ParsedEnvironment
|
from .environment import ParsedEnvironment
|
||||||
from .typing import PathOrStr, PlatformName
|
from .typing import PathOrStr, PlatformName
|
||||||
|
|
||||||
resources_dir = Path(__file__).parent / 'resources'
|
resources_dir = Path(__file__).parent / "resources"
|
||||||
|
|
||||||
get_pip_script = resources_dir / 'get-pip.py'
|
get_pip_script = resources_dir / "get-pip.py"
|
||||||
install_certifi_script = resources_dir / "install_certifi.py"
|
install_certifi_script = resources_dir / "install_certifi.py"
|
||||||
|
|
||||||
|
|
||||||
def prepare_command(command: str, **kwargs: PathOrStr) -> str:
|
def prepare_command(command: str, **kwargs: PathOrStr) -> str:
|
||||||
'''
|
"""
|
||||||
Preprocesses a command by expanding variables like {python}.
|
Preprocesses a command by expanding variables like {python}.
|
||||||
|
|
||||||
For example, used in the test_command option to specify the path to the
|
For example, used in the test_command option to specify the path to the
|
||||||
project's root.
|
project's root.
|
||||||
'''
|
"""
|
||||||
return command.format(python='python', pip='pip', **kwargs)
|
return command.format(python="python", pip="pip", **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def get_build_verbosity_extra_flags(level: int) -> List[str]:
|
def get_build_verbosity_extra_flags(level: int) -> List[str]:
|
||||||
if level > 0:
|
if level > 0:
|
||||||
return ['-' + level * 'v']
|
return ["-" + level * "v"]
|
||||||
elif level < 0:
|
elif level < 0:
|
||||||
return ['-' + -level * 'q']
|
return ["-" + -level * "q"]
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def read_python_configs(config: PlatformName) -> List[Dict[str, str]]:
|
def read_python_configs(config: PlatformName) -> List[Dict[str, str]]:
|
||||||
input_file = resources_dir / 'build-platforms.toml'
|
input_file = resources_dir / "build-platforms.toml"
|
||||||
loaded_file = toml.load(input_file)
|
loaded_file = toml.load(input_file)
|
||||||
results: List[Dict[str, str]] = list(loaded_file[config]['python_configurations'])
|
results: List[Dict[str, str]] = list(loaded_file[config]["python_configurations"])
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ class IdentifierSelector:
|
|||||||
def __call__(self, build_id: str) -> bool:
|
def __call__(self, build_id: str) -> bool:
|
||||||
# Filter build selectors by python_requires if set
|
# Filter build selectors by python_requires if set
|
||||||
if self.requires_python is not None:
|
if self.requires_python is not None:
|
||||||
py_ver_str = build_id.split('-')[0]
|
py_ver_str = build_id.split("-")[0]
|
||||||
major = int(py_ver_str[2])
|
major = int(py_ver_str[2])
|
||||||
minor = int(py_ver_str[3:])
|
minor = int(py_ver_str[3:])
|
||||||
version = Version(f"{major}.{minor}.99")
|
version = Version(f"{major}.{minor}.99")
|
||||||
@@ -124,14 +124,14 @@ class Unbuffered:
|
|||||||
|
|
||||||
|
|
||||||
def download(url: str, dest: Path) -> None:
|
def download(url: str, dest: Path) -> None:
|
||||||
print(f'+ Download {url} to {dest}')
|
print(f"+ Download {url} to {dest}")
|
||||||
dest_dir = dest.parent
|
dest_dir = dest.parent
|
||||||
if not dest_dir.exists():
|
if not dest_dir.exists():
|
||||||
dest_dir.mkdir(parents=True)
|
dest_dir.mkdir(parents=True)
|
||||||
|
|
||||||
# we've had issues when relying on the host OS' CA certificates on Windows,
|
# we've had issues when relying on the host OS' CA certificates on Windows,
|
||||||
# so we use certifi (this sounds odd but requests also does this by default)
|
# so we use certifi (this sounds odd but requests also does this by default)
|
||||||
cafile = os.environ.get('SSL_CERT_FILE', certifi.where())
|
cafile = os.environ.get("SSL_CERT_FILE", certifi.where())
|
||||||
context = ssl.create_default_context(cafile=cafile)
|
context = ssl.create_default_context(cafile=cafile)
|
||||||
repeat_num = 3
|
repeat_num = 3
|
||||||
for i in range(repeat_num):
|
for i in range(repeat_num):
|
||||||
@@ -156,15 +156,15 @@ class DependencyConstraints:
|
|||||||
self.base_file_path = base_file_path.resolve()
|
self.base_file_path = base_file_path.resolve()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def with_defaults() -> 'DependencyConstraints':
|
def with_defaults() -> "DependencyConstraints":
|
||||||
return DependencyConstraints(base_file_path=resources_dir / 'constraints.txt')
|
return DependencyConstraints(base_file_path=resources_dir / "constraints.txt")
|
||||||
|
|
||||||
def get_for_python_version(self, version: str) -> Path:
|
def get_for_python_version(self, version: str) -> Path:
|
||||||
version_parts = version.split('.')
|
version_parts = version.split(".")
|
||||||
|
|
||||||
# try to find a version-specific dependency file e.g. if
|
# try to find a version-specific dependency file e.g. if
|
||||||
# ./constraints.txt is the base, look for ./constraints-python27.txt
|
# ./constraints.txt is the base, look for ./constraints-python27.txt
|
||||||
specific_stem = self.base_file_path.stem + f'-python{version_parts[0]}{version_parts[1]}'
|
specific_stem = self.base_file_path.stem + f"-python{version_parts[0]}{version_parts[1]}"
|
||||||
specific_name = specific_stem + self.base_file_path.suffix
|
specific_name = specific_stem + self.base_file_path.suffix
|
||||||
specific_file_path = self.base_file_path.with_name(specific_name)
|
specific_file_path = self.base_file_path.with_name(specific_name)
|
||||||
if specific_file_path.exists():
|
if specific_file_path.exists():
|
||||||
@@ -173,7 +173,7 @@ class DependencyConstraints:
|
|||||||
return self.base_file_path
|
return self.base_file_path
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'{self.__class__.__name__}{self.base_file_path!r})'
|
return f"{self.__class__.__name__}{self.base_file_path!r})"
|
||||||
|
|
||||||
|
|
||||||
class BuildOptions(NamedTuple):
|
class BuildOptions(NamedTuple):
|
||||||
@@ -198,7 +198,7 @@ class BuildOptions(NamedTuple):
|
|||||||
class NonPlatformWheelError(Exception):
|
class NonPlatformWheelError(Exception):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
message = textwrap.dedent(
|
message = textwrap.dedent(
|
||||||
'''
|
"""
|
||||||
cibuildwheel: Build failed because a pure Python wheel was generated.
|
cibuildwheel: Build failed because a pure Python wheel was generated.
|
||||||
|
|
||||||
If you intend to build a pure-Python wheel, you don't need cibuildwheel - use
|
If you intend to build a pure-Python wheel, you don't need cibuildwheel - use
|
||||||
@@ -206,65 +206,65 @@ class NonPlatformWheelError(Exception):
|
|||||||
|
|
||||||
If you expected a platform wheel, check your project configuration, or run
|
If you expected a platform wheel, check your project configuration, or run
|
||||||
cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs.
|
cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs.
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
super().__init__(message)
|
super().__init__(message)
|
||||||
|
|
||||||
|
|
||||||
def strtobool(val: str) -> bool:
|
def strtobool(val: str) -> bool:
|
||||||
return val.lower() in {'y', 'yes', 't', 'true', 'on', '1'}
|
return val.lower() in {"y", "yes", "t", "true", "on", "1"}
|
||||||
|
|
||||||
|
|
||||||
class CIProvider(Enum):
|
class CIProvider(Enum):
|
||||||
travis_ci = 'travis'
|
travis_ci = "travis"
|
||||||
appveyor = 'appveyor'
|
appveyor = "appveyor"
|
||||||
circle_ci = 'circle_ci'
|
circle_ci = "circle_ci"
|
||||||
azure_pipelines = 'azure_pipelines'
|
azure_pipelines = "azure_pipelines"
|
||||||
github_actions = 'github_actions'
|
github_actions = "github_actions"
|
||||||
gitlab = 'gitlab'
|
gitlab = "gitlab"
|
||||||
other = 'other'
|
other = "other"
|
||||||
|
|
||||||
|
|
||||||
def detect_ci_provider() -> Optional[CIProvider]:
|
def detect_ci_provider() -> Optional[CIProvider]:
|
||||||
if 'TRAVIS' in os.environ:
|
if "TRAVIS" in os.environ:
|
||||||
return CIProvider.travis_ci
|
return CIProvider.travis_ci
|
||||||
elif 'APPVEYOR' in os.environ:
|
elif "APPVEYOR" in os.environ:
|
||||||
return CIProvider.appveyor
|
return CIProvider.appveyor
|
||||||
elif 'CIRCLECI' in os.environ:
|
elif "CIRCLECI" in os.environ:
|
||||||
return CIProvider.circle_ci
|
return CIProvider.circle_ci
|
||||||
elif 'AZURE_HTTP_USER_AGENT' in os.environ:
|
elif "AZURE_HTTP_USER_AGENT" in os.environ:
|
||||||
return CIProvider.azure_pipelines
|
return CIProvider.azure_pipelines
|
||||||
elif 'GITHUB_ACTIONS' in os.environ:
|
elif "GITHUB_ACTIONS" in os.environ:
|
||||||
return CIProvider.github_actions
|
return CIProvider.github_actions
|
||||||
elif 'GITLAB_CI' in os.environ:
|
elif "GITLAB_CI" in os.environ:
|
||||||
return CIProvider.gitlab
|
return CIProvider.gitlab
|
||||||
elif strtobool(os.environ.get('CI', 'false')):
|
elif strtobool(os.environ.get("CI", "false")):
|
||||||
return CIProvider.other
|
return CIProvider.other
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def unwrap(text: str) -> str:
|
def unwrap(text: str) -> str:
|
||||||
'''
|
"""
|
||||||
Unwraps multi-line text to a single line
|
Unwraps multi-line text to a single line
|
||||||
'''
|
"""
|
||||||
# remove initial line indent
|
# remove initial line indent
|
||||||
text = textwrap.dedent(text)
|
text = textwrap.dedent(text)
|
||||||
# remove leading/trailing whitespace
|
# remove leading/trailing whitespace
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
# remove consecutive whitespace
|
# remove consecutive whitespace
|
||||||
return re.sub(r'\s+', ' ', text)
|
return re.sub(r"\s+", " ", text)
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def print_new_wheels(msg: str, output_dir: Path) -> Iterator[None]:
|
def print_new_wheels(msg: str, output_dir: Path) -> Iterator[None]:
|
||||||
'''
|
"""
|
||||||
Prints the new items in a directory upon exiting. The message to display
|
Prints the new items in a directory upon exiting. The message to display
|
||||||
can include {n} for number of wheels, {s} for total number of seconds,
|
can include {n} for number of wheels, {s} for total number of seconds,
|
||||||
and/or {m} for total number of minutes. Does not print anything if this
|
and/or {m} for total number of minutes. Does not print anything if this
|
||||||
exits via exception.
|
exits via exception.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
existing_contents = set(output_dir.iterdir())
|
existing_contents = set(output_dir.iterdir())
|
||||||
|
|||||||
+87
-87
@@ -24,35 +24,35 @@ from .util import (
|
|||||||
read_python_configs,
|
read_python_configs,
|
||||||
)
|
)
|
||||||
|
|
||||||
IS_RUNNING_ON_AZURE = Path('C:\\hostedtoolcache').exists()
|
IS_RUNNING_ON_AZURE = Path("C:\\hostedtoolcache").exists()
|
||||||
|
|
||||||
|
|
||||||
def call(
|
def call(
|
||||||
args: Sequence[PathOrStr], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None
|
args: Sequence[PathOrStr], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
print('+ ' + ' '.join(str(a) for a in args))
|
print("+ " + " ".join(str(a) for a in args))
|
||||||
# we use shell=True here, even though we don't need a shell due to a bug
|
# we use shell=True here, even though we don't need a shell due to a bug
|
||||||
# https://bugs.python.org/issue8557
|
# https://bugs.python.org/issue8557
|
||||||
subprocess.run([str(a) for a in args], env=env, cwd=cwd, shell=True, check=True)
|
subprocess.run([str(a) for a in args], env=env, cwd=cwd, shell=True, check=True)
|
||||||
|
|
||||||
|
|
||||||
def shell(command: str, env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None) -> None:
|
def shell(command: str, env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None) -> None:
|
||||||
print(f'+ {command}')
|
print(f"+ {command}")
|
||||||
subprocess.run(command, env=env, cwd=cwd, shell=True, check=True)
|
subprocess.run(command, env=env, cwd=cwd, shell=True, check=True)
|
||||||
|
|
||||||
|
|
||||||
def get_nuget_args(version: str, arch: str) -> List[str]:
|
def get_nuget_args(version: str, arch: str) -> List[str]:
|
||||||
python_name = 'python' if version[0] == '3' else 'python2'
|
python_name = "python" if version[0] == "3" else "python2"
|
||||||
if arch == '32':
|
if arch == "32":
|
||||||
python_name += 'x86'
|
python_name += "x86"
|
||||||
return [
|
return [
|
||||||
python_name,
|
python_name,
|
||||||
'-Version',
|
"-Version",
|
||||||
version,
|
version,
|
||||||
'-FallbackSource',
|
"-FallbackSource",
|
||||||
'https://api.nuget.org/v3/index.json',
|
"https://api.nuget.org/v3/index.json",
|
||||||
'-OutputDirectory',
|
"-OutputDirectory",
|
||||||
'C:\\cibw\\python',
|
"C:\\cibw\\python",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -68,20 +68,20 @@ def get_python_configurations(
|
|||||||
architectures: Set[Architecture],
|
architectures: Set[Architecture],
|
||||||
) -> List[PythonConfiguration]:
|
) -> List[PythonConfiguration]:
|
||||||
|
|
||||||
full_python_configs = read_python_configs('windows')
|
full_python_configs = read_python_configs("windows")
|
||||||
|
|
||||||
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
|
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
|
||||||
|
|
||||||
map_arch = {
|
map_arch = {
|
||||||
'32': Architecture.x86,
|
"32": Architecture.x86,
|
||||||
'64': Architecture.AMD64,
|
"64": Architecture.AMD64,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Only supported with custom compiler, since MS removed the 2008 compiler download
|
# Only supported with custom compiler, since MS removed the 2008 compiler download
|
||||||
custom_compiler = os.environ.get('DISTUTILS_USE_SDK') and os.environ.get('MSSdk')
|
custom_compiler = os.environ.get("DISTUTILS_USE_SDK") and os.environ.get("MSSdk")
|
||||||
if not custom_compiler:
|
if not custom_compiler:
|
||||||
python_configurations = [
|
python_configurations = [
|
||||||
c for c in python_configurations if not c.version.startswith('2.7')
|
c for c in python_configurations if not c.version.startswith("2.7")
|
||||||
]
|
]
|
||||||
|
|
||||||
# skip builds as required
|
# skip builds as required
|
||||||
@@ -101,25 +101,25 @@ def extract_zip(zip_src: Path, dest: Path) -> None:
|
|||||||
|
|
||||||
def install_cpython(version: str, arch: str, nuget: Path) -> Path:
|
def install_cpython(version: str, arch: str, nuget: Path) -> Path:
|
||||||
nuget_args = get_nuget_args(version, arch)
|
nuget_args = get_nuget_args(version, arch)
|
||||||
installation_path = Path(nuget_args[-1]) / (nuget_args[0] + '.' + version) / 'tools'
|
installation_path = Path(nuget_args[-1]) / (nuget_args[0] + "." + version) / "tools"
|
||||||
call([nuget, 'install', *nuget_args])
|
call([nuget, "install", *nuget_args])
|
||||||
return installation_path
|
return installation_path
|
||||||
|
|
||||||
|
|
||||||
def install_pypy(version: str, arch: str, url: str) -> Path:
|
def install_pypy(version: str, arch: str, url: str) -> Path:
|
||||||
assert arch == '32'
|
assert arch == "32"
|
||||||
# Inside the PyPy zip file is a directory with the same name
|
# Inside the PyPy zip file is a directory with the same name
|
||||||
zip_filename = url.rsplit('/', 1)[-1]
|
zip_filename = url.rsplit("/", 1)[-1]
|
||||||
extension = ".zip"
|
extension = ".zip"
|
||||||
assert zip_filename.endswith(extension)
|
assert zip_filename.endswith(extension)
|
||||||
installation_path = Path('C:\\cibw') / zip_filename[: -len(extension)]
|
installation_path = Path("C:\\cibw") / zip_filename[: -len(extension)]
|
||||||
if not installation_path.exists():
|
if not installation_path.exists():
|
||||||
pypy_zip = Path('C:\\cibw') / zip_filename
|
pypy_zip = Path("C:\\cibw") / zip_filename
|
||||||
download(url, pypy_zip)
|
download(url, pypy_zip)
|
||||||
# Extract to the parent directory because the zip file still contains a directory
|
# Extract to the parent directory because the zip file still contains a directory
|
||||||
extract_zip(pypy_zip, installation_path.parent)
|
extract_zip(pypy_zip, installation_path.parent)
|
||||||
pypy_exe = 'pypy3.exe' if version[0] == '3' else 'pypy.exe'
|
pypy_exe = "pypy3.exe" if version[0] == "3" else "pypy.exe"
|
||||||
(installation_path / 'python.exe').symlink_to(installation_path / pypy_exe)
|
(installation_path / "python.exe").symlink_to(installation_path / pypy_exe)
|
||||||
return installation_path
|
return installation_path
|
||||||
|
|
||||||
|
|
||||||
@@ -128,19 +128,19 @@ def setup_python(
|
|||||||
dependency_constraint_flags: Sequence[PathOrStr],
|
dependency_constraint_flags: Sequence[PathOrStr],
|
||||||
environment: ParsedEnvironment,
|
environment: ParsedEnvironment,
|
||||||
) -> Dict[str, str]:
|
) -> Dict[str, str]:
|
||||||
nuget = Path('C:\\cibw\\nuget.exe')
|
nuget = Path("C:\\cibw\\nuget.exe")
|
||||||
if not nuget.exists():
|
if not nuget.exists():
|
||||||
log.step('Downloading nuget...')
|
log.step("Downloading nuget...")
|
||||||
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
|
download("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", nuget)
|
||||||
|
|
||||||
implementation_id = python_configuration.identifier.split("-")[0]
|
implementation_id = python_configuration.identifier.split("-")[0]
|
||||||
log.step(f'Installing Python {implementation_id}...')
|
log.step(f"Installing Python {implementation_id}...")
|
||||||
|
|
||||||
if implementation_id.startswith('cp'):
|
if implementation_id.startswith("cp"):
|
||||||
installation_path = install_cpython(
|
installation_path = install_cpython(
|
||||||
python_configuration.version, python_configuration.arch, nuget
|
python_configuration.version, python_configuration.arch, nuget
|
||||||
)
|
)
|
||||||
elif implementation_id.startswith('pp'):
|
elif implementation_id.startswith("pp"):
|
||||||
assert python_configuration.url is not None
|
assert python_configuration.url is not None
|
||||||
installation_path = install_pypy(
|
installation_path = install_pypy(
|
||||||
python_configuration.version, python_configuration.arch, python_configuration.url
|
python_configuration.version, python_configuration.arch, python_configuration.url
|
||||||
@@ -148,29 +148,29 @@ def setup_python(
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Unknown Python implementation")
|
raise ValueError("Unknown Python implementation")
|
||||||
|
|
||||||
assert (installation_path / 'python.exe').exists()
|
assert (installation_path / "python.exe").exists()
|
||||||
|
|
||||||
log.step('Setting up build environment...')
|
log.step("Setting up build environment...")
|
||||||
|
|
||||||
# set up PATH and environment variables for run_with_env
|
# set up PATH and environment variables for run_with_env
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env['PYTHON_VERSION'] = python_configuration.version
|
env["PYTHON_VERSION"] = python_configuration.version
|
||||||
env['PYTHON_ARCH'] = python_configuration.arch
|
env["PYTHON_ARCH"] = python_configuration.arch
|
||||||
env['PATH'] = os.pathsep.join(
|
env["PATH"] = os.pathsep.join(
|
||||||
[str(installation_path), str(installation_path / 'Scripts'), env['PATH']]
|
[str(installation_path), str(installation_path / "Scripts"), env["PATH"]]
|
||||||
)
|
)
|
||||||
env['PIP_DISABLE_PIP_VERSION_CHECK'] = '1'
|
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
|
||||||
|
|
||||||
# update env with results from CIBW_ENVIRONMENT
|
# update env with results from CIBW_ENVIRONMENT
|
||||||
env = environment.as_dictionary(prev_environment=env)
|
env = environment.as_dictionary(prev_environment=env)
|
||||||
|
|
||||||
# for the logs - check we're running the right version of python
|
# for the logs - check we're running the right version of python
|
||||||
call(['where', 'python'], env=env)
|
call(["where", "python"], env=env)
|
||||||
call(['python', '--version'], env=env)
|
call(["python", "--version"], env=env)
|
||||||
call(['python', '-c', '"import struct; print(struct.calcsize(\'P\') * 8)"'], env=env)
|
call(["python", "-c", "\"import struct; print(struct.calcsize('P') * 8)\""], env=env)
|
||||||
where_python = (
|
where_python = (
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
['where', 'python'],
|
["where", "python"],
|
||||||
env=env,
|
env=env,
|
||||||
universal_newlines=True,
|
universal_newlines=True,
|
||||||
check=True,
|
check=True,
|
||||||
@@ -179,7 +179,7 @@ def setup_python(
|
|||||||
.stdout.splitlines()[0]
|
.stdout.splitlines()[0]
|
||||||
.strip()
|
.strip()
|
||||||
)
|
)
|
||||||
if where_python != str(installation_path / 'python.exe'):
|
if where_python != str(installation_path / "python.exe"):
|
||||||
print(
|
print(
|
||||||
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it.",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
@@ -187,32 +187,32 @@ def setup_python(
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# make sure pip is installed
|
# make sure pip is installed
|
||||||
if not (installation_path / 'Scripts' / 'pip.exe').exists():
|
if not (installation_path / "Scripts" / "pip.exe").exists():
|
||||||
call(['python', get_pip_script, *dependency_constraint_flags], env=env, cwd="C:\\cibw")
|
call(["python", get_pip_script, *dependency_constraint_flags], env=env, cwd="C:\\cibw")
|
||||||
assert (installation_path / 'Scripts' / 'pip.exe').exists()
|
assert (installation_path / "Scripts" / "pip.exe").exists()
|
||||||
where_pip = (
|
where_pip = (
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
['where', 'pip'], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE
|
["where", "pip"], env=env, universal_newlines=True, check=True, stdout=subprocess.PIPE
|
||||||
)
|
)
|
||||||
.stdout.splitlines()[0]
|
.stdout.splitlines()[0]
|
||||||
.strip()
|
.strip()
|
||||||
)
|
)
|
||||||
if where_pip.strip() != str(installation_path / 'Scripts' / 'pip.exe'):
|
if where_pip.strip() != str(installation_path / "Scripts" / "pip.exe"):
|
||||||
print(
|
print(
|
||||||
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it.",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
log.step('Installing build tools...')
|
log.step("Installing build tools...")
|
||||||
|
|
||||||
call(
|
call(
|
||||||
['python', '-m', 'pip', 'install', '--upgrade', 'pip', *dependency_constraint_flags],
|
["python", "-m", "pip", "install", "--upgrade", "pip", *dependency_constraint_flags],
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
call(['pip', '--version'], env=env)
|
call(["pip", "--version"], env=env)
|
||||||
call(
|
call(
|
||||||
['pip', 'install', '--upgrade', 'setuptools', 'wheel', *dependency_constraint_flags],
|
["pip", "install", "--upgrade", "setuptools", "wheel", *dependency_constraint_flags],
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -231,33 +231,33 @@ def pep_518_cp35_workaround(package_dir: Path, env: Dict[str, str]) -> None:
|
|||||||
mostly "isolated".
|
mostly "isolated".
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pyproject_path = package_dir / 'pyproject.toml'
|
pyproject_path = package_dir / "pyproject.toml"
|
||||||
|
|
||||||
if pyproject_path.exists():
|
if pyproject_path.exists():
|
||||||
data = toml.load(pyproject_path)
|
data = toml.load(pyproject_path)
|
||||||
requirements = data['build-system'].get('requires', []) if 'build-system' in data else []
|
requirements = data["build-system"].get("requires", []) if "build-system" in data else []
|
||||||
|
|
||||||
if requirements:
|
if requirements:
|
||||||
log.step('Performing PEP518 workaround...')
|
log.step("Performing PEP518 workaround...")
|
||||||
with tempfile.TemporaryDirectory() as d:
|
with tempfile.TemporaryDirectory() as d:
|
||||||
reqfile = Path(d) / "requirements.txt"
|
reqfile = Path(d) / "requirements.txt"
|
||||||
with reqfile.open('w') as f:
|
with reqfile.open("w") as f:
|
||||||
for r in requirements:
|
for r in requirements:
|
||||||
print(r, file=f)
|
print(r, file=f)
|
||||||
call(['pip', 'install', '-r', reqfile], env=env)
|
call(["pip", "install", "-r", reqfile], env=env)
|
||||||
|
|
||||||
|
|
||||||
def build(options: BuildOptions) -> None:
|
def build(options: BuildOptions) -> None:
|
||||||
temp_dir = Path(tempfile.mkdtemp(prefix='cibuildwheel'))
|
temp_dir = Path(tempfile.mkdtemp(prefix="cibuildwheel"))
|
||||||
built_wheel_dir = temp_dir / 'built_wheel'
|
built_wheel_dir = temp_dir / "built_wheel"
|
||||||
repaired_wheel_dir = temp_dir / 'repaired_wheel'
|
repaired_wheel_dir = temp_dir / "repaired_wheel"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if options.before_all:
|
if options.before_all:
|
||||||
log.step('Running before_all...')
|
log.step("Running before_all...")
|
||||||
env = options.environment.as_dictionary(prev_environment=os.environ)
|
env = options.environment.as_dictionary(prev_environment=os.environ)
|
||||||
before_all_prepared = prepare_command(
|
before_all_prepared = prepare_command(
|
||||||
options.before_all, project='.', package=options.package_dir
|
options.before_all, project=".", package=options.package_dir
|
||||||
)
|
)
|
||||||
shell(before_all_prepared, env=env)
|
shell(before_all_prepared, env=env)
|
||||||
|
|
||||||
@@ -271,7 +271,7 @@ def build(options: BuildOptions) -> None:
|
|||||||
dependency_constraint_flags: Sequence[PathOrStr] = []
|
dependency_constraint_flags: Sequence[PathOrStr] = []
|
||||||
if options.dependency_constraints:
|
if options.dependency_constraints:
|
||||||
dependency_constraint_flags = [
|
dependency_constraint_flags = [
|
||||||
'-c',
|
"-c",
|
||||||
options.dependency_constraints.get_for_python_version(config.version),
|
options.dependency_constraints.get_for_python_version(config.version),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -280,18 +280,18 @@ def build(options: BuildOptions) -> None:
|
|||||||
|
|
||||||
# run the before_build command
|
# run the before_build command
|
||||||
if options.before_build:
|
if options.before_build:
|
||||||
log.step('Running before_build...')
|
log.step("Running before_build...")
|
||||||
before_build_prepared = prepare_command(
|
before_build_prepared = prepare_command(
|
||||||
options.before_build, project='.', package=options.package_dir
|
options.before_build, project=".", package=options.package_dir
|
||||||
)
|
)
|
||||||
shell(before_build_prepared, env=env)
|
shell(before_build_prepared, env=env)
|
||||||
|
|
||||||
# activate the PEP 518 patch if on Windows Python 3.5
|
# activate the PEP 518 patch if on Windows Python 3.5
|
||||||
# (will only have an effect if PEP 517 builds are used):
|
# (will only have an effect if PEP 517 builds are used):
|
||||||
if config.version.startswith('3.5'):
|
if config.version.startswith("3.5"):
|
||||||
pep_518_cp35_workaround(options.package_dir, env)
|
pep_518_cp35_workaround(options.package_dir, env)
|
||||||
|
|
||||||
log.step('Building wheel...')
|
log.step("Building wheel...")
|
||||||
if built_wheel_dir.exists():
|
if built_wheel_dir.exists():
|
||||||
shutil.rmtree(built_wheel_dir)
|
shutil.rmtree(built_wheel_dir)
|
||||||
built_wheel_dir.mkdir(parents=True)
|
built_wheel_dir.mkdir(parents=True)
|
||||||
@@ -299,29 +299,29 @@ def build(options: BuildOptions) -> None:
|
|||||||
# see https://github.com/joerick/cibuildwheel/pull/369
|
# see https://github.com/joerick/cibuildwheel/pull/369
|
||||||
call(
|
call(
|
||||||
[
|
[
|
||||||
'pip',
|
"pip",
|
||||||
'wheel',
|
"wheel",
|
||||||
options.package_dir.resolve(),
|
options.package_dir.resolve(),
|
||||||
'-w',
|
"-w",
|
||||||
built_wheel_dir,
|
built_wheel_dir,
|
||||||
'--no-deps',
|
"--no-deps",
|
||||||
*get_build_verbosity_extra_flags(options.build_verbosity),
|
*get_build_verbosity_extra_flags(options.build_verbosity),
|
||||||
],
|
],
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
|
|
||||||
built_wheel = next(built_wheel_dir.glob('*.whl'))
|
built_wheel = next(built_wheel_dir.glob("*.whl"))
|
||||||
|
|
||||||
# repair the wheel
|
# repair the wheel
|
||||||
if repaired_wheel_dir.exists():
|
if repaired_wheel_dir.exists():
|
||||||
shutil.rmtree(repaired_wheel_dir)
|
shutil.rmtree(repaired_wheel_dir)
|
||||||
repaired_wheel_dir.mkdir(parents=True)
|
repaired_wheel_dir.mkdir(parents=True)
|
||||||
|
|
||||||
if built_wheel.name.endswith('none-any.whl'):
|
if built_wheel.name.endswith("none-any.whl"):
|
||||||
raise NonPlatformWheelError()
|
raise NonPlatformWheelError()
|
||||||
|
|
||||||
if options.repair_command:
|
if options.repair_command:
|
||||||
log.step('Repairing wheel...')
|
log.step("Repairing wheel...")
|
||||||
repair_command_prepared = prepare_command(
|
repair_command_prepared = prepare_command(
|
||||||
options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
|
options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
|
||||||
)
|
)
|
||||||
@@ -329,57 +329,57 @@ def build(options: BuildOptions) -> None:
|
|||||||
else:
|
else:
|
||||||
shutil.move(str(built_wheel), repaired_wheel_dir)
|
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||||
|
|
||||||
repaired_wheel = next(repaired_wheel_dir.glob('*.whl'))
|
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
||||||
|
|
||||||
if options.test_command and options.test_selector(config.identifier):
|
if options.test_command and options.test_selector(config.identifier):
|
||||||
log.step('Testing wheel...')
|
log.step("Testing wheel...")
|
||||||
# set up a virtual environment to install and test from, to make sure
|
# set up a virtual environment to install and test from, to make sure
|
||||||
# there are no dependencies that were pulled in at build time.
|
# there are no dependencies that were pulled in at build time.
|
||||||
call(['pip', 'install', 'virtualenv', *dependency_constraint_flags], env=env)
|
call(["pip", "install", "virtualenv", *dependency_constraint_flags], env=env)
|
||||||
venv_dir = Path(tempfile.mkdtemp())
|
venv_dir = Path(tempfile.mkdtemp())
|
||||||
|
|
||||||
# Use --no-download to ensure determinism by using seed libraries
|
# Use --no-download to ensure determinism by using seed libraries
|
||||||
# built into virtualenv
|
# built into virtualenv
|
||||||
call(['python', '-m', 'virtualenv', '--no-download', venv_dir], env=env)
|
call(["python", "-m", "virtualenv", "--no-download", venv_dir], env=env)
|
||||||
|
|
||||||
virtualenv_env = env.copy()
|
virtualenv_env = env.copy()
|
||||||
virtualenv_env['PATH'] = os.pathsep.join(
|
virtualenv_env["PATH"] = os.pathsep.join(
|
||||||
[
|
[
|
||||||
str(venv_dir / 'Scripts'),
|
str(venv_dir / "Scripts"),
|
||||||
virtualenv_env['PATH'],
|
virtualenv_env["PATH"],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that we are using the Python from the virtual environment
|
# check that we are using the Python from the virtual environment
|
||||||
call(['where', 'python'], env=virtualenv_env)
|
call(["where", "python"], env=virtualenv_env)
|
||||||
|
|
||||||
if options.before_test:
|
if options.before_test:
|
||||||
before_test_prepared = prepare_command(
|
before_test_prepared = prepare_command(
|
||||||
options.before_test,
|
options.before_test,
|
||||||
project='.',
|
project=".",
|
||||||
package=options.package_dir,
|
package=options.package_dir,
|
||||||
)
|
)
|
||||||
shell(before_test_prepared, env=virtualenv_env)
|
shell(before_test_prepared, env=virtualenv_env)
|
||||||
|
|
||||||
# install the wheel
|
# install the wheel
|
||||||
call(
|
call(
|
||||||
['pip', 'install', str(repaired_wheel) + options.test_extras],
|
["pip", "install", str(repaired_wheel) + options.test_extras],
|
||||||
env=virtualenv_env,
|
env=virtualenv_env,
|
||||||
)
|
)
|
||||||
|
|
||||||
# test the wheel
|
# test the wheel
|
||||||
if options.test_requires:
|
if options.test_requires:
|
||||||
call(['pip', 'install'] + options.test_requires, env=virtualenv_env)
|
call(["pip", "install"] + options.test_requires, env=virtualenv_env)
|
||||||
|
|
||||||
# run the tests from c:\, with an absolute path in the command
|
# run the tests from c:\, with an absolute path in the command
|
||||||
# (this ensures that Python runs the tests against the installed wheel
|
# (this ensures that Python runs the tests against the installed wheel
|
||||||
# and not the repo code)
|
# and not the repo code)
|
||||||
test_command_prepared = prepare_command(
|
test_command_prepared = prepare_command(
|
||||||
options.test_command,
|
options.test_command,
|
||||||
project=Path('.').resolve(),
|
project=Path(".").resolve(),
|
||||||
package=options.package_dir.resolve(),
|
package=options.package_dir.resolve(),
|
||||||
)
|
)
|
||||||
shell(test_command_prepared, cwd='c:\\', env=virtualenv_env)
|
shell(test_command_prepared, cwd="c:\\", env=virtualenv_env)
|
||||||
|
|
||||||
# clean up
|
# clean up
|
||||||
shutil.rmtree(venv_dir)
|
shutil.rmtree(venv_dir)
|
||||||
@@ -389,6 +389,6 @@ def build(options: BuildOptions) -> None:
|
|||||||
log.build_end()
|
log.build_end()
|
||||||
except subprocess.CalledProcessError as error:
|
except subprocess.CalledProcessError as error:
|
||||||
log.step_end_with_error(
|
log.step_end_with_error(
|
||||||
f'Command {error.cmd} failed with code {error.returncode}. {error.stdout}'
|
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -9,4 +9,3 @@ build-backend = "setuptools.build_meta"
|
|||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 100
|
line-length = 100
|
||||||
target-version = ['py36', 'py37', 'py38', 'py39']
|
target-version = ['py36', 'py37', 'py38', 'py39']
|
||||||
skip-string-normalization = true
|
|
||||||
|
|||||||
+12
-12
@@ -9,38 +9,38 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
basic_project = test_projects.new_c_project(
|
basic_project = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
'''
|
"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
if os.environ.get("CIBUILDWHEEL", "0") != "1":
|
if os.environ.get("CIBUILDWHEEL", "0") != "1":
|
||||||
raise Exception("CIBUILDWHEEL environment variable is not set to 1")
|
raise Exception("CIBUILDWHEEL environment variable is not set to 1")
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
basic_project.generate(project_dir)
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason='to keep test output clean')
|
@pytest.mark.skip(reason="to keep test output clean")
|
||||||
def test_sample_build(tmp_path, capfd):
|
def test_sample_build(tmp_path, capfd):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
basic_project.generate(project_dir)
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
# build the wheels, and let the output passthrough to the caller, so
|
# build the wheels, and let the output passthrough to the caller, so
|
||||||
# we can see how it looks
|
# we can see how it looks
|
||||||
with capfd.disabled():
|
with capfd.disabled():
|
||||||
logger = Logger()
|
logger = Logger()
|
||||||
logger.step('test_sample_build')
|
logger.step("test_sample_build")
|
||||||
try:
|
try:
|
||||||
utils.cibuildwheel_run(project_dir)
|
utils.cibuildwheel_run(project_dir)
|
||||||
finally:
|
finally:
|
||||||
@@ -48,7 +48,7 @@ def test_sample_build(tmp_path, capfd):
|
|||||||
|
|
||||||
|
|
||||||
def test_build_identifiers(tmp_path):
|
def test_build_identifiers(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
basic_project.generate(project_dir)
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
# check that the number of expected wheels matches the number of build
|
# check that the number of expected wheels matches the number of build
|
||||||
@@ -56,13 +56,13 @@ def test_build_identifiers(tmp_path):
|
|||||||
# after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there
|
# after adding CIBW_MANYLINUX_IMAGE to support manylinux2010, there
|
||||||
# can be multiple wheels for each wheel, though, so we need to limit
|
# can be multiple wheels for each wheel, though, so we need to limit
|
||||||
# the expected wheels
|
# the expected wheels
|
||||||
if platform.machine() in ['x86_64', 'i686']:
|
if platform.machine() in ["x86_64", "i686"]:
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w
|
w
|
||||||
for w in utils.expected_wheels('spam', '0.1.0')
|
for w in utils.expected_wheels("spam", "0.1.0")
|
||||||
if '-manylinux' not in w or '-manylinux1' in w
|
if "-manylinux" not in w or "-manylinux1" in w
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir)
|
build_identifiers = utils.cibuildwheel_get_build_identifiers(project_dir)
|
||||||
assert len(expected_wheels) == len(build_identifiers)
|
assert len(expected_wheels) == len(build_identifiers)
|
||||||
|
|||||||
+16
-16
@@ -7,7 +7,7 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
project_with_before_build_asserts = test_projects.new_c_project(
|
project_with_before_build_asserts = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
# assert that the Python version as written to text_info.txt in the CIBW_BEFORE_ALL step
|
# assert that the Python version as written to text_info.txt in the CIBW_BEFORE_ALL step
|
||||||
# is the same one as is currently running.
|
# is the same one as is currently running.
|
||||||
with open("text_info.txt") as f:
|
with open("text_info.txt") as f:
|
||||||
@@ -15,16 +15,16 @@ project_with_before_build_asserts = test_projects.new_c_project(
|
|||||||
|
|
||||||
print("## stored text: " + stored_text)
|
print("## stored text: " + stored_text)
|
||||||
assert stored_text == "sample text 123"
|
assert stored_text == "sample text 123"
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_before_build_asserts.generate(project_dir)
|
project_with_before_build_asserts.generate(project_dir)
|
||||||
|
|
||||||
with (project_dir / 'text_info.txt').open(mode='w') as ff:
|
with (project_dir / "text_info.txt").open(mode="w") as ff:
|
||||||
print("dummy text", file=ff)
|
print("dummy text", file=ff)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
@@ -34,43 +34,43 @@ def test(tmp_path):
|
|||||||
add_env={
|
add_env={
|
||||||
# write python version information to a temporary file, this is
|
# write python version information to a temporary file, this is
|
||||||
# checked in setup.py
|
# checked in setup.py
|
||||||
'CIBW_BEFORE_ALL': before_all_command,
|
"CIBW_BEFORE_ALL": before_all_command,
|
||||||
'CIBW_BEFORE_ALL_LINUX': f'{before_all_command} && python -c "import sys; assert sys.version_info >= (3, 6)"',
|
"CIBW_BEFORE_ALL_LINUX": f'{before_all_command} && python -c "import sys; assert sys.version_info >= (3, 6)"',
|
||||||
'CIBW_ENVIRONMENT': "TEST_VAL='123'",
|
"CIBW_ENVIRONMENT": "TEST_VAL='123'",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
(project_dir / 'text_info.txt').unlink()
|
(project_dir / "text_info.txt").unlink()
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
def test_failing_command(tmp_path):
|
def test_failing_command(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
test_projects.new_c_project().generate(project_dir)
|
test_projects.new_c_project().generate(project_dir)
|
||||||
|
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
utils.cibuildwheel_run(
|
utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BEFORE_ALL': 'false',
|
"CIBW_BEFORE_ALL": "false",
|
||||||
'CIBW_BEFORE_ALL_WINDOWS': 'exit /b 1',
|
"CIBW_BEFORE_ALL_WINDOWS": "exit /b 1",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_cwd(tmp_path):
|
def test_cwd(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
test_projects.new_c_project().generate(project_dir)
|
test_projects.new_c_project().generate(project_dir)
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BEFORE_ALL': f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
"CIBW_BEFORE_ALL": f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
||||||
'CIBW_BEFORE_ALL_LINUX': '''python -c "import os; assert os.getcwd() == '/project'"''',
|
"CIBW_BEFORE_ALL_LINUX": '''python -c "import os; assert os.getcwd() == '/project'"''',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
+14
-14
@@ -7,7 +7,7 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
project_with_before_build_asserts = test_projects.new_c_project(
|
project_with_before_build_asserts = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_BUILD step
|
# assert that the Python version as written to pythonversion.txt in the CIBW_BEFORE_BUILD step
|
||||||
@@ -27,17 +27,17 @@ project_with_before_build_asserts = test_projects.new_c_project(
|
|||||||
print('sys.executable', sys.executable)
|
print('sys.executable', sys.executable)
|
||||||
# windows/mac are case insensitive
|
# windows/mac are case insensitive
|
||||||
assert os.path.realpath(stored_executable).lower() == os.path.realpath(sys.executable).lower()
|
assert os.path.realpath(stored_executable).lower() == os.path.realpath(sys.executable).lower()
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_before_build_asserts.generate(project_dir)
|
project_with_before_build_asserts.generate(project_dir)
|
||||||
|
|
||||||
before_build = (
|
before_build = (
|
||||||
'''python -c "import sys; open('{output_dir}pythonversion.txt', 'w').write(sys.version)" && '''
|
"""python -c "import sys; open('{output_dir}pythonversion.txt', 'w').write(sys.version)" && """
|
||||||
'''python -c "import sys; open('{output_dir}pythonexecutable.txt', 'w').write(sys.executable)"'''
|
'''python -c "import sys; open('{output_dir}pythonexecutable.txt', 'w').write(sys.executable)"'''
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,41 +47,41 @@ def test(tmp_path):
|
|||||||
add_env={
|
add_env={
|
||||||
# write python version information to a temporary file, this is
|
# write python version information to a temporary file, this is
|
||||||
# checked in setup.py
|
# checked in setup.py
|
||||||
'CIBW_BEFORE_BUILD': before_build.format(output_dir='/tmp/'),
|
"CIBW_BEFORE_BUILD": before_build.format(output_dir="/tmp/"),
|
||||||
'CIBW_BEFORE_BUILD_WINDOWS': before_build.format(output_dir=r'c:\\'),
|
"CIBW_BEFORE_BUILD_WINDOWS": before_build.format(output_dir=r"c:\\"),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
def test_failing_command(tmp_path):
|
def test_failing_command(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
test_projects.new_c_project().generate(project_dir)
|
test_projects.new_c_project().generate(project_dir)
|
||||||
|
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
utils.cibuildwheel_run(
|
utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BEFORE_BUILD': 'false',
|
"CIBW_BEFORE_BUILD": "false",
|
||||||
'CIBW_BEFORE_BUILD_WINDOWS': 'exit /b 1',
|
"CIBW_BEFORE_BUILD_WINDOWS": "exit /b 1",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_cwd(tmp_path):
|
def test_cwd(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
test_projects.new_c_project().generate(project_dir)
|
test_projects.new_c_project().generate(project_dir)
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BEFORE_BUILD': f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
"CIBW_BEFORE_BUILD": f'''python -c "import os; assert os.getcwd() == {str(project_dir)!r}"''',
|
||||||
'CIBW_BEFORE_BUILD_LINUX': '''python -c "import os; assert os.getcwd() == '/project'"''',
|
"CIBW_BEFORE_BUILD_LINUX": '''python -c "import os; assert os.getcwd() == '/project'"''',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
+11
-11
@@ -2,8 +2,8 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
before_test_project = test_projects.new_c_project()
|
before_test_project = test_projects.new_c_project()
|
||||||
before_test_project.files[
|
before_test_project.files[
|
||||||
'test/spam_test.py'
|
"test/spam_test.py"
|
||||||
] = r'''
|
] = r"""
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
@@ -32,13 +32,13 @@ class TestBeforeTest(TestCase):
|
|||||||
# vssadm~1 instead of vssadministrator
|
# vssadm~1 instead of vssadministrator
|
||||||
|
|
||||||
assert os.stat(stored_prefix) == os.stat(sys.prefix)
|
assert os.stat(stored_prefix) == os.stat(sys.prefix)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
before_test_project.generate(project_dir)
|
before_test_project.generate(project_dir)
|
||||||
test_project_dir = project_dir / 'dependency'
|
test_project_dir = project_dir / "dependency"
|
||||||
test_projects.new_c_project().generate(test_project_dir)
|
test_projects.new_c_project().generate(test_project_dir)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
@@ -47,16 +47,16 @@ def test(tmp_path):
|
|||||||
add_env={
|
add_env={
|
||||||
# write python version information to a temporary file, this is
|
# write python version information to a temporary file, this is
|
||||||
# checked in setup.py
|
# checked in setup.py
|
||||||
'CIBW_BEFORE_TEST': '''python -c "import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonprefix.txt', 'w').write(sys.prefix)" && python -m pip install {project}/dependency''',
|
"CIBW_BEFORE_TEST": """python -c "import sys; open('/tmp/pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('/tmp/pythonprefix.txt', 'w').write(sys.prefix)" && python -m pip install {project}/dependency""",
|
||||||
'CIBW_BEFORE_TEST_WINDOWS': '''python -c "import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonprefix.txt', 'w').write(sys.prefix)" && python -m pip install {project}/dependency''',
|
"CIBW_BEFORE_TEST_WINDOWS": """python -c "import sys; open('c:\\pythonversion.txt', 'w').write(sys.version)" && python -c "import sys; open('c:\\pythonprefix.txt', 'w').write(sys.prefix)" && python -m pip install {project}/dependency""",
|
||||||
'CIBW_TEST_REQUIRES': 'nose',
|
"CIBW_TEST_REQUIRES": "nose",
|
||||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||||
# mac/linux.
|
# mac/linux.
|
||||||
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
"CIBW_TEST_COMMAND": "false || nosetests {project}/test",
|
||||||
'CIBW_TEST_COMMAND_WINDOWS': 'nosetests {project}/test',
|
"CIBW_TEST_COMMAND_WINDOWS": "nosetests {project}/test",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
@@ -4,32 +4,32 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
project_with_skip_asserts = test_projects.new_c_project(
|
project_with_skip_asserts = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
# explode if run on Python 2.7 or Python 3.7 (these should be skipped)
|
# explode if run on Python 2.7 or Python 3.7 (these should be skipped)
|
||||||
if sys.version_info[0:2] == (2, 7):
|
if sys.version_info[0:2] == (2, 7):
|
||||||
raise Exception("Python 2.7 should not be built")
|
raise Exception("Python 2.7 should not be built")
|
||||||
if sys.version_info[0:2] == (3, 7):
|
if sys.version_info[0:2] == (3, 7):
|
||||||
raise Exception("Python 3.7 should be skipped")
|
raise Exception("Python 3.7 should be skipped")
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_skip_asserts.generate(project_dir)
|
project_with_skip_asserts.generate(project_dir)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BUILD': 'cp3?-*',
|
"CIBW_BUILD": "cp3?-*",
|
||||||
'CIBW_SKIP': 'cp37-*',
|
"CIBW_SKIP": "cp37-*",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that we got the right wheels. There should be no 2.7 or 3.7.
|
# check that we got the right wheels. There should be no 2.7 or 3.7.
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w for w in utils.expected_wheels('spam', '0.1.0') if ('-cp3' in w) and ('-cp37' not in w)
|
w for w in utils.expected_wheels("spam", "0.1.0") if ("-cp3" in w) and ("-cp37" not in w)
|
||||||
]
|
]
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
+46
-46
@@ -8,7 +8,7 @@ from .test_projects import TestProject
|
|||||||
|
|
||||||
cpp_test_project = TestProject()
|
cpp_test_project = TestProject()
|
||||||
|
|
||||||
setup_py_template = r'''
|
setup_py_template = r"""
|
||||||
from setuptools import Extension, setup
|
from setuptools import Extension, setup
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
@@ -16,9 +16,9 @@ setup(
|
|||||||
ext_modules=[Extension('spam', sources=['spam.cpp'], language="c++", extra_compile_args={{ extra_compile_args }})],
|
ext_modules=[Extension('spam', sources=['spam.cpp'], language="c++", extra_compile_args={{ extra_compile_args }})],
|
||||||
version="0.1.0",
|
version="0.1.0",
|
||||||
)
|
)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
spam_cpp_template = r'''
|
spam_cpp_template = r"""
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
|
||||||
{{ spam_cpp_top_level_add }}
|
{{ spam_cpp_top_level_add }}
|
||||||
@@ -69,59 +69,59 @@ MOD_INIT(spam)
|
|||||||
|
|
||||||
MOD_RETURN(m)
|
MOD_RETURN(m)
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
|
|
||||||
cpp_test_project.files['setup.py'] = jinja2.Template(setup_py_template)
|
cpp_test_project.files["setup.py"] = jinja2.Template(setup_py_template)
|
||||||
cpp_test_project.files['spam.cpp'] = jinja2.Template(spam_cpp_template)
|
cpp_test_project.files["spam.cpp"] = jinja2.Template(spam_cpp_template)
|
||||||
|
|
||||||
cpp11_project = cpp_test_project.copy()
|
cpp11_project = cpp_test_project.copy()
|
||||||
cpp11_project.template_context['extra_compile_args'] = (
|
cpp11_project.template_context["extra_compile_args"] = (
|
||||||
['/std:c++11'] if utils.platform == 'windows' else ['-std=c++11']
|
["/std:c++11"] if utils.platform == "windows" else ["-std=c++11"]
|
||||||
)
|
)
|
||||||
cpp11_project.template_context['spam_cpp_top_level_add'] = '#include <array>'
|
cpp11_project.template_context["spam_cpp_top_level_add"] = "#include <array>"
|
||||||
|
|
||||||
|
|
||||||
def test_cpp11(tmp_path):
|
def test_cpp11(tmp_path):
|
||||||
# This test checks that the C++11 standard is supported
|
# This test checks that the C++11 standard is supported
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
|
|
||||||
cpp11_project.generate(project_dir)
|
cpp11_project.generate(project_dir)
|
||||||
|
|
||||||
# VC++ for Python 2.7 does not support modern standards
|
# VC++ for Python 2.7 does not support modern standards
|
||||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32'}
|
add_env = {"CIBW_SKIP": "cp27-win* pp27-win32"}
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w
|
w
|
||||||
for w in utils.expected_wheels('spam', '0.1.0')
|
for w in utils.expected_wheels("spam", "0.1.0")
|
||||||
if 'cp27-cp27m-win' not in w and 'pp27-pypy_73-win32' not in w
|
if "cp27-cp27m-win" not in w and "pp27-pypy_73-win32" not in w
|
||||||
]
|
]
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
cpp14_project = cpp_test_project.copy()
|
cpp14_project = cpp_test_project.copy()
|
||||||
cpp14_project.template_context['extra_compile_args'] = (
|
cpp14_project.template_context["extra_compile_args"] = (
|
||||||
['/std:c++14'] if utils.platform == 'windows' else ['-std=c++14']
|
["/std:c++14"] if utils.platform == "windows" else ["-std=c++14"]
|
||||||
)
|
)
|
||||||
cpp14_project.template_context['spam_cpp_top_level_add'] = "int a = 100'000;"
|
cpp14_project.template_context["spam_cpp_top_level_add"] = "int a = 100'000;"
|
||||||
|
|
||||||
|
|
||||||
def test_cpp14(tmp_path):
|
def test_cpp14(tmp_path):
|
||||||
# This test checks that the C++14 standard is supported
|
# This test checks that the C++14 standard is supported
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
|
|
||||||
cpp14_project.generate(project_dir)
|
cpp14_project.generate(project_dir)
|
||||||
|
|
||||||
# VC++ for Python 2.7 does not support modern standards
|
# VC++ for Python 2.7 does not support modern standards
|
||||||
# The manylinux1 docker image does not have a compiler which supports C++11
|
# The manylinux1 docker image does not have a compiler which supports C++11
|
||||||
add_env = {'CIBW_SKIP': 'cp27-win* pp27-win32'}
|
add_env = {"CIBW_SKIP": "cp27-win* pp27-win32"}
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w
|
w
|
||||||
for w in utils.expected_wheels('spam', '0.1.0')
|
for w in utils.expected_wheels("spam", "0.1.0")
|
||||||
if 'cp27-cp27m-win' not in w and 'pp27-pypy_73-win32' not in w
|
if "cp27-cp27m-win" not in w and "pp27-pypy_73-win32" not in w
|
||||||
]
|
]
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
@@ -131,38 +131,38 @@ cpp17_project = cpp_test_project.copy()
|
|||||||
|
|
||||||
# Python and PyPy 2.7 headers use the `register` keyword, which is forbidden in
|
# Python and PyPy 2.7 headers use the `register` keyword, which is forbidden in
|
||||||
# the C++17 standard, so we need the -Wno-register or /wd5033 options
|
# the C++17 standard, so we need the -Wno-register or /wd5033 options
|
||||||
cpp17_project.template_context['extra_compile_args'] = (
|
cpp17_project.template_context["extra_compile_args"] = (
|
||||||
['/std:c++17', '/wd5033'] if utils.platform == 'windows' else ['-std=c++17', '-Wno-register']
|
["/std:c++17", "/wd5033"] if utils.platform == "windows" else ["-std=c++17", "-Wno-register"]
|
||||||
)
|
)
|
||||||
cpp17_project.template_context[
|
cpp17_project.template_context[
|
||||||
'spam_cpp_top_level_add'
|
"spam_cpp_top_level_add"
|
||||||
] = r'''
|
] = r"""
|
||||||
#include <utility>
|
#include <utility>
|
||||||
auto a = std::pair(5.0, false);
|
auto a = std::pair(5.0, false);
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test_cpp17(tmp_path):
|
def test_cpp17(tmp_path):
|
||||||
# This test checks that the C++17 standard is supported
|
# This test checks that the C++17 standard is supported
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
|
|
||||||
cpp17_project.generate(project_dir)
|
cpp17_project.generate(project_dir)
|
||||||
|
|
||||||
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
|
if os.environ.get("APPVEYOR_BUILD_WORKER_IMAGE", "") == "Visual Studio 2015":
|
||||||
pytest.skip('Visual Studio 2015 does not support C++17')
|
pytest.skip("Visual Studio 2015 does not support C++17")
|
||||||
|
|
||||||
# Pypy's distutils sets the default compiler to 'msvc9compiler', which
|
# Pypy's distutils sets the default compiler to 'msvc9compiler', which
|
||||||
# is too old to support cpp17.
|
# is too old to support cpp17.
|
||||||
add_env = {'CIBW_SKIP': 'cp27-win* pp??-*'}
|
add_env = {"CIBW_SKIP": "cp27-win* pp??-*"}
|
||||||
|
|
||||||
if utils.platform == 'macos':
|
if utils.platform == "macos":
|
||||||
add_env['MACOSX_DEPLOYMENT_TARGET'] = '10.13'
|
add_env["MACOSX_DEPLOYMENT_TARGET"] = "10.13"
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w
|
w
|
||||||
for w in utils.expected_wheels('spam', '0.1.0', macosx_deployment_target='10.13')
|
for w in utils.expected_wheels("spam", "0.1.0", macosx_deployment_target="10.13")
|
||||||
if 'cp27-cp27m-win' not in w and '-pp' not in w
|
if "cp27-cp27m-win" not in w and "-pp" not in w
|
||||||
]
|
]
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
@@ -171,13 +171,13 @@ def test_cpp17(tmp_path):
|
|||||||
def test_cpp17_py27_modern_msvc_workaround(tmp_path):
|
def test_cpp17_py27_modern_msvc_workaround(tmp_path):
|
||||||
# This test checks the workaround for building Python 2.7 wheel with MSVC 14
|
# This test checks the workaround for building Python 2.7 wheel with MSVC 14
|
||||||
|
|
||||||
if utils.platform != 'windows':
|
if utils.platform != "windows":
|
||||||
pytest.skip('the test is only relevant to the Windows build')
|
pytest.skip("the test is only relevant to the Windows build")
|
||||||
|
|
||||||
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') == 'Visual Studio 2015':
|
if os.environ.get("APPVEYOR_BUILD_WORKER_IMAGE", "") == "Visual Studio 2015":
|
||||||
pytest.skip('Visual Studio 2015 does not support C++17')
|
pytest.skip("Visual Studio 2015 does not support C++17")
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
cpp17_project.generate(project_dir)
|
cpp17_project.generate(project_dir)
|
||||||
|
|
||||||
# VC++ for Python 2.7 (i.e., MSVC 9) does not support modern standards
|
# VC++ for Python 2.7 (i.e., MSVC 9) does not support modern standards
|
||||||
@@ -186,7 +186,7 @@ def test_cpp17_py27_modern_msvc_workaround(tmp_path):
|
|||||||
# included with Python: see documentation for more info
|
# included with Python: see documentation for more info
|
||||||
# DISTUTILS_USE_SDK and MSSdk=1 tell distutils/setuptools that we are adding
|
# DISTUTILS_USE_SDK and MSSdk=1 tell distutils/setuptools that we are adding
|
||||||
# MSVC's compiler, tools, and libraries to PATH ourselves
|
# MSVC's compiler, tools, and libraries to PATH ourselves
|
||||||
add_env = {'DISTUTILS_USE_SDK': '1', 'MSSdk': '1'}
|
add_env = {"DISTUTILS_USE_SDK": "1", "MSSdk": "1"}
|
||||||
|
|
||||||
# Use existing setuptools code to run Visual Studio's vcvarsall.bat and get the
|
# Use existing setuptools code to run Visual Studio's vcvarsall.bat and get the
|
||||||
# necessary environment variables, since running vcvarsall.bat in a subprocess
|
# necessary environment variables, since running vcvarsall.bat in a subprocess
|
||||||
@@ -201,22 +201,22 @@ def test_cpp17_py27_modern_msvc_workaround(tmp_path):
|
|||||||
def add_vcvars(prev_env, platform):
|
def add_vcvars(prev_env, platform):
|
||||||
vcvarsall_env = setuptools.msvc.msvc14_get_vc_env(platform)
|
vcvarsall_env = setuptools.msvc.msvc14_get_vc_env(platform)
|
||||||
env = prev_env.copy()
|
env = prev_env.copy()
|
||||||
for vcvar in ['path', 'include', 'lib']:
|
for vcvar in ["path", "include", "lib"]:
|
||||||
env[vcvar] = vcvarsall_env[vcvar]
|
env[vcvar] = vcvarsall_env[vcvar]
|
||||||
return env
|
return env
|
||||||
|
|
||||||
add_env_x86 = add_vcvars(add_env, 'x86')
|
add_env_x86 = add_vcvars(add_env, "x86")
|
||||||
add_env_x86['CIBW_BUILD'] = '?p27-win32'
|
add_env_x86["CIBW_BUILD"] = "?p27-win32"
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env_x86)
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env_x86)
|
||||||
|
|
||||||
add_env_x64 = add_vcvars(add_env, 'x64')
|
add_env_x64 = add_vcvars(add_env, "x64")
|
||||||
add_env_x64['CIBW_BUILD'] = 'cp27-win_amd64'
|
add_env_x64["CIBW_BUILD"] = "cp27-win_amd64"
|
||||||
actual_wheels += utils.cibuildwheel_run(project_dir, add_env=add_env_x64)
|
actual_wheels += utils.cibuildwheel_run(project_dir, add_env=add_env_x64)
|
||||||
|
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w
|
w
|
||||||
for w in utils.expected_wheels('spam', '0.1.0', exclude_27=False)
|
for w in utils.expected_wheels("spam", "0.1.0", exclude_27=False)
|
||||||
if 'cp27-cp27m-win' in w or 'pp27-pypy_73-win32' in w
|
if "cp27-cp27m-win" in w or "pp27-pypy_73-win32" in w
|
||||||
]
|
]
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
project_with_expected_version_checks = test_projects.new_c_project(
|
project_with_expected_version_checks = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@@ -31,124 +31,124 @@ project_with_expected_version_checks = test_projects.new_c_project(
|
|||||||
assert '{}=={}'.format(package_name, expected_version) in versions, (
|
assert '{}=={}'.format(package_name, expected_version) in versions, (
|
||||||
'error: {} version should equal {}'.format(package_name, expected_version)
|
'error: {} version should equal {}'.format(package_name, expected_version)
|
||||||
)
|
)
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
VERSION_REGEX = r'([\w-]+)==([^\s]+)'
|
VERSION_REGEX = r"([\w-]+)==([^\s]+)"
|
||||||
|
|
||||||
|
|
||||||
def get_versions_from_constraint_file(constraint_file):
|
def get_versions_from_constraint_file(constraint_file):
|
||||||
constraint_file_text = constraint_file.read_text(encoding='utf8')
|
constraint_file_text = constraint_file.read_text(encoding="utf8")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
package: version for package, version in re.findall(VERSION_REGEX, constraint_file_text)
|
package: version for package, version in re.findall(VERSION_REGEX, constraint_file_text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('python_version', ['2.7', '3.5', '3.6', '3.8'])
|
@pytest.mark.parametrize("python_version", ["2.7", "3.5", "3.6", "3.8"])
|
||||||
def test_pinned_versions(tmp_path, python_version):
|
def test_pinned_versions(tmp_path, python_version):
|
||||||
if utils.platform == 'linux':
|
if utils.platform == "linux":
|
||||||
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
|
||||||
|
|
||||||
if utils.platform == 'windows' and python_version == '2.7':
|
if utils.platform == "windows" and python_version == "2.7":
|
||||||
pytest.skip('Windows requires a workaround')
|
pytest.skip("Windows requires a workaround")
|
||||||
|
|
||||||
is_macos_11_or_later = utils.platform == 'macos' and utils.get_macos_version() >= (10, 16)
|
is_macos_11_or_later = utils.platform == "macos" and utils.get_macos_version() >= (10, 16)
|
||||||
|
|
||||||
if is_macos_11_or_later and python_version == '3.5':
|
if is_macos_11_or_later and python_version == "3.5":
|
||||||
pytest.skip('CPython 3.5 doesn\'t work on macOS Big Sur+')
|
pytest.skip("CPython 3.5 doesn't work on macOS Big Sur+")
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_expected_version_checks.generate(project_dir)
|
project_with_expected_version_checks.generate(project_dir)
|
||||||
|
|
||||||
build_environment = {}
|
build_environment = {}
|
||||||
|
|
||||||
if python_version == '2.7':
|
if python_version == "2.7":
|
||||||
constraint_filename = 'constraints-python27.txt'
|
constraint_filename = "constraints-python27.txt"
|
||||||
build_pattern = '[cp]p27-*'
|
build_pattern = "[cp]p27-*"
|
||||||
elif python_version == '3.5':
|
elif python_version == "3.5":
|
||||||
constraint_filename = 'constraints-python35.txt'
|
constraint_filename = "constraints-python35.txt"
|
||||||
build_pattern = '[cp]p35-*'
|
build_pattern = "[cp]p35-*"
|
||||||
elif python_version == '3.6':
|
elif python_version == "3.6":
|
||||||
constraint_filename = 'constraints-python36.txt'
|
constraint_filename = "constraints-python36.txt"
|
||||||
build_pattern = '[cp]p36-*'
|
build_pattern = "[cp]p36-*"
|
||||||
elif python_version == '3.7':
|
elif python_version == "3.7":
|
||||||
constraint_filename = 'constraints-python37.txt'
|
constraint_filename = "constraints-python37.txt"
|
||||||
build_pattern = '[cp]p37-*'
|
build_pattern = "[cp]p37-*"
|
||||||
else:
|
else:
|
||||||
constraint_filename = 'constraints.txt'
|
constraint_filename = "constraints.txt"
|
||||||
build_pattern = '[cp]p38-*'
|
build_pattern = "[cp]p38-*"
|
||||||
|
|
||||||
constraint_file = cibuildwheel.util.resources_dir / constraint_filename
|
constraint_file = cibuildwheel.util.resources_dir / constraint_filename
|
||||||
constraint_versions = get_versions_from_constraint_file(constraint_file)
|
constraint_versions = get_versions_from_constraint_file(constraint_file)
|
||||||
|
|
||||||
for package in ['pip', 'setuptools', 'wheel', 'virtualenv']:
|
for package in ["pip", "setuptools", "wheel", "virtualenv"]:
|
||||||
env_name = f'EXPECTED_{package.upper()}_VERSION'
|
env_name = f"EXPECTED_{package.upper()}_VERSION"
|
||||||
build_environment[env_name] = constraint_versions[package]
|
build_environment[env_name] = constraint_versions[package]
|
||||||
|
|
||||||
cibw_environment_option = ' '.join(f'{k}={v}' for k, v in build_environment.items())
|
cibw_environment_option = " ".join(f"{k}={v}" for k, v in build_environment.items())
|
||||||
|
|
||||||
# build and test the wheels
|
# build and test the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BUILD': build_pattern,
|
"CIBW_BUILD": build_pattern,
|
||||||
'CIBW_ENVIRONMENT': cibw_environment_option,
|
"CIBW_ENVIRONMENT": cibw_environment_option,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
if python_version == '2.7':
|
if python_version == "2.7":
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w for w in utils.expected_wheels('spam', '0.1.0') if '-cp27' in w or '-pp27' in w
|
w for w in utils.expected_wheels("spam", "0.1.0") if "-cp27" in w or "-pp27" in w
|
||||||
]
|
]
|
||||||
elif python_version == '3.5':
|
elif python_version == "3.5":
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w for w in utils.expected_wheels('spam', '0.1.0') if '-cp35' in w or '-pp35' in w
|
w for w in utils.expected_wheels("spam", "0.1.0") if "-cp35" in w or "-pp35" in w
|
||||||
]
|
]
|
||||||
elif python_version == '3.6':
|
elif python_version == "3.6":
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w for w in utils.expected_wheels('spam', '0.1.0') if '-cp36' in w or '-pp36' in w
|
w for w in utils.expected_wheels("spam", "0.1.0") if "-cp36" in w or "-pp36" in w
|
||||||
]
|
]
|
||||||
elif python_version == '3.8':
|
elif python_version == "3.8":
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w for w in utils.expected_wheels('spam', '0.1.0') if '-cp38' in w or '-pp38' in w
|
w for w in utils.expected_wheels("spam", "0.1.0") if "-cp38" in w or "-pp38" in w
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
raise ValueError('unhandled python version')
|
raise ValueError("unhandled python version")
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('python_version', ['2.7', '3.x'])
|
@pytest.mark.parametrize("python_version", ["2.7", "3.x"])
|
||||||
def test_dependency_constraints_file(tmp_path, python_version):
|
def test_dependency_constraints_file(tmp_path, python_version):
|
||||||
if utils.platform == 'linux':
|
if utils.platform == "linux":
|
||||||
pytest.skip('linux doesn\'t pin individual tool versions, it pins manylinux images instead')
|
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
|
||||||
|
|
||||||
if utils.platform == 'windows' and python_version == '2.7':
|
if utils.platform == "windows" and python_version == "2.7":
|
||||||
pytest.skip('Windows + Travis CI requires a workaround')
|
pytest.skip("Windows + Travis CI requires a workaround")
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_expected_version_checks.generate(project_dir)
|
project_with_expected_version_checks.generate(project_dir)
|
||||||
|
|
||||||
tool_versions = {
|
tool_versions = {
|
||||||
'pip': '20.0.2',
|
"pip": "20.0.2",
|
||||||
'setuptools': '44.0.0' if python_version == '2.7' else '46.0.0',
|
"setuptools": "44.0.0" if python_version == "2.7" else "46.0.0",
|
||||||
'wheel': '0.34.2',
|
"wheel": "0.34.2",
|
||||||
'virtualenv': '20.0.10',
|
"virtualenv": "20.0.10",
|
||||||
}
|
}
|
||||||
|
|
||||||
constraints_file = tmp_path / 'constraints.txt'
|
constraints_file = tmp_path / "constraints.txt"
|
||||||
constraints_file.write_text(
|
constraints_file.write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
'''
|
"""
|
||||||
pip=={pip}
|
pip=={pip}
|
||||||
setuptools=={setuptools}
|
setuptools=={setuptools}
|
||||||
wheel=={wheel}
|
wheel=={wheel}
|
||||||
virtualenv=={virtualenv}
|
virtualenv=={virtualenv}
|
||||||
'''.format(
|
""".format(
|
||||||
**tool_versions
|
**tool_versions
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -157,31 +157,31 @@ def test_dependency_constraints_file(tmp_path, python_version):
|
|||||||
build_environment = {}
|
build_environment = {}
|
||||||
|
|
||||||
for package_name, version in tool_versions.items():
|
for package_name, version in tool_versions.items():
|
||||||
env_name = f'EXPECTED_{package_name.upper()}_VERSION'
|
env_name = f"EXPECTED_{package_name.upper()}_VERSION"
|
||||||
build_environment[env_name] = version
|
build_environment[env_name] = version
|
||||||
|
|
||||||
cibw_environment_option = ' '.join(f'{k}={v}' for k, v in build_environment.items())
|
cibw_environment_option = " ".join(f"{k}={v}" for k, v in build_environment.items())
|
||||||
|
|
||||||
# build and test the wheels
|
# build and test the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BUILD': '[cp]p27-*' if python_version == '2.7' else '[cp]p3?-*',
|
"CIBW_BUILD": "[cp]p27-*" if python_version == "2.7" else "[cp]p3?-*",
|
||||||
'CIBW_ENVIRONMENT': cibw_environment_option,
|
"CIBW_ENVIRONMENT": cibw_environment_option,
|
||||||
'CIBW_DEPENDENCY_VERSIONS': str(constraints_file),
|
"CIBW_DEPENDENCY_VERSIONS": str(constraints_file),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
if python_version == '2.7':
|
if python_version == "2.7":
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w for w in utils.expected_wheels('spam', '0.1.0') if '-cp27' in w or '-pp27' in w
|
w for w in utils.expected_wheels("spam", "0.1.0") if "-cp27" in w or "-pp27" in w
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w
|
w
|
||||||
for w in utils.expected_wheels('spam', '0.1.0')
|
for w in utils.expected_wheels("spam", "0.1.0")
|
||||||
if '-cp27' not in w and '-pp27' not in w
|
if "-cp27" not in w and "-pp27" not in w
|
||||||
]
|
]
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
+12
-12
@@ -7,7 +7,7 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
dockcross_only_project = test_projects.new_c_project(
|
dockcross_only_project = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# check that we're running in the correct docker image as specified in the
|
# check that we're running in the correct docker image as specified in the
|
||||||
@@ -16,35 +16,35 @@ dockcross_only_project = test_projects.new_c_project(
|
|||||||
raise Exception(
|
raise Exception(
|
||||||
"/dockcross directory not found. Is this test running in the correct docker image?"
|
"/dockcross directory not found. Is this test running in the correct docker image?"
|
||||||
)
|
)
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
if utils.platform != 'linux':
|
if utils.platform != "linux":
|
||||||
pytest.skip('the test is only relevant to the linux build')
|
pytest.skip("the test is only relevant to the linux build")
|
||||||
if platform.machine() not in ['x86_64', 'i686']:
|
if platform.machine() not in ["x86_64", "i686"]:
|
||||||
pytest.skip(
|
pytest.skip(
|
||||||
'this test is currently only possible on x86_64/i686 due to availability of alternative images'
|
"this test is currently only possible on x86_64/i686 due to availability of alternative images"
|
||||||
)
|
)
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
dockcross_only_project.generate(project_dir)
|
dockcross_only_project.generate(project_dir)
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_MANYLINUX_X86_64_IMAGE': 'dockcross/manylinux2010-x64',
|
"CIBW_MANYLINUX_X86_64_IMAGE": "dockcross/manylinux2010-x64",
|
||||||
'CIBW_MANYLINUX_I686_IMAGE': 'dockcross/manylinux2010-x86',
|
"CIBW_MANYLINUX_I686_IMAGE": "dockcross/manylinux2010-x86",
|
||||||
'CIBW_SKIP': 'pp* cp27-* cp39-*',
|
"CIBW_SKIP": "pp* cp27-* cp39-*",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels built
|
# also check that we got the right wheels built
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w
|
w
|
||||||
for w in utils.expected_wheels('spam', '0.1.0')
|
for w in utils.expected_wheels("spam", "0.1.0")
|
||||||
if '-pp' not in w and '-cp39-' not in w and '-cp27-' not in w
|
if "-pp" not in w and "-cp39-" not in w and "-cp27-" not in w
|
||||||
]
|
]
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
+14
-14
@@ -7,45 +7,45 @@ from . import test_projects, utils
|
|||||||
project_with_a_test = test_projects.new_c_project()
|
project_with_a_test = test_projects.new_c_project()
|
||||||
|
|
||||||
project_with_a_test.files[
|
project_with_a_test.files[
|
||||||
'test/spam_test.py'
|
"test/spam_test.py"
|
||||||
] = r'''
|
] = r"""
|
||||||
import spam
|
import spam
|
||||||
|
|
||||||
def test_spam():
|
def test_spam():
|
||||||
assert spam.system('python -c "exit(0)"') == 0
|
assert spam.system('python -c "exit(0)"') == 0
|
||||||
assert spam.system('python -c "exit(1)"') != 0
|
assert spam.system('python -c "exit(1)"') != 0
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.emulation
|
@pytest.mark.emulation
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_a_test.generate(project_dir)
|
project_with_a_test.generate(project_dir)
|
||||||
|
|
||||||
# build and test the wheels
|
# build and test the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_TEST_REQUIRES': 'pytest',
|
"CIBW_TEST_REQUIRES": "pytest",
|
||||||
'CIBW_TEST_COMMAND': 'pytest {project}/test',
|
"CIBW_TEST_COMMAND": "pytest {project}/test",
|
||||||
'CIBW_ARCHS': 'aarch64 ppc64le s390x',
|
"CIBW_ARCHS": "aarch64 ppc64le s390x",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
expected_wheels = (
|
expected_wheels = (
|
||||||
utils.expected_wheels('spam', '0.1.0', machine_arch='aarch64')
|
utils.expected_wheels("spam", "0.1.0", machine_arch="aarch64")
|
||||||
+ utils.expected_wheels('spam', '0.1.0', machine_arch='ppc64le')
|
+ utils.expected_wheels("spam", "0.1.0", machine_arch="ppc64le")
|
||||||
+ utils.expected_wheels('spam', '0.1.0', machine_arch='s390x')
|
+ utils.expected_wheels("spam", "0.1.0", machine_arch="s390x")
|
||||||
)
|
)
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
def test_setting_arch_on_other_platforms(tmp_path, capfd):
|
def test_setting_arch_on_other_platforms(tmp_path, capfd):
|
||||||
if utils.platform == 'linux':
|
if utils.platform == "linux":
|
||||||
pytest.skip('this test checks the behaviour on platforms other than linux')
|
pytest.skip("this test checks the behaviour on platforms other than linux")
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_a_test.generate(project_dir)
|
project_with_a_test.generate(project_dir)
|
||||||
|
|
||||||
# build and test the wheels
|
# build and test the wheels
|
||||||
@@ -53,7 +53,7 @@ def test_setting_arch_on_other_platforms(tmp_path, capfd):
|
|||||||
utils.cibuildwheel_run(
|
utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_ARCHS': 'aarch64',
|
"CIBW_ARCHS": "aarch64",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+15
-15
@@ -8,7 +8,7 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
project_with_environment_asserts = test_projects.new_c_project(
|
project_with_environment_asserts = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# explode if environment isn't correct, as set in CIBW_ENVIRONMENT
|
# explode if environment isn't correct, as set in CIBW_ENVIRONMENT
|
||||||
@@ -27,13 +27,13 @@ project_with_environment_asserts = test_projects.new_c_project(
|
|||||||
raise Exception('PATH should contain "/opt/cibw_test_path". It was "%s"' % PATH)
|
raise Exception('PATH should contain "/opt/cibw_test_path". It was "%s"' % PATH)
|
||||||
if "$PATH" in PATH:
|
if "$PATH" in PATH:
|
||||||
raise Exception('$PATH should be expanded in PATH. It was "%s"' % PATH)
|
raise Exception('$PATH should be expanded in PATH. It was "%s"' % PATH)
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_environment_asserts.generate(project_dir)
|
project_with_environment_asserts.generate(project_dir)
|
||||||
|
|
||||||
# write some information into the CIBW_ENVIRONMENT, for expansion and
|
# write some information into the CIBW_ENVIRONMENT, for expansion and
|
||||||
@@ -42,19 +42,19 @@ def test(tmp_path):
|
|||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_ENVIRONMENT': '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH=$PATH:/opt/cibw_test_path''',
|
"CIBW_ENVIRONMENT": """CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH=$PATH:/opt/cibw_test_path""",
|
||||||
'CIBW_ENVIRONMENT_WINDOWS': '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH="$PATH;/opt/cibw_test_path"''',
|
"CIBW_ENVIRONMENT_WINDOWS": '''CIBW_TEST_VAR="a b c" CIBW_TEST_VAR_2=1 CIBW_TEST_VAR_3="$(echo 'test string 3')" PATH="$PATH;/opt/cibw_test_path"''',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels built
|
# also check that we got the right wheels built
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
def test_overridden_path(tmp_path, capfd):
|
def test_overridden_path(tmp_path, capfd):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
output_dir = tmp_path / 'output'
|
output_dir = tmp_path / "output"
|
||||||
|
|
||||||
project = test_projects.new_c_project()
|
project = test_projects.new_c_project()
|
||||||
project.generate(project_dir)
|
project.generate(project_dir)
|
||||||
@@ -62,26 +62,26 @@ def test_overridden_path(tmp_path, capfd):
|
|||||||
|
|
||||||
# mess up PATH, somehow
|
# mess up PATH, somehow
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
if utils.platform == 'linux':
|
if utils.platform == "linux":
|
||||||
utils.cibuildwheel_run(
|
utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
output_dir=output_dir,
|
output_dir=output_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BEFORE_ALL': 'mkdir new_path && touch new_path/python && chmod +x new_path/python',
|
"CIBW_BEFORE_ALL": "mkdir new_path && touch new_path/python && chmod +x new_path/python",
|
||||||
'CIBW_ENVIRONMENT': '''PATH="$(pwd)/new_path:$PATH"''',
|
"CIBW_ENVIRONMENT": '''PATH="$(pwd)/new_path:$PATH"''',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
new_path = tmp_path / 'another_bin'
|
new_path = tmp_path / "another_bin"
|
||||||
new_path.mkdir()
|
new_path.mkdir()
|
||||||
(new_path / 'python').touch(mode=0o777)
|
(new_path / "python").touch(mode=0o777)
|
||||||
|
|
||||||
utils.cibuildwheel_run(
|
utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
output_dir=output_dir,
|
output_dir=output_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'NEW_PATH': str(new_path),
|
"NEW_PATH": str(new_path),
|
||||||
'CIBW_ENVIRONMENT': f'''PATH="$NEW_PATH{os.pathsep}$PATH"''',
|
"CIBW_ENVIRONMENT": f'''PATH="$NEW_PATH{os.pathsep}$PATH"''',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+45
-45
@@ -9,14 +9,14 @@ from . import test_projects, utils
|
|||||||
basic_project = test_projects.new_c_project()
|
basic_project = test_projects.new_c_project()
|
||||||
|
|
||||||
ALL_MACOS_WHEELS = {
|
ALL_MACOS_WHEELS = {
|
||||||
*utils.expected_wheels('spam', '0.1.0', machine_arch='x86_64'),
|
*utils.expected_wheels("spam", "0.1.0", machine_arch="x86_64"),
|
||||||
*utils.expected_wheels('spam', '0.1.0', machine_arch='arm64'),
|
*utils.expected_wheels("spam", "0.1.0", machine_arch="arm64"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_xcode_version() -> Tuple[int, int]:
|
def get_xcode_version() -> Tuple[int, int]:
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
['xcodebuild', '-version'],
|
["xcodebuild", "-version"],
|
||||||
universal_newlines=True,
|
universal_newlines=True,
|
||||||
check=True,
|
check=True,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
@@ -24,112 +24,112 @@ def get_xcode_version() -> Tuple[int, int]:
|
|||||||
lines = output.splitlines()
|
lines = output.splitlines()
|
||||||
_, version_str = lines[0].split()
|
_, version_str = lines[0].split()
|
||||||
|
|
||||||
version_parts = version_str.split('.')
|
version_parts = version_str.split(".")
|
||||||
return (int(version_parts[0]), int(version_parts[1]))
|
return (int(version_parts[0]), int(version_parts[1]))
|
||||||
|
|
||||||
|
|
||||||
def test_cross_compiled_build(tmp_path):
|
def test_cross_compiled_build(tmp_path):
|
||||||
if utils.platform != 'macos':
|
if utils.platform != "macos":
|
||||||
pytest.skip('this test is only relevant to macos')
|
pytest.skip("this test is only relevant to macos")
|
||||||
if get_xcode_version() < (12, 2):
|
if get_xcode_version() < (12, 2):
|
||||||
pytest.skip('this test only works with Xcode 12.2 or greater')
|
pytest.skip("this test only works with Xcode 12.2 or greater")
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
basic_project.generate(project_dir)
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BUILD': 'cp39-*',
|
"CIBW_BUILD": "cp39-*",
|
||||||
'CIBW_ARCHS': 'x86_64, universal2, arm64',
|
"CIBW_ARCHS": "x86_64, universal2, arm64",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w]
|
expected_wheels = [w for w in ALL_MACOS_WHEELS if "cp39" in w]
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('build_universal2', [False, True])
|
@pytest.mark.parametrize("build_universal2", [False, True])
|
||||||
def test_cross_compiled_test(tmp_path, capfd, build_universal2):
|
def test_cross_compiled_test(tmp_path, capfd, build_universal2):
|
||||||
if utils.platform != 'macos':
|
if utils.platform != "macos":
|
||||||
pytest.skip('this test is only relevant to macos')
|
pytest.skip("this test is only relevant to macos")
|
||||||
if get_xcode_version() < (12, 2):
|
if get_xcode_version() < (12, 2):
|
||||||
pytest.skip('this test only works with Xcode 12.2 or greater')
|
pytest.skip("this test only works with Xcode 12.2 or greater")
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
basic_project.generate(project_dir)
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BUILD': 'cp39-*',
|
"CIBW_BUILD": "cp39-*",
|
||||||
'CIBW_TEST_COMMAND': '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
"CIBW_TEST_COMMAND": '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
||||||
'CIBW_ARCHS': 'universal2' if build_universal2 else 'x86_64 arm64',
|
"CIBW_ARCHS": "universal2" if build_universal2 else "x86_64 arm64",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
|
|
||||||
if platform.machine() == 'x86_64':
|
if platform.machine() == "x86_64":
|
||||||
# ensure that tests were run on only x86_64
|
# ensure that tests were run on only x86_64
|
||||||
assert 'running tests on x86_64' in captured.out
|
assert "running tests on x86_64" in captured.out
|
||||||
assert 'running tests on arm64' not in captured.out
|
assert "running tests on arm64" not in captured.out
|
||||||
if build_universal2:
|
if build_universal2:
|
||||||
assert (
|
assert (
|
||||||
'While universal2 wheels can be built on x86_64, the arm64 part of them cannot currently be tested'
|
"While universal2 wheels can be built on x86_64, the arm64 part of them cannot currently be tested"
|
||||||
in captured.err
|
in captured.err
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
assert (
|
assert (
|
||||||
'While arm64 wheels can be built on x86_64, they cannot be tested' in captured.err
|
"While arm64 wheels can be built on x86_64, they cannot be tested" in captured.err
|
||||||
)
|
)
|
||||||
elif platform.machine() == 'arm64':
|
elif platform.machine() == "arm64":
|
||||||
# ensure that tests were run on both x86_64 and arm64
|
# ensure that tests were run on both x86_64 and arm64
|
||||||
assert 'running tests on x86_64' in captured.out
|
assert "running tests on x86_64" in captured.out
|
||||||
assert 'running tests on arm64' in captured.out
|
assert "running tests on arm64" in captured.out
|
||||||
|
|
||||||
if build_universal2:
|
if build_universal2:
|
||||||
expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w and 'universal2' in w]
|
expected_wheels = [w for w in ALL_MACOS_WHEELS if "cp39" in w and "universal2" in w]
|
||||||
else:
|
else:
|
||||||
expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w and 'universal2' not in w]
|
expected_wheels = [w for w in ALL_MACOS_WHEELS if "cp39" in w and "universal2" not in w]
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('skip_arm64_test', [False, True])
|
@pytest.mark.parametrize("skip_arm64_test", [False, True])
|
||||||
def test_universal2_testing(tmp_path, capfd, skip_arm64_test):
|
def test_universal2_testing(tmp_path, capfd, skip_arm64_test):
|
||||||
if utils.platform != 'macos':
|
if utils.platform != "macos":
|
||||||
pytest.skip('this test is only relevant to macos')
|
pytest.skip("this test is only relevant to macos")
|
||||||
if get_xcode_version() < (12, 2):
|
if get_xcode_version() < (12, 2):
|
||||||
pytest.skip('this test only works with Xcode 12.2 or greater')
|
pytest.skip("this test only works with Xcode 12.2 or greater")
|
||||||
if platform.machine() != 'x86_64':
|
if platform.machine() != "x86_64":
|
||||||
pytest.skip('this test only works on x86_64')
|
pytest.skip("this test only works on x86_64")
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
basic_project.generate(project_dir)
|
basic_project.generate(project_dir)
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BUILD': 'cp39-*',
|
"CIBW_BUILD": "cp39-*",
|
||||||
'CIBW_TEST_COMMAND': '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
"CIBW_TEST_COMMAND": '''python -c "import platform; print('running tests on ' + platform.machine())"''',
|
||||||
'CIBW_ARCHS': 'universal2',
|
"CIBW_ARCHS": "universal2",
|
||||||
'CIBW_TEST_SKIP': '*_universal2:arm64' if skip_arm64_test else '',
|
"CIBW_TEST_SKIP": "*_universal2:arm64" if skip_arm64_test else "",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
|
|
||||||
if platform.machine() == 'x86_64':
|
if platform.machine() == "x86_64":
|
||||||
assert 'running tests on x86_64' in captured.out
|
assert "running tests on x86_64" in captured.out
|
||||||
assert 'running tests on arm64' not in captured.out
|
assert "running tests on arm64" not in captured.out
|
||||||
|
|
||||||
warning_message = 'While universal2 wheels can be built on x86_64, the arm64 part of them cannot currently be tested'
|
warning_message = "While universal2 wheels can be built on x86_64, the arm64 part of them cannot currently be tested"
|
||||||
if skip_arm64_test:
|
if skip_arm64_test:
|
||||||
assert warning_message not in captured.err
|
assert warning_message not in captured.err
|
||||||
else:
|
else:
|
||||||
assert warning_message in captured.err
|
assert warning_message in captured.err
|
||||||
|
|
||||||
expected_wheels = [w for w in ALL_MACOS_WHEELS if 'cp39' in w and 'universal2' in w]
|
expected_wheels = [w for w in ALL_MACOS_WHEELS if "cp39" in w and "universal2" in w]
|
||||||
|
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from . import test_projects, utils
|
|||||||
# TODO: specify these at runtime according to manylinux_image
|
# TODO: specify these at runtime according to manylinux_image
|
||||||
project_with_manylinux_symbols = test_projects.new_c_project(
|
project_with_manylinux_symbols = test_projects.new_c_project(
|
||||||
spam_c_top_level_add=textwrap.dedent(
|
spam_c_top_level_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -21,10 +21,10 @@ project_with_manylinux_symbols = test_projects.new_c_project(
|
|||||||
#if !__GLIBC_PREREQ(2, 5) /* manylinux1 is glibc 2.5 */
|
#if !__GLIBC_PREREQ(2, 5) /* manylinux1 is glibc 2.5 */
|
||||||
#error "Must run on a glibc >= 2.5 linux environment"
|
#error "Must run on a glibc >= 2.5 linux environment"
|
||||||
#endif
|
#endif
|
||||||
'''
|
"""
|
||||||
),
|
),
|
||||||
spam_c_function_add=textwrap.dedent(
|
spam_c_function_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 24)
|
#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 24)
|
||||||
// nextupf is only available in manylinux_2_24+
|
// nextupf is only available in manylinux_2_24+
|
||||||
sts = (int)nextupf(0.0F);
|
sts = (int)nextupf(0.0F);
|
||||||
@@ -35,50 +35,50 @@ project_with_manylinux_symbols = test_projects.new_c_project(
|
|||||||
// malloc_info is only available on manylinux2010+
|
// malloc_info is only available on manylinux2010+
|
||||||
sts = malloc_info(0, stdout);
|
sts = malloc_info(0, stdout);
|
||||||
#endif
|
#endif
|
||||||
'''
|
"""
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'manylinux_image', ['manylinux1', 'manylinux2010', 'manylinux2014', 'manylinux_2_24']
|
"manylinux_image", ["manylinux1", "manylinux2010", "manylinux2014", "manylinux_2_24"]
|
||||||
)
|
)
|
||||||
def test(manylinux_image, tmp_path):
|
def test(manylinux_image, tmp_path):
|
||||||
if utils.platform != 'linux':
|
if utils.platform != "linux":
|
||||||
pytest.skip('the docker test is only relevant to the linux build')
|
pytest.skip("the docker test is only relevant to the linux build")
|
||||||
elif platform.machine() not in ['x86_64', 'i686']:
|
elif platform.machine() not in ["x86_64", "i686"]:
|
||||||
if manylinux_image in ['manylinux1', 'manylinux2010']:
|
if manylinux_image in ["manylinux1", "manylinux2010"]:
|
||||||
pytest.skip("manylinux1 and 2010 doesn't exist for non-x86 architectures")
|
pytest.skip("manylinux1 and 2010 doesn't exist for non-x86 architectures")
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_manylinux_symbols.generate(project_dir)
|
project_with_manylinux_symbols.generate(project_dir)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
# CFLAGS environment variable is necessary to fail on 'malloc_info' (on manylinux1) during compilation/linking,
|
# CFLAGS environment variable is necessary to fail on 'malloc_info' (on manylinux1) during compilation/linking,
|
||||||
# rather than when dynamically loading the Python
|
# rather than when dynamically loading the Python
|
||||||
add_env = {
|
add_env = {
|
||||||
'CIBW_ENVIRONMENT': 'CFLAGS="$CFLAGS -O0 -Werror=implicit-function-declaration"',
|
"CIBW_ENVIRONMENT": 'CFLAGS="$CFLAGS -O0 -Werror=implicit-function-declaration"',
|
||||||
'CIBW_MANYLINUX_X86_64_IMAGE': manylinux_image,
|
"CIBW_MANYLINUX_X86_64_IMAGE": manylinux_image,
|
||||||
'CIBW_MANYLINUX_I686_IMAGE': manylinux_image,
|
"CIBW_MANYLINUX_I686_IMAGE": manylinux_image,
|
||||||
'CIBW_MANYLINUX_PYPY_X86_64_IMAGE': manylinux_image,
|
"CIBW_MANYLINUX_PYPY_X86_64_IMAGE": manylinux_image,
|
||||||
'CIBW_MANYLINUX_AARCH64_IMAGE': manylinux_image,
|
"CIBW_MANYLINUX_AARCH64_IMAGE": manylinux_image,
|
||||||
'CIBW_MANYLINUX_PPC64LE_IMAGE': manylinux_image,
|
"CIBW_MANYLINUX_PPC64LE_IMAGE": manylinux_image,
|
||||||
'CIBW_MANYLINUX_S390X_IMAGE': manylinux_image,
|
"CIBW_MANYLINUX_S390X_IMAGE": manylinux_image,
|
||||||
}
|
}
|
||||||
if manylinux_image == 'manylinux1':
|
if manylinux_image == "manylinux1":
|
||||||
# We don't have a manylinux1 image for PyPy
|
# We don't have a manylinux1 image for PyPy
|
||||||
add_env['CIBW_SKIP'] = 'pp*'
|
add_env["CIBW_SKIP"] = "pp*"
|
||||||
elif manylinux_image in {'manylinux2014', 'manylinux_2_24'}:
|
elif manylinux_image in {"manylinux2014", "manylinux_2_24"}:
|
||||||
# We don't have a manylinux2014 / 'manylinux_2_24' image for PyPy (yet?)
|
# We don't have a manylinux2014 / 'manylinux_2_24' image for PyPy (yet?)
|
||||||
# Python 2.7 not available on manylinux2014 / 'manylinux_2_24'
|
# Python 2.7 not available on manylinux2014 / 'manylinux_2_24'
|
||||||
add_env['CIBW_SKIP'] = 'cp27* pp*'
|
add_env["CIBW_SKIP"] = "cp27* pp*"
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
actual_wheels = utils.cibuildwheel_run(project_dir, add_env=add_env)
|
||||||
|
|
||||||
expected_wheels = [
|
expected_wheels = [
|
||||||
w for w in utils.expected_wheels('spam', '0.1.0', manylinux_versions=[manylinux_image])
|
w for w in utils.expected_wheels("spam", "0.1.0", manylinux_versions=[manylinux_image])
|
||||||
]
|
]
|
||||||
if manylinux_image in {'manylinux2014', 'manylinux_2_24'}:
|
if manylinux_image in {"manylinux2014", "manylinux_2_24"}:
|
||||||
expected_wheels = [w for w in expected_wheels if '-cp27' not in w]
|
expected_wheels = [w for w in expected_wheels if "-cp27" not in w]
|
||||||
if manylinux_image in {'manylinux1', 'manylinux2014', 'manylinux_2_24'}:
|
if manylinux_image in {"manylinux1", "manylinux2014", "manylinux_2_24"}:
|
||||||
expected_wheels = [w for w in expected_wheels if '-pp' not in w]
|
expected_wheels = [w for w in expected_wheels if "-pp" not in w]
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
@@ -8,42 +8,42 @@ from pathlib import Path
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = ArgumentParser(
|
parser = ArgumentParser(
|
||||||
prog="python -m test.test_projects", description='Generate a test project to check it out'
|
prog="python -m test.test_projects", description="Generate a test project to check it out"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--open',
|
"--open",
|
||||||
action='store_true',
|
action="store_true",
|
||||||
help='Open the generated project in a file explorer',
|
help="Open the generated project in a file explorer",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'PROJECT',
|
"PROJECT",
|
||||||
help='Python path to a project object. E.g. test.test_0_basic.basic_project',
|
help="Python path to a project object. E.g. test.test_0_basic.basic_project",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'OUTPUT',
|
"OUTPUT",
|
||||||
nargs='?',
|
nargs="?",
|
||||||
help='Path to output dir. If no dir is passed, a tempdir will be generated.',
|
help="Path to output dir. If no dir is passed, a tempdir will be generated.",
|
||||||
)
|
)
|
||||||
options = parser.parse_args()
|
options = parser.parse_args()
|
||||||
|
|
||||||
module, _, name = options.PROJECT.rpartition('.')
|
module, _, name = options.PROJECT.rpartition(".")
|
||||||
|
|
||||||
project = getattr(importlib.import_module(module), name)
|
project = getattr(importlib.import_module(module), name)
|
||||||
|
|
||||||
project_dir = Path(options.OUTPUT or tempfile.mkdtemp())
|
project_dir = Path(options.OUTPUT or tempfile.mkdtemp())
|
||||||
project.generate(project_dir)
|
project.generate(project_dir)
|
||||||
|
|
||||||
print('Project generated at', project_dir)
|
print("Project generated at", project_dir)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
if options.open:
|
if options.open:
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == "darwin":
|
||||||
subprocess.run(['open', '--', project_dir], check=True)
|
subprocess.run(["open", "--", project_dir], check=True)
|
||||||
elif sys.platform == 'linux2':
|
elif sys.platform == "linux2":
|
||||||
subprocess.run(['xdg-open', '--', project_dir], check=True)
|
subprocess.run(["xdg-open", "--", project_dir], check=True)
|
||||||
elif sys.platform == 'win32':
|
elif sys.platform == "win32":
|
||||||
subprocess.run(['explorer', project_dir], check=True)
|
subprocess.run(["explorer", project_dir], check=True)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ TemplateContext = Dict[str, Any]
|
|||||||
|
|
||||||
|
|
||||||
class TestProject:
|
class TestProject:
|
||||||
'''
|
"""
|
||||||
An object that represents a project that can be built by cibuildwheel.
|
An object that represents a project that can be built by cibuildwheel.
|
||||||
Can be manipulated in tests by changing `files` and `template_context`.
|
Can be manipulated in tests by changing `files` and `template_context`.
|
||||||
|
|
||||||
Write out to the filesystem using `generate`.
|
Write out to the filesystem using `generate`.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
__test__ = False # Have pytest ignore this class on `from .test_projects import TestProject`
|
__test__ = False # Have pytest ignore this class on `from .test_projects import TestProject`
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ class TestProject:
|
|||||||
file_path = path / filename
|
file_path = path / filename
|
||||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with file_path.open('w', encoding='utf8') as f:
|
with file_path.open("w", encoding="utf8") as f:
|
||||||
if isinstance(content, jinja2.Template):
|
if isinstance(content, jinja2.Template):
|
||||||
content = content.render(self.template_context)
|
content = content.render(self.template_context)
|
||||||
|
|
||||||
|
|||||||
+19
-19
@@ -2,7 +2,7 @@ import jinja2
|
|||||||
|
|
||||||
from .base import TestProject
|
from .base import TestProject
|
||||||
|
|
||||||
SPAM_C_TEMPLATE = r'''
|
SPAM_C_TEMPLATE = r"""
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
|
||||||
{{ spam_c_top_level_add }}
|
{{ spam_c_top_level_add }}
|
||||||
@@ -57,9 +57,9 @@ MOD_INIT(spam)
|
|||||||
|
|
||||||
MOD_RETURN(m)
|
MOD_RETURN(m)
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
|
|
||||||
SETUP_PY_TEMPLATE = r'''
|
SETUP_PY_TEMPLATE = r"""
|
||||||
import sys
|
import sys
|
||||||
from setuptools import setup, Extension
|
from setuptools import setup, Extension
|
||||||
|
|
||||||
@@ -77,42 +77,42 @@ setup(
|
|||||||
)],
|
)],
|
||||||
{{ setup_py_setup_args_add | indent(4) }}
|
{{ setup_py_setup_args_add | indent(4) }}
|
||||||
)
|
)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
SETUP_CFG_TEMPLATE = r'''
|
SETUP_CFG_TEMPLATE = r"""
|
||||||
[metadata]
|
[metadata]
|
||||||
name = spam
|
name = spam
|
||||||
version = 0.1.0
|
version = 0.1.0
|
||||||
|
|
||||||
{{ setup_cfg_add }}
|
{{ setup_cfg_add }}
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
def new_c_project(
|
def new_c_project(
|
||||||
*,
|
*,
|
||||||
spam_c_top_level_add='',
|
spam_c_top_level_add="",
|
||||||
spam_c_function_add='',
|
spam_c_function_add="",
|
||||||
setup_py_add='',
|
setup_py_add="",
|
||||||
setup_py_setup_args_add='',
|
setup_py_setup_args_add="",
|
||||||
setup_cfg_add='',
|
setup_cfg_add="",
|
||||||
):
|
):
|
||||||
project = TestProject()
|
project = TestProject()
|
||||||
|
|
||||||
project.files.update(
|
project.files.update(
|
||||||
{
|
{
|
||||||
'spam.c': jinja2.Template(SPAM_C_TEMPLATE),
|
"spam.c": jinja2.Template(SPAM_C_TEMPLATE),
|
||||||
'setup.py': jinja2.Template(SETUP_PY_TEMPLATE),
|
"setup.py": jinja2.Template(SETUP_PY_TEMPLATE),
|
||||||
'setup.cfg': jinja2.Template(SETUP_CFG_TEMPLATE),
|
"setup.cfg": jinja2.Template(SETUP_CFG_TEMPLATE),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
project.template_context.update(
|
project.template_context.update(
|
||||||
{
|
{
|
||||||
'spam_c_top_level_add': spam_c_top_level_add,
|
"spam_c_top_level_add": spam_c_top_level_add,
|
||||||
'spam_c_function_add': spam_c_function_add,
|
"spam_c_function_add": spam_c_function_add,
|
||||||
'setup_py_add': setup_py_add,
|
"setup_py_add": setup_py_add,
|
||||||
'setup_py_setup_args_add': setup_py_setup_args_add,
|
"setup_py_setup_args_add": setup_py_setup_args_add,
|
||||||
'setup_cfg_add': setup_cfg_add,
|
"setup_cfg_add": setup_cfg_add,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -7,8 +7,8 @@ from . import utils
|
|||||||
|
|
||||||
pure_python_project = test_projects.TestProject()
|
pure_python_project = test_projects.TestProject()
|
||||||
pure_python_project.files[
|
pure_python_project.files[
|
||||||
'setup.py'
|
"setup.py"
|
||||||
] = '''
|
] = """
|
||||||
from setuptools import Extension, setup
|
from setuptools import Extension, setup
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
@@ -16,27 +16,27 @@ setup(
|
|||||||
py_modules=['spam'],
|
py_modules=['spam'],
|
||||||
version="0.1.0",
|
version="0.1.0",
|
||||||
)
|
)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
pure_python_project.files[
|
pure_python_project.files[
|
||||||
'spam.py'
|
"spam.py"
|
||||||
] = '''
|
] = """
|
||||||
def a_function():
|
def a_function():
|
||||||
pass
|
pass
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test(tmp_path, capfd):
|
def test(tmp_path, capfd):
|
||||||
# this test checks that if a pure wheel is generated, the build should
|
# this test checks that if a pure wheel is generated, the build should
|
||||||
# fail.
|
# fail.
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
pure_python_project.generate(project_dir)
|
pure_python_project.generate(project_dir)
|
||||||
|
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||||
print('produced wheels:', actual_wheels)
|
print("produced wheels:", actual_wheels)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
print('out', captured.out)
|
print("out", captured.out)
|
||||||
print('err', captured.err)
|
print("err", captured.err)
|
||||||
assert "Build failed because a pure Python wheel was generated" in captured.err
|
assert "Build failed because a pure Python wheel was generated" in captured.err
|
||||||
|
|||||||
+4
-4
@@ -4,7 +4,7 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
project_with_ssl_tests = test_projects.new_c_project(
|
project_with_ssl_tests = test_projects.new_c_project(
|
||||||
setup_py_add=textwrap.dedent(
|
setup_py_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
import ssl
|
import ssl
|
||||||
|
|
||||||
if sys.version_info[0] == 2:
|
if sys.version_info[0] == 2:
|
||||||
@@ -16,7 +16,7 @@ project_with_ssl_tests = test_projects.new_c_project(
|
|||||||
data = urlopen("https://www.nist.gov", context=context)
|
data = urlopen("https://www.nist.gov", context=context)
|
||||||
data = urlopen("https://raw.githubusercontent.com/joerick/cibuildwheel/master/CI.md", context=context)
|
data = urlopen("https://raw.githubusercontent.com/joerick/cibuildwheel/master/CI.md", context=context)
|
||||||
data = urlopen("https://raw.githubusercontent.com/joerick/cibuildwheel/master/CI.md")
|
data = urlopen("https://raw.githubusercontent.com/joerick/cibuildwheel/master/CI.md")
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,10 +24,10 @@ project_with_ssl_tests = test_projects.new_c_project(
|
|||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
# this test checks that SSL is working in the build environment using
|
# this test checks that SSL is working in the build environment using
|
||||||
# some checks in setup.py.
|
# some checks in setup.py.
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_ssl_tests.generate(project_dir)
|
project_with_ssl_tests.generate(project_dir)
|
||||||
|
|
||||||
actual_wheels = utils.cibuildwheel_run(project_dir)
|
actual_wheels = utils.cibuildwheel_run(project_dir)
|
||||||
|
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
+18
-18
@@ -8,13 +8,13 @@ from .test_projects.c import SPAM_C_TEMPLATE
|
|||||||
|
|
||||||
subdir_package_project = TestProject()
|
subdir_package_project = TestProject()
|
||||||
|
|
||||||
subdir_package_project.files['src/spam/spam.c'] = jinja2.Template(SPAM_C_TEMPLATE)
|
subdir_package_project.files["src/spam/spam.c"] = jinja2.Template(SPAM_C_TEMPLATE)
|
||||||
subdir_package_project.template_context['spam_c_top_level_add'] = ''
|
subdir_package_project.template_context["spam_c_top_level_add"] = ""
|
||||||
subdir_package_project.template_context['spam_c_function_add'] = ''
|
subdir_package_project.template_context["spam_c_function_add"] = ""
|
||||||
|
|
||||||
subdir_package_project.files[
|
subdir_package_project.files[
|
||||||
'src/spam/setup.py'
|
"src/spam/setup.py"
|
||||||
] = r'''
|
] = r"""
|
||||||
from setuptools import Extension, setup
|
from setuptools import Extension, setup
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
@@ -22,40 +22,40 @@ setup(
|
|||||||
ext_modules=[Extension('spam', sources=['spam.c'])],
|
ext_modules=[Extension('spam', sources=['spam.c'])],
|
||||||
version="0.1.0",
|
version="0.1.0",
|
||||||
)
|
)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
subdir_package_project.files[
|
subdir_package_project.files[
|
||||||
'src/spam/test/run_tests.py'
|
"src/spam/test/run_tests.py"
|
||||||
] = r'''
|
] = r"""
|
||||||
print('run_tests.py executed!')
|
print('run_tests.py executed!')
|
||||||
'''
|
"""
|
||||||
|
|
||||||
subdir_package_project.files[
|
subdir_package_project.files[
|
||||||
'bin/before_build.py'
|
"bin/before_build.py"
|
||||||
] = r'''
|
] = r"""
|
||||||
print('before_build.py executed!')
|
print('before_build.py executed!')
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test(capfd, tmp_path):
|
def test(capfd, tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
subdir_package_project.generate(project_dir)
|
subdir_package_project.generate(project_dir)
|
||||||
|
|
||||||
package_dir = Path('src', 'spam')
|
package_dir = Path("src", "spam")
|
||||||
# build the wheels
|
# build the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
package_dir=package_dir,
|
package_dir=package_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_BEFORE_BUILD': 'python {project}/bin/before_build.py',
|
"CIBW_BEFORE_BUILD": "python {project}/bin/before_build.py",
|
||||||
'CIBW_TEST_COMMAND': 'python {package}/test/run_tests.py',
|
"CIBW_TEST_COMMAND": "python {package}/test/run_tests.py",
|
||||||
# this shouldn't depend on the version of python, so build only CPython 3.6
|
# this shouldn't depend on the version of python, so build only CPython 3.6
|
||||||
'CIBW_BUILD': 'cp36-*',
|
"CIBW_BUILD": "cp36-*",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = [w for w in utils.expected_wheels('spam', '0.1.0') if 'cp36' in w]
|
expected_wheels = [w for w in utils.expected_wheels("spam", "0.1.0") if "cp36" in w]
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
|
|||||||
+22
-22
@@ -8,15 +8,15 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
project_with_a_test = test_projects.new_c_project(
|
project_with_a_test = test_projects.new_c_project(
|
||||||
setup_cfg_add=textwrap.dedent(
|
setup_cfg_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
[options.extras_require]
|
[options.extras_require]
|
||||||
test = nose
|
test = nose
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
project_with_a_test.files[
|
project_with_a_test.files[
|
||||||
'test/spam_test.py'
|
"test/spam_test.py"
|
||||||
] = r'''
|
] = r'''
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
@@ -75,63 +75,63 @@ class TestSpam(TestCase):
|
|||||||
|
|
||||||
|
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_a_test.generate(project_dir)
|
project_with_a_test.generate(project_dir)
|
||||||
|
|
||||||
# build and test the wheels
|
# build and test the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_TEST_REQUIRES': 'nose',
|
"CIBW_TEST_REQUIRES": "nose",
|
||||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||||
# mac/linux.
|
# mac/linux.
|
||||||
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
"CIBW_TEST_COMMAND": "false || nosetests {project}/test",
|
||||||
'CIBW_TEST_COMMAND_WINDOWS': 'COLOR 00 || nosetests {project}/test',
|
"CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || nosetests {project}/test",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
def test_extras_require(tmp_path):
|
def test_extras_require(tmp_path):
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_a_test.generate(project_dir)
|
project_with_a_test.generate(project_dir)
|
||||||
|
|
||||||
# build and test the wheels
|
# build and test the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir,
|
project_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_TEST_EXTRAS': 'test',
|
"CIBW_TEST_EXTRAS": "test",
|
||||||
# the 'false ||' bit is to ensure this command runs in a shell on
|
# the 'false ||' bit is to ensure this command runs in a shell on
|
||||||
# mac/linux.
|
# mac/linux.
|
||||||
'CIBW_TEST_COMMAND': 'false || nosetests {project}/test',
|
"CIBW_TEST_COMMAND": "false || nosetests {project}/test",
|
||||||
'CIBW_TEST_COMMAND_WINDOWS': 'COLOR 00 || nosetests {project}/test',
|
"CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || nosetests {project}/test",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# also check that we got the right wheels
|
# also check that we got the right wheels
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|
||||||
|
|
||||||
project_with_a_failing_test = test_projects.new_c_project()
|
project_with_a_failing_test = test_projects.new_c_project()
|
||||||
project_with_a_failing_test.files[
|
project_with_a_failing_test.files[
|
||||||
'test/spam_test.py'
|
"test/spam_test.py"
|
||||||
] = r'''
|
] = r"""
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
class TestSpam(TestCase):
|
class TestSpam(TestCase):
|
||||||
def test_something(self):
|
def test_something(self):
|
||||||
self.fail('this test is supposed to fail')
|
self.fail('this test is supposed to fail')
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test_failing_test(tmp_path):
|
def test_failing_test(tmp_path):
|
||||||
"""Ensure a failing test causes cibuildwheel to error out and exit"""
|
"""Ensure a failing test causes cibuildwheel to error out and exit"""
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
output_dir = tmp_path / 'output'
|
output_dir = tmp_path / "output"
|
||||||
project_with_a_failing_test.generate(project_dir)
|
project_with_a_failing_test.generate(project_dir)
|
||||||
|
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
@@ -139,12 +139,12 @@ def test_failing_test(tmp_path):
|
|||||||
project_dir,
|
project_dir,
|
||||||
output_dir=output_dir,
|
output_dir=output_dir,
|
||||||
add_env={
|
add_env={
|
||||||
'CIBW_TEST_REQUIRES': 'nose',
|
"CIBW_TEST_REQUIRES": "nose",
|
||||||
'CIBW_TEST_COMMAND': 'nosetests {project}/test',
|
"CIBW_TEST_COMMAND": "nosetests {project}/test",
|
||||||
# manylinux1 has a version of bash that's been shown to have
|
# manylinux1 has a version of bash that's been shown to have
|
||||||
# problems with this, so let's check that.
|
# problems with this, so let's check that.
|
||||||
'CIBW_MANYLINUX_I686_IMAGE': 'manylinux1',
|
"CIBW_MANYLINUX_I686_IMAGE": "manylinux1",
|
||||||
'CIBW_MANYLINUX_X86_64_IMAGE': 'manylinux1',
|
"CIBW_MANYLINUX_X86_64_IMAGE": "manylinux1",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,26 +7,26 @@ from .test_projects import TestProject
|
|||||||
|
|
||||||
so_file_project = TestProject()
|
so_file_project = TestProject()
|
||||||
|
|
||||||
so_file_project.files['libnothing.so'] = ''
|
so_file_project.files["libnothing.so"] = ""
|
||||||
|
|
||||||
so_file_project.files[
|
so_file_project.files[
|
||||||
'setup.py'
|
"setup.py"
|
||||||
] = '''
|
] = """
|
||||||
raise Exception('this build will fail')
|
raise Exception('this build will fail')
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test_failed_project_with_so_files(tmp_path, capfd):
|
def test_failed_project_with_so_files(tmp_path, capfd):
|
||||||
if utils.platform != 'linux':
|
if utils.platform != "linux":
|
||||||
pytest.skip('this test is only relevant to the linux build')
|
pytest.skip("this test is only relevant to the linux build")
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
so_file_project.generate(project_dir)
|
so_file_project.generate(project_dir)
|
||||||
|
|
||||||
with pytest.raises(subprocess.CalledProcessError):
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
utils.cibuildwheel_run(project_dir)
|
utils.cibuildwheel_run(project_dir)
|
||||||
|
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
print('out', captured.out)
|
print("out", captured.out)
|
||||||
print('err', captured.err)
|
print("err", captured.err)
|
||||||
assert "NOTE: Shared object (.so) files found in this project." in captured.err
|
assert "NOTE: Shared object (.so) files found in this project." in captured.err
|
||||||
|
|||||||
@@ -6,27 +6,27 @@ from . import test_projects, utils
|
|||||||
|
|
||||||
project_with_unicode = test_projects.new_c_project(
|
project_with_unicode = test_projects.new_c_project(
|
||||||
spam_c_function_add=textwrap.dedent(
|
spam_c_function_add=textwrap.dedent(
|
||||||
r'''
|
r"""
|
||||||
{
|
{
|
||||||
Py_XDECREF(PyUnicode_FromStringAndSize("foo", 4));
|
Py_XDECREF(PyUnicode_FromStringAndSize("foo", 4));
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
if utils.platform != 'linux':
|
if utils.platform != "linux":
|
||||||
pytest.skip('the docker test is only relevant to the linux build')
|
pytest.skip("the docker test is only relevant to the linux build")
|
||||||
|
|
||||||
project_dir = tmp_path / 'project'
|
project_dir = tmp_path / "project"
|
||||||
project_with_unicode.generate(project_dir)
|
project_with_unicode.generate(project_dir)
|
||||||
|
|
||||||
# build the wheels
|
# build the wheels
|
||||||
actual_wheels = utils.cibuildwheel_run(
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
project_dir, add_env={'CIBW_TEST_COMMAND': 'python -c "import spam"'}
|
project_dir, add_env={"CIBW_TEST_COMMAND": 'python -c "import spam"'}
|
||||||
)
|
)
|
||||||
|
|
||||||
# check that the expected wheels are produced
|
# check that the expected wheels are produced
|
||||||
expected_wheels = utils.expected_wheels('spam', '0.1.0')
|
expected_wheels = utils.expected_wheels("spam", "0.1.0")
|
||||||
assert set(actual_wheels) == set(expected_wheels)
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
|
|||||||
+58
-58
@@ -1,8 +1,8 @@
|
|||||||
'''
|
"""
|
||||||
Utility functions used by the cibuildwheel tests.
|
Utility functions used by the cibuildwheel tests.
|
||||||
|
|
||||||
This file is added to the PYTHONPATH in the test runner at bin/run_test.py.
|
This file is added to the PYTHONPATH in the test runner at bin/run_test.py.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import platform as pm
|
import platform as pm
|
||||||
@@ -14,16 +14,16 @@ from tempfile import mkdtemp
|
|||||||
|
|
||||||
platform: str
|
platform: str
|
||||||
|
|
||||||
if 'CIBW_PLATFORM' in os.environ:
|
if "CIBW_PLATFORM" in os.environ:
|
||||||
platform = os.environ['CIBW_PLATFORM']
|
platform = os.environ["CIBW_PLATFORM"]
|
||||||
elif sys.platform.startswith('linux'):
|
elif sys.platform.startswith("linux"):
|
||||||
platform = 'linux'
|
platform = "linux"
|
||||||
elif sys.platform.startswith('darwin'):
|
elif sys.platform.startswith("darwin"):
|
||||||
platform = 'macos'
|
platform = "macos"
|
||||||
elif sys.platform in ['win32', 'cygwin']:
|
elif sys.platform in ["win32", "cygwin"]:
|
||||||
platform = 'windows'
|
platform = "windows"
|
||||||
else:
|
else:
|
||||||
raise Exception('Unsupported platform')
|
raise Exception("Unsupported platform")
|
||||||
|
|
||||||
|
|
||||||
# Python 2 does not have a tempfile.TemporaryDirectory context manager
|
# Python 2 does not have a tempfile.TemporaryDirectory context manager
|
||||||
@@ -38,23 +38,23 @@ def TemporaryDirectoryIfNone(path):
|
|||||||
|
|
||||||
|
|
||||||
def cibuildwheel_get_build_identifiers(project_path, env=None):
|
def cibuildwheel_get_build_identifiers(project_path, env=None):
|
||||||
'''
|
"""
|
||||||
Returns the list of build identifiers that cibuildwheel will try to build
|
Returns the list of build identifiers that cibuildwheel will try to build
|
||||||
for the current platform.
|
for the current platform.
|
||||||
'''
|
"""
|
||||||
cmd_output = subprocess.run(
|
cmd_output = subprocess.run(
|
||||||
[sys.executable, '-m', 'cibuildwheel', '--print-build-identifiers', str(project_path)],
|
[sys.executable, "-m", "cibuildwheel", "--print-build-identifiers", str(project_path)],
|
||||||
universal_newlines=True,
|
universal_newlines=True,
|
||||||
env=env,
|
env=env,
|
||||||
check=True,
|
check=True,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
).stdout
|
).stdout
|
||||||
|
|
||||||
return cmd_output.strip().split('\n')
|
return cmd_output.strip().split("\n")
|
||||||
|
|
||||||
|
|
||||||
def cibuildwheel_run(project_path, package_dir='.', env=None, add_env=None, output_dir=None):
|
def cibuildwheel_run(project_path, package_dir=".", env=None, add_env=None, output_dir=None):
|
||||||
'''
|
"""
|
||||||
Runs cibuildwheel as a subprocess, building the project at project_path.
|
Runs cibuildwheel as a subprocess, building the project at project_path.
|
||||||
|
|
||||||
Uses the current Python interpreter.
|
Uses the current Python interpreter.
|
||||||
@@ -67,11 +67,11 @@ def cibuildwheel_run(project_path, package_dir='.', env=None, add_env=None, outp
|
|||||||
:param output_dir: directory where wheels are saved. If None, a temporary
|
:param output_dir: directory where wheels are saved. If None, a temporary
|
||||||
directory will be used for the duration of the command.
|
directory will be used for the duration of the command.
|
||||||
:return: list of built wheels (file names).
|
:return: list of built wheels (file names).
|
||||||
'''
|
"""
|
||||||
if env is None:
|
if env is None:
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
# If present in the host environment, remove the MACOSX_DEPLOYMENT_TARGET for consistency
|
# If present in the host environment, remove the MACOSX_DEPLOYMENT_TARGET for consistency
|
||||||
env.pop('MACOSX_DEPLOYMENT_TARGET', None)
|
env.pop("MACOSX_DEPLOYMENT_TARGET", None)
|
||||||
|
|
||||||
if add_env is not None:
|
if add_env is not None:
|
||||||
env.update(add_env)
|
env.update(add_env)
|
||||||
@@ -80,9 +80,9 @@ def cibuildwheel_run(project_path, package_dir='.', env=None, add_env=None, outp
|
|||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
sys.executable,
|
sys.executable,
|
||||||
'-m',
|
"-m",
|
||||||
'cibuildwheel',
|
"cibuildwheel",
|
||||||
'--output-dir',
|
"--output-dir",
|
||||||
str(_output_dir),
|
str(_output_dir),
|
||||||
str(package_dir),
|
str(package_dir),
|
||||||
],
|
],
|
||||||
@@ -95,14 +95,14 @@ def cibuildwheel_run(project_path, package_dir='.', env=None, add_env=None, outp
|
|||||||
|
|
||||||
|
|
||||||
def _get_arm64_macosx_deployment_target(macosx_deployment_target: str) -> str:
|
def _get_arm64_macosx_deployment_target(macosx_deployment_target: str) -> str:
|
||||||
'''
|
"""
|
||||||
The first version of macOS that supports arm is 11.0. So the wheel tag
|
The first version of macOS that supports arm is 11.0. So the wheel tag
|
||||||
cannot contain an earlier deployment target, even if
|
cannot contain an earlier deployment target, even if
|
||||||
MACOSX_DEPLOYMENT_TARGET sets it.
|
MACOSX_DEPLOYMENT_TARGET sets it.
|
||||||
'''
|
"""
|
||||||
version_tuple = tuple(map(int, macosx_deployment_target.split('.')))
|
version_tuple = tuple(map(int, macosx_deployment_target.split(".")))
|
||||||
if version_tuple <= (11, 0):
|
if version_tuple <= (11, 0):
|
||||||
return '11.0'
|
return "11.0"
|
||||||
else:
|
else:
|
||||||
return macosx_deployment_target
|
return macosx_deployment_target
|
||||||
|
|
||||||
@@ -111,14 +111,14 @@ def expected_wheels(
|
|||||||
package_name,
|
package_name,
|
||||||
package_version,
|
package_version,
|
||||||
manylinux_versions=None,
|
manylinux_versions=None,
|
||||||
macosx_deployment_target='10.9',
|
macosx_deployment_target="10.9",
|
||||||
machine_arch=None,
|
machine_arch=None,
|
||||||
*,
|
*,
|
||||||
exclude_27=platform == 'windows',
|
exclude_27=platform == "windows",
|
||||||
):
|
):
|
||||||
'''
|
"""
|
||||||
Returns a list of expected wheels from a run of cibuildwheel.
|
Returns a list of expected wheels from a run of cibuildwheel.
|
||||||
'''
|
"""
|
||||||
# per PEP 425 (https://www.python.org/dev/peps/pep-0425/), wheel files shall have name of the form
|
# per PEP 425 (https://www.python.org/dev/peps/pep-0425/), wheel files shall have name of the form
|
||||||
# {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
|
# {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
|
||||||
# {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
|
# {python tag} and {abi tag} are closely related to the python interpreter used to build the wheel
|
||||||
@@ -128,55 +128,55 @@ def expected_wheels(
|
|||||||
machine_arch = pm.machine()
|
machine_arch = pm.machine()
|
||||||
|
|
||||||
if manylinux_versions is None:
|
if manylinux_versions is None:
|
||||||
if machine_arch == 'x86_64':
|
if machine_arch == "x86_64":
|
||||||
manylinux_versions = ['manylinux1', 'manylinux2010']
|
manylinux_versions = ["manylinux1", "manylinux2010"]
|
||||||
else:
|
else:
|
||||||
manylinux_versions = ['manylinux2014']
|
manylinux_versions = ["manylinux2014"]
|
||||||
|
|
||||||
python_abi_tags = ['cp35-cp35m', 'cp36-cp36m', 'cp37-cp37m', 'cp38-cp38', 'cp39-cp39']
|
python_abi_tags = ["cp35-cp35m", "cp36-cp36m", "cp37-cp37m", "cp38-cp38", "cp39-cp39"]
|
||||||
|
|
||||||
if machine_arch in ['x86_64', 'AMD64', 'x86']:
|
if machine_arch in ["x86_64", "AMD64", "x86"]:
|
||||||
python_abi_tags += ['cp27-cp27m', 'pp27-pypy_73', 'pp36-pypy36_pp73', 'pp37-pypy37_pp73']
|
python_abi_tags += ["cp27-cp27m", "pp27-pypy_73", "pp36-pypy36_pp73", "pp37-pypy37_pp73"]
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
python_abi_tags.append('cp27-cp27mu') # python 2.7 has 2 different ABI on manylinux
|
python_abi_tags.append("cp27-cp27mu") # python 2.7 has 2 different ABI on manylinux
|
||||||
|
|
||||||
if platform == 'macos' and get_macos_version() >= (10, 16):
|
if platform == "macos" and get_macos_version() >= (10, 16):
|
||||||
# 10.16 is sometimes reported as the macOS version on macOS 11.
|
# 10.16 is sometimes reported as the macOS version on macOS 11.
|
||||||
# CPython 3.5 doesn't work on macOS 11.
|
# CPython 3.5 doesn't work on macOS 11.
|
||||||
python_abi_tags.remove('cp35-cp35m')
|
python_abi_tags.remove("cp35-cp35m")
|
||||||
# pypy not supported on macOS 11.
|
# pypy not supported on macOS 11.
|
||||||
python_abi_tags = [t for t in python_abi_tags if not t.startswith('pp')]
|
python_abi_tags = [t for t in python_abi_tags if not t.startswith("pp")]
|
||||||
|
|
||||||
if platform == 'macos' and machine_arch == 'arm64':
|
if platform == "macos" and machine_arch == "arm64":
|
||||||
# currently, arm64 macs are only supported by cp39
|
# currently, arm64 macs are only supported by cp39
|
||||||
python_abi_tags = ['cp39-cp39']
|
python_abi_tags = ["cp39-cp39"]
|
||||||
|
|
||||||
wheels = []
|
wheels = []
|
||||||
|
|
||||||
for python_abi_tag in python_abi_tags:
|
for python_abi_tag in python_abi_tags:
|
||||||
platform_tags = []
|
platform_tags = []
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
architectures = [machine_arch]
|
architectures = [machine_arch]
|
||||||
|
|
||||||
if machine_arch == 'x86_64' and python_abi_tag.startswith('cp'):
|
if machine_arch == "x86_64" and python_abi_tag.startswith("cp"):
|
||||||
architectures.append('i686')
|
architectures.append("i686")
|
||||||
|
|
||||||
platform_tags = [
|
platform_tags = [
|
||||||
f'{manylinux_version}_{architecture}'
|
f"{manylinux_version}_{architecture}"
|
||||||
for architecture in architectures
|
for architecture in architectures
|
||||||
for manylinux_version in manylinux_versions
|
for manylinux_version in manylinux_versions
|
||||||
]
|
]
|
||||||
|
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
if python_abi_tag.startswith('cp'):
|
if python_abi_tag.startswith("cp"):
|
||||||
platform_tags = ['win32', 'win_amd64']
|
platform_tags = ["win32", "win_amd64"]
|
||||||
else:
|
else:
|
||||||
platform_tags = ['win32']
|
platform_tags = ["win32"]
|
||||||
|
|
||||||
elif platform == 'macos':
|
elif platform == "macos":
|
||||||
if python_abi_tag == 'cp39-cp39' and machine_arch == 'arm64':
|
if python_abi_tag == "cp39-cp39" and machine_arch == "arm64":
|
||||||
arm64_macosx_deployment_target = _get_arm64_macosx_deployment_target(
|
arm64_macosx_deployment_target = _get_arm64_macosx_deployment_target(
|
||||||
macosx_deployment_target
|
macosx_deployment_target
|
||||||
)
|
)
|
||||||
@@ -190,26 +190,26 @@ def expected_wheels(
|
|||||||
]
|
]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception('unsupported platform')
|
raise Exception("unsupported platform")
|
||||||
|
|
||||||
for platform_tag in platform_tags:
|
for platform_tag in platform_tags:
|
||||||
wheels.append(f'{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl')
|
wheels.append(f"{package_name}-{package_version}-{python_abi_tag}-{platform_tag}.whl")
|
||||||
|
|
||||||
# Travis on Windows does not support using the default Python 2.7 compiler,
|
# Travis on Windows does not support using the default Python 2.7 compiler,
|
||||||
# so we support skipping here.
|
# so we support skipping here.
|
||||||
if exclude_27:
|
if exclude_27:
|
||||||
wheels = [w for w in wheels if '-cp27-' not in w and '-pp2' not in w]
|
wheels = [w for w in wheels if "-cp27-" not in w and "-pp2" not in w]
|
||||||
|
|
||||||
return wheels
|
return wheels
|
||||||
|
|
||||||
|
|
||||||
def get_macos_version():
|
def get_macos_version():
|
||||||
'''
|
"""
|
||||||
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
|
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
|
||||||
|
|
||||||
These tuples can be used in comparisons, e.g.
|
These tuples can be used in comparisons, e.g.
|
||||||
(10, 14) <= (11, 0) == True
|
(10, 14) <= (11, 0) == True
|
||||||
(11, 2) <= (11, 0) != True
|
(11, 2) <= (11, 0) != True
|
||||||
'''
|
"""
|
||||||
version_str, _, _ = pm.mac_ver()
|
version_str, _, _ = pm.mac_ver()
|
||||||
return tuple(map(int, version_str.split(".")[:2]))
|
return tuple(map(int, version_str.split(".")[:2]))
|
||||||
|
|||||||
@@ -6,21 +6,21 @@ from cibuildwheel.util import BuildSelector
|
|||||||
def test_build():
|
def test_build():
|
||||||
build_selector = BuildSelector(build_config="cp3?-* *-manylinux1*", skip_config="")
|
build_selector = BuildSelector(build_config="cp3?-* *-manylinux1*", skip_config="")
|
||||||
|
|
||||||
assert build_selector('cp27-manylinux1_x86_64')
|
assert build_selector("cp27-manylinux1_x86_64")
|
||||||
assert build_selector('cp36-manylinux1_x86_64')
|
assert build_selector("cp36-manylinux1_x86_64")
|
||||||
assert build_selector('cp37-manylinux1_x86_64')
|
assert build_selector("cp37-manylinux1_x86_64")
|
||||||
assert build_selector('cp27-manylinux1_i686')
|
assert build_selector("cp27-manylinux1_i686")
|
||||||
assert build_selector('cp36-manylinux1_i686')
|
assert build_selector("cp36-manylinux1_i686")
|
||||||
assert build_selector('cp37-manylinux1_i686')
|
assert build_selector("cp37-manylinux1_i686")
|
||||||
assert not build_selector('cp27-macosx_10_6_intel')
|
assert not build_selector("cp27-macosx_10_6_intel")
|
||||||
assert build_selector('cp36-macosx_10_6_intel')
|
assert build_selector("cp36-macosx_10_6_intel")
|
||||||
assert build_selector('cp37-macosx_10_6_intel')
|
assert build_selector("cp37-macosx_10_6_intel")
|
||||||
assert not build_selector('cp27-win32')
|
assert not build_selector("cp27-win32")
|
||||||
assert build_selector('cp36-win32')
|
assert build_selector("cp36-win32")
|
||||||
assert build_selector('cp37-win32')
|
assert build_selector("cp37-win32")
|
||||||
assert not build_selector('cp27-win_amd64')
|
assert not build_selector("cp27-win_amd64")
|
||||||
assert build_selector('cp36-win_amd64')
|
assert build_selector("cp36-win_amd64")
|
||||||
assert build_selector('cp37-win_amd64')
|
assert build_selector("cp37-win_amd64")
|
||||||
|
|
||||||
|
|
||||||
def test_skip():
|
def test_skip():
|
||||||
@@ -28,21 +28,21 @@ def test_skip():
|
|||||||
build_config="*", skip_config="cp27-* cp3?-manylinux1_i686 cp36-win* *-win32"
|
build_config="*", skip_config="cp27-* cp3?-manylinux1_i686 cp36-win* *-win32"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not build_selector('cp27-manylinux1_x86_64')
|
assert not build_selector("cp27-manylinux1_x86_64")
|
||||||
assert build_selector('cp36-manylinux1_x86_64')
|
assert build_selector("cp36-manylinux1_x86_64")
|
||||||
assert build_selector('cp37-manylinux1_x86_64')
|
assert build_selector("cp37-manylinux1_x86_64")
|
||||||
assert not build_selector('cp27-manylinux1_i686')
|
assert not build_selector("cp27-manylinux1_i686")
|
||||||
assert not build_selector('cp36-manylinux1_i686')
|
assert not build_selector("cp36-manylinux1_i686")
|
||||||
assert not build_selector('cp37-manylinux1_i686')
|
assert not build_selector("cp37-manylinux1_i686")
|
||||||
assert not build_selector('cp27-macosx_10_6_intel')
|
assert not build_selector("cp27-macosx_10_6_intel")
|
||||||
assert build_selector('cp36-macosx_10_6_intel')
|
assert build_selector("cp36-macosx_10_6_intel")
|
||||||
assert build_selector('cp37-macosx_10_6_intel')
|
assert build_selector("cp37-macosx_10_6_intel")
|
||||||
assert not build_selector('cp27-win32')
|
assert not build_selector("cp27-win32")
|
||||||
assert not build_selector('cp36-win32')
|
assert not build_selector("cp36-win32")
|
||||||
assert not build_selector('cp37-win32')
|
assert not build_selector("cp37-win32")
|
||||||
assert not build_selector('cp27-win_amd64')
|
assert not build_selector("cp27-win_amd64")
|
||||||
assert not build_selector('cp36-win_amd64')
|
assert not build_selector("cp36-win_amd64")
|
||||||
assert build_selector('cp37-win_amd64')
|
assert build_selector("cp37-win_amd64")
|
||||||
|
|
||||||
|
|
||||||
def test_build_and_skip():
|
def test_build_and_skip():
|
||||||
@@ -50,31 +50,31 @@ def test_build_and_skip():
|
|||||||
build_config="cp36-* cp37-macosx* *-manylinux1*", skip_config="cp27-* cp37-manylinux1_i686"
|
build_config="cp36-* cp37-macosx* *-manylinux1*", skip_config="cp27-* cp37-manylinux1_i686"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not build_selector('cp27-manylinux1_x86_64')
|
assert not build_selector("cp27-manylinux1_x86_64")
|
||||||
assert build_selector('cp36-manylinux1_x86_64')
|
assert build_selector("cp36-manylinux1_x86_64")
|
||||||
assert build_selector('cp37-manylinux1_x86_64')
|
assert build_selector("cp37-manylinux1_x86_64")
|
||||||
assert not build_selector('cp27-manylinux1_i686')
|
assert not build_selector("cp27-manylinux1_i686")
|
||||||
assert build_selector('cp36-manylinux1_i686')
|
assert build_selector("cp36-manylinux1_i686")
|
||||||
assert not build_selector('cp37-manylinux1_i686')
|
assert not build_selector("cp37-manylinux1_i686")
|
||||||
assert not build_selector('cp27-macosx_10_6_intel')
|
assert not build_selector("cp27-macosx_10_6_intel")
|
||||||
assert build_selector('cp36-macosx_10_6_intel')
|
assert build_selector("cp36-macosx_10_6_intel")
|
||||||
assert build_selector('cp37-macosx_10_6_intel')
|
assert build_selector("cp37-macosx_10_6_intel")
|
||||||
assert not build_selector('cp27-win32')
|
assert not build_selector("cp27-win32")
|
||||||
assert build_selector('cp36-win32')
|
assert build_selector("cp36-win32")
|
||||||
assert not build_selector('cp37-win32')
|
assert not build_selector("cp37-win32")
|
||||||
assert not build_selector('cp27-win_amd64')
|
assert not build_selector("cp27-win_amd64")
|
||||||
assert build_selector('cp36-win_amd64')
|
assert build_selector("cp36-win_amd64")
|
||||||
assert not build_selector('cp37-win_amd64')
|
assert not build_selector("cp37-win_amd64")
|
||||||
|
|
||||||
|
|
||||||
def test_build_braces():
|
def test_build_braces():
|
||||||
build_selector = BuildSelector(build_config="cp{36,37}*", skip_config="")
|
build_selector = BuildSelector(build_config="cp{36,37}*", skip_config="")
|
||||||
|
|
||||||
assert not build_selector('cp27-manylinux1_x86_64')
|
assert not build_selector("cp27-manylinux1_x86_64")
|
||||||
assert build_selector('cp36-manylinux1_x86_64')
|
assert build_selector("cp36-manylinux1_x86_64")
|
||||||
assert build_selector('cp37-manylinux1_x86_64')
|
assert build_selector("cp37-manylinux1_x86_64")
|
||||||
assert not build_selector('cp38-manylinux1_x86_64')
|
assert not build_selector("cp38-manylinux1_x86_64")
|
||||||
assert not build_selector('cp39-manylinux1_x86_64')
|
assert not build_selector("cp39-manylinux1_x86_64")
|
||||||
|
|
||||||
|
|
||||||
def test_build_limited_python():
|
def test_build_limited_python():
|
||||||
@@ -82,18 +82,18 @@ def test_build_limited_python():
|
|||||||
build_config="*", skip_config="", requires_python=SpecifierSet(">=3.6")
|
build_config="*", skip_config="", requires_python=SpecifierSet(">=3.6")
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not build_selector('cp27-manylinux1_x86_64')
|
assert not build_selector("cp27-manylinux1_x86_64")
|
||||||
assert build_selector('cp36-manylinux1_x86_64')
|
assert build_selector("cp36-manylinux1_x86_64")
|
||||||
assert build_selector('cp37-manylinux1_x86_64')
|
assert build_selector("cp37-manylinux1_x86_64")
|
||||||
assert not build_selector('cp27-manylinux1_i686')
|
assert not build_selector("cp27-manylinux1_i686")
|
||||||
assert build_selector('cp36-manylinux1_i686')
|
assert build_selector("cp36-manylinux1_i686")
|
||||||
assert build_selector('cp37-manylinux1_i686')
|
assert build_selector("cp37-manylinux1_i686")
|
||||||
assert not build_selector('cp27-win32')
|
assert not build_selector("cp27-win32")
|
||||||
assert build_selector('cp36-win32')
|
assert build_selector("cp36-win32")
|
||||||
assert build_selector('cp37-win32')
|
assert build_selector("cp37-win32")
|
||||||
assert not build_selector('pp27-win32')
|
assert not build_selector("pp27-win32")
|
||||||
assert build_selector('pp36-win32')
|
assert build_selector("pp36-win32")
|
||||||
assert build_selector('pp37-win32')
|
assert build_selector("pp37-win32")
|
||||||
|
|
||||||
|
|
||||||
def test_build_limited_python_partial():
|
def test_build_limited_python_partial():
|
||||||
@@ -103,9 +103,9 @@ def test_build_limited_python_partial():
|
|||||||
requires_python=SpecifierSet(">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"),
|
requires_python=SpecifierSet(">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert build_selector('cp27-manylinux1_x86_64')
|
assert build_selector("cp27-manylinux1_x86_64")
|
||||||
assert not build_selector('cp35-manylinux1_x86_64')
|
assert not build_selector("cp35-manylinux1_x86_64")
|
||||||
assert build_selector('cp36-manylinux1_x86_64')
|
assert build_selector("cp36-manylinux1_x86_64")
|
||||||
|
|
||||||
|
|
||||||
def test_build_limited_python_patch():
|
def test_build_limited_python_patch():
|
||||||
@@ -113,6 +113,6 @@ def test_build_limited_python_patch():
|
|||||||
build_config="*", skip_config="", requires_python=SpecifierSet(">=2.7.9")
|
build_config="*", skip_config="", requires_python=SpecifierSet(">=2.7.9")
|
||||||
)
|
)
|
||||||
|
|
||||||
assert build_selector('cp27-manylinux1_x86_64')
|
assert build_selector("cp27-manylinux1_x86_64")
|
||||||
assert build_selector('cp36-manylinux1_x86_64')
|
assert build_selector("cp36-manylinux1_x86_64")
|
||||||
assert build_selector('cp37-manylinux1_x86_64')
|
assert build_selector("cp37-manylinux1_x86_64")
|
||||||
|
|||||||
@@ -7,21 +7,21 @@ def test_defaults():
|
|||||||
dependency_constraints = DependencyConstraints.with_defaults()
|
dependency_constraints = DependencyConstraints.with_defaults()
|
||||||
|
|
||||||
project_root = Path(__file__).parents[1]
|
project_root = Path(__file__).parents[1]
|
||||||
resources_dir = project_root / 'cibuildwheel' / 'resources'
|
resources_dir = project_root / "cibuildwheel" / "resources"
|
||||||
|
|
||||||
assert dependency_constraints.base_file_path.samefile(resources_dir / 'constraints.txt')
|
assert dependency_constraints.base_file_path.samefile(resources_dir / "constraints.txt")
|
||||||
assert dependency_constraints.get_for_python_version('3.99').samefile(
|
assert dependency_constraints.get_for_python_version("3.99").samefile(
|
||||||
resources_dir / 'constraints.txt'
|
resources_dir / "constraints.txt"
|
||||||
)
|
)
|
||||||
assert dependency_constraints.get_for_python_version('3.9').samefile(
|
assert dependency_constraints.get_for_python_version("3.9").samefile(
|
||||||
resources_dir / 'constraints-python39.txt'
|
resources_dir / "constraints-python39.txt"
|
||||||
)
|
)
|
||||||
assert dependency_constraints.get_for_python_version('3.6').samefile(
|
assert dependency_constraints.get_for_python_version("3.6").samefile(
|
||||||
resources_dir / 'constraints-python36.txt'
|
resources_dir / "constraints-python36.txt"
|
||||||
)
|
)
|
||||||
assert dependency_constraints.get_for_python_version('3.5').samefile(
|
assert dependency_constraints.get_for_python_version("3.5").samefile(
|
||||||
resources_dir / 'constraints-python35.txt'
|
resources_dir / "constraints-python35.txt"
|
||||||
)
|
)
|
||||||
assert dependency_constraints.get_for_python_version('2.7').samefile(
|
assert dependency_constraints.get_for_python_version("2.7").samefile(
|
||||||
resources_dir / 'constraints-python27.txt'
|
resources_dir / "constraints-python27.txt"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,25 +14,25 @@ from cibuildwheel.environment import EnvironmentAssignment
|
|||||||
# multi architectures and include python3.8
|
# multi architectures and include python3.8
|
||||||
pm = platform.machine()
|
pm = platform.machine()
|
||||||
if pm == "x86_64":
|
if pm == "x86_64":
|
||||||
DEFAULT_IMAGE = 'quay.io/pypa/manylinux2014_x86_64:2020-05-17-2f8ac3b'
|
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_x86_64:2020-05-17-2f8ac3b"
|
||||||
elif pm == "aarch64":
|
elif pm == "aarch64":
|
||||||
DEFAULT_IMAGE = 'quay.io/pypa/manylinux2014_aarch64:2020-05-17-2f8ac3b'
|
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_aarch64:2020-05-17-2f8ac3b"
|
||||||
elif pm == "ppc64le":
|
elif pm == "ppc64le":
|
||||||
DEFAULT_IMAGE = 'quay.io/pypa/manylinux2014_ppc64le:2020-05-17-2f8ac3b'
|
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_ppc64le:2020-05-17-2f8ac3b"
|
||||||
elif pm == "s390x":
|
elif pm == "s390x":
|
||||||
DEFAULT_IMAGE = 'quay.io/pypa/manylinux2014_s390x:2020-05-17-2f8ac3b'
|
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_s390x:2020-05-17-2f8ac3b"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.docker
|
@pytest.mark.docker
|
||||||
def test_simple():
|
def test_simple():
|
||||||
with DockerContainer(DEFAULT_IMAGE) as container:
|
with DockerContainer(DEFAULT_IMAGE) as container:
|
||||||
assert container.call(['echo', 'hello'], capture_output=True) == 'hello\n'
|
assert container.call(["echo", "hello"], capture_output=True) == "hello\n"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.docker
|
@pytest.mark.docker
|
||||||
def test_no_lf():
|
def test_no_lf():
|
||||||
with DockerContainer(DEFAULT_IMAGE) as container:
|
with DockerContainer(DEFAULT_IMAGE) as container:
|
||||||
assert container.call(['printf', 'hello'], capture_output=True) == 'hello'
|
assert container.call(["printf", "hello"], capture_output=True) == "hello"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.docker
|
@pytest.mark.docker
|
||||||
@@ -40,24 +40,24 @@ def test_environment():
|
|||||||
with DockerContainer(DEFAULT_IMAGE) as container:
|
with DockerContainer(DEFAULT_IMAGE) as container:
|
||||||
assert (
|
assert (
|
||||||
container.call(
|
container.call(
|
||||||
['sh', '-c', 'echo $TEST_VAR'], env={'TEST_VAR': '1'}, capture_output=True
|
["sh", "-c", "echo $TEST_VAR"], env={"TEST_VAR": "1"}, capture_output=True
|
||||||
)
|
)
|
||||||
== '1\n'
|
== "1\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.docker
|
@pytest.mark.docker
|
||||||
def test_cwd():
|
def test_cwd():
|
||||||
with DockerContainer(DEFAULT_IMAGE, cwd='/cibuildwheel/working_directory') as container:
|
with DockerContainer(DEFAULT_IMAGE, cwd="/cibuildwheel/working_directory") as container:
|
||||||
assert container.call(['pwd'], capture_output=True) == '/cibuildwheel/working_directory\n'
|
assert container.call(["pwd"], capture_output=True) == "/cibuildwheel/working_directory\n"
|
||||||
assert container.call(['pwd'], capture_output=True, cwd='/opt') == '/opt\n'
|
assert container.call(["pwd"], capture_output=True, cwd="/opt") == "/opt\n"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.docker
|
@pytest.mark.docker
|
||||||
def test_container_removed():
|
def test_container_removed():
|
||||||
with DockerContainer(DEFAULT_IMAGE) as container:
|
with DockerContainer(DEFAULT_IMAGE) as container:
|
||||||
docker_containers_listing = subprocess.run(
|
docker_containers_listing = subprocess.run(
|
||||||
'docker container ls',
|
"docker container ls",
|
||||||
shell=True,
|
shell=True,
|
||||||
check=True,
|
check=True,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
@@ -67,7 +67,7 @@ def test_container_removed():
|
|||||||
old_container_name = container.name
|
old_container_name = container.name
|
||||||
|
|
||||||
docker_containers_listing = subprocess.run(
|
docker_containers_listing = subprocess.run(
|
||||||
'docker container ls',
|
"docker container ls",
|
||||||
shell=True,
|
shell=True,
|
||||||
check=True,
|
check=True,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
@@ -81,17 +81,17 @@ def test_large_environment():
|
|||||||
# max environment variable size is 128kB
|
# max environment variable size is 128kB
|
||||||
long_env_var_length = 127 * 1024
|
long_env_var_length = 127 * 1024
|
||||||
large_environment = {
|
large_environment = {
|
||||||
'a': '0' * long_env_var_length,
|
"a": "0" * long_env_var_length,
|
||||||
'b': '0' * long_env_var_length,
|
"b": "0" * long_env_var_length,
|
||||||
'c': '0' * long_env_var_length,
|
"c": "0" * long_env_var_length,
|
||||||
'd': '0' * long_env_var_length,
|
"d": "0" * long_env_var_length,
|
||||||
}
|
}
|
||||||
|
|
||||||
with DockerContainer(DEFAULT_IMAGE) as container:
|
with DockerContainer(DEFAULT_IMAGE) as container:
|
||||||
# check the length of d
|
# check the length of d
|
||||||
assert (
|
assert (
|
||||||
container.call(['sh', '-c', 'echo ${#d}'], env=large_environment, capture_output=True)
|
container.call(["sh", "-c", "echo ${#d}"], env=large_environment, capture_output=True)
|
||||||
== f'{long_env_var_length}\n'
|
== f"{long_env_var_length}\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -103,13 +103,13 @@ def test_binary_output():
|
|||||||
# check that we can pass though arbitrary binary data without erroring
|
# check that we can pass though arbitrary binary data without erroring
|
||||||
container.call(
|
container.call(
|
||||||
[
|
[
|
||||||
'/usr/bin/python2',
|
"/usr/bin/python2",
|
||||||
'-c',
|
"-c",
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
'''
|
"""
|
||||||
import sys
|
import sys
|
||||||
sys.stdout.write(''.join(chr(n) for n in range(0, 256)))
|
sys.stdout.write(''.join(chr(n) for n in range(0, 256)))
|
||||||
'''
|
"""
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -117,19 +117,19 @@ def test_binary_output():
|
|||||||
# check that we can capture arbitrary binary data
|
# check that we can capture arbitrary binary data
|
||||||
output = container.call(
|
output = container.call(
|
||||||
[
|
[
|
||||||
'/usr/bin/python2',
|
"/usr/bin/python2",
|
||||||
'-c',
|
"-c",
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
'''
|
"""
|
||||||
import sys
|
import sys
|
||||||
sys.stdout.write(''.join(chr(n % 256) for n in range(0, 512)))
|
sys.stdout.write(''.join(chr(n % 256) for n in range(0, 512)))
|
||||||
'''
|
"""
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
data = bytes(output, encoding='utf8', errors='surrogateescape')
|
data = bytes(output, encoding="utf8", errors="surrogateescape")
|
||||||
|
|
||||||
for i in range(512):
|
for i in range(512):
|
||||||
assert data[i] == i % 256
|
assert data[i] == i % 256
|
||||||
@@ -137,10 +137,10 @@ def test_binary_output():
|
|||||||
# check that environment variables can carry binary data, except null characters
|
# check that environment variables can carry binary data, except null characters
|
||||||
# (https://www.gnu.org/software/libc/manual/html_node/Environment-Variables.html)
|
# (https://www.gnu.org/software/libc/manual/html_node/Environment-Variables.html)
|
||||||
binary_data = bytes(n for n in range(1, 256))
|
binary_data = bytes(n for n in range(1, 256))
|
||||||
binary_data_string = str(binary_data, encoding='utf8', errors='surrogateescape')
|
binary_data_string = str(binary_data, encoding="utf8", errors="surrogateescape")
|
||||||
output = container.call(
|
output = container.call(
|
||||||
['python2', '-c', 'import os, sys; sys.stdout.write(os.environ["TEST_VAR"])'],
|
["python2", "-c", 'import os, sys; sys.stdout.write(os.environ["TEST_VAR"])'],
|
||||||
env={'TEST_VAR': binary_data_string},
|
env={"TEST_VAR": binary_data_string},
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
)
|
)
|
||||||
assert output == binary_data_string
|
assert output == binary_data_string
|
||||||
@@ -151,45 +151,45 @@ def test_file_operations(tmp_path: Path):
|
|||||||
with DockerContainer(DEFAULT_IMAGE) as container:
|
with DockerContainer(DEFAULT_IMAGE) as container:
|
||||||
# test copying a file in
|
# test copying a file in
|
||||||
test_binary_data = bytes(random.randrange(256) for _ in range(1000))
|
test_binary_data = bytes(random.randrange(256) for _ in range(1000))
|
||||||
original_test_file = tmp_path / 'test.dat'
|
original_test_file = tmp_path / "test.dat"
|
||||||
original_test_file.write_bytes(test_binary_data)
|
original_test_file.write_bytes(test_binary_data)
|
||||||
|
|
||||||
dst_file = PurePath('/tmp/test.dat')
|
dst_file = PurePath("/tmp/test.dat")
|
||||||
|
|
||||||
container.copy_into(original_test_file, dst_file)
|
container.copy_into(original_test_file, dst_file)
|
||||||
|
|
||||||
output = container.call(['cat', dst_file], capture_output=True)
|
output = container.call(["cat", dst_file], capture_output=True)
|
||||||
assert test_binary_data == bytes(output, encoding='utf8', errors='surrogateescape')
|
assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.docker
|
@pytest.mark.docker
|
||||||
def test_dir_operations(tmp_path: Path):
|
def test_dir_operations(tmp_path: Path):
|
||||||
with DockerContainer(DEFAULT_IMAGE) as container:
|
with DockerContainer(DEFAULT_IMAGE) as container:
|
||||||
test_binary_data = bytes(random.randrange(256) for _ in range(1000))
|
test_binary_data = bytes(random.randrange(256) for _ in range(1000))
|
||||||
original_test_file = tmp_path / 'test.dat'
|
original_test_file = tmp_path / "test.dat"
|
||||||
original_test_file.write_bytes(test_binary_data)
|
original_test_file.write_bytes(test_binary_data)
|
||||||
|
|
||||||
# test copying a dir in
|
# test copying a dir in
|
||||||
test_dir = tmp_path / 'test_dir'
|
test_dir = tmp_path / "test_dir"
|
||||||
test_dir.mkdir()
|
test_dir.mkdir()
|
||||||
test_file = test_dir / 'test.dat'
|
test_file = test_dir / "test.dat"
|
||||||
shutil.copyfile(original_test_file, test_file)
|
shutil.copyfile(original_test_file, test_file)
|
||||||
|
|
||||||
dst_dir = PurePath('/tmp/test_dir')
|
dst_dir = PurePath("/tmp/test_dir")
|
||||||
dst_file = dst_dir / 'test.dat'
|
dst_file = dst_dir / "test.dat"
|
||||||
container.copy_into(test_dir, dst_dir)
|
container.copy_into(test_dir, dst_dir)
|
||||||
|
|
||||||
output = container.call(['cat', dst_file], capture_output=True)
|
output = container.call(["cat", dst_file], capture_output=True)
|
||||||
assert test_binary_data == bytes(output, encoding='utf8', errors='surrogateescape')
|
assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape")
|
||||||
|
|
||||||
# test glob
|
# test glob
|
||||||
assert container.glob(dst_dir, '*.dat') == [dst_file]
|
assert container.glob(dst_dir, "*.dat") == [dst_file]
|
||||||
|
|
||||||
# test copy dir out
|
# test copy dir out
|
||||||
new_test_dir = tmp_path / 'test_dir_new'
|
new_test_dir = tmp_path / "test_dir_new"
|
||||||
container.copy_out(dst_dir, new_test_dir)
|
container.copy_out(dst_dir, new_test_dir)
|
||||||
|
|
||||||
assert test_binary_data == (new_test_dir / 'test.dat').read_bytes()
|
assert test_binary_data == (new_test_dir / "test.dat").read_bytes()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.docker
|
@pytest.mark.docker
|
||||||
|
|||||||
@@ -5,27 +5,27 @@ import pytest
|
|||||||
|
|
||||||
from cibuildwheel.util import download
|
from cibuildwheel.util import download
|
||||||
|
|
||||||
DOWNLOAD_URL = 'https://raw.githubusercontent.com/joerick/cibuildwheel/v1.6.3/requirements-dev.txt'
|
DOWNLOAD_URL = "https://raw.githubusercontent.com/joerick/cibuildwheel/v1.6.3/requirements-dev.txt"
|
||||||
|
|
||||||
|
|
||||||
def test_download(monkeypatch, tmp_path):
|
def test_download(monkeypatch, tmp_path):
|
||||||
monkeypatch.delenv('SSL_CERT_FILE', raising=False)
|
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
||||||
dest = tmp_path / 'file.txt'
|
dest = tmp_path / "file.txt"
|
||||||
download(DOWNLOAD_URL, dest)
|
download(DOWNLOAD_URL, dest)
|
||||||
assert len(dest.read_bytes()) == 134
|
assert len(dest.read_bytes()) == 134
|
||||||
|
|
||||||
|
|
||||||
def test_download_good_ssl_cert_file(monkeypatch, tmp_path):
|
def test_download_good_ssl_cert_file(monkeypatch, tmp_path):
|
||||||
monkeypatch.setenv('SSL_CERT_FILE', certifi.where())
|
monkeypatch.setenv("SSL_CERT_FILE", certifi.where())
|
||||||
dest = tmp_path / 'file.txt'
|
dest = tmp_path / "file.txt"
|
||||||
download(DOWNLOAD_URL, dest)
|
download(DOWNLOAD_URL, dest)
|
||||||
assert len(dest.read_bytes()) == 134
|
assert len(dest.read_bytes()) == 134
|
||||||
|
|
||||||
|
|
||||||
def test_download_bad_ssl_cert_file(monkeypatch, tmp_path):
|
def test_download_bad_ssl_cert_file(monkeypatch, tmp_path):
|
||||||
bad_cafile = tmp_path / 'ca.pem'
|
bad_cafile = tmp_path / "ca.pem"
|
||||||
bad_cafile.write_text('bad certificates')
|
bad_cafile.write_text("bad certificates")
|
||||||
monkeypatch.setenv('SSL_CERT_FILE', str(bad_cafile))
|
monkeypatch.setenv("SSL_CERT_FILE", str(bad_cafile))
|
||||||
dest = tmp_path / 'file.txt'
|
dest = tmp_path / "file.txt"
|
||||||
with pytest.raises(ssl.SSLError):
|
with pytest.raises(ssl.SSLError):
|
||||||
download(DOWNLOAD_URL, dest)
|
download(DOWNLOAD_URL, dest)
|
||||||
|
|||||||
@@ -4,70 +4,70 @@ from cibuildwheel.environment import parse_environment
|
|||||||
|
|
||||||
|
|
||||||
def test_basic_parsing():
|
def test_basic_parsing():
|
||||||
environment_recipe = parse_environment('VAR=1 VBR=2')
|
environment_recipe = parse_environment("VAR=1 VBR=2")
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(prev_environment={})
|
environment_dict = environment_recipe.as_dictionary(prev_environment={})
|
||||||
environment_cmds = environment_recipe.as_shell_commands()
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
|
|
||||||
assert environment_dict == {'VAR': '1', 'VBR': '2'}
|
assert environment_dict == {"VAR": "1", "VBR": "2"}
|
||||||
assert environment_cmds == ['export VAR=1', 'export VBR=2']
|
assert environment_cmds == ["export VAR=1", "export VBR=2"]
|
||||||
|
|
||||||
|
|
||||||
def test_quotes():
|
def test_quotes():
|
||||||
environment_recipe = parse_environment('A=1 VAR="1 NOT_A_VAR=2" VBR=\'vbr\'')
|
environment_recipe = parse_environment("A=1 VAR=\"1 NOT_A_VAR=2\" VBR='vbr'")
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(prev_environment={})
|
environment_dict = environment_recipe.as_dictionary(prev_environment={})
|
||||||
environment_cmds = environment_recipe.as_shell_commands()
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
|
|
||||||
assert environment_dict == {'A': '1', 'VAR': '1 NOT_A_VAR=2', 'VBR': 'vbr'}
|
assert environment_dict == {"A": "1", "VAR": "1 NOT_A_VAR=2", "VBR": "vbr"}
|
||||||
assert environment_cmds == ['export A=1', 'export VAR="1 NOT_A_VAR=2"', 'export VBR=\'vbr\'']
|
assert environment_cmds == ["export A=1", 'export VAR="1 NOT_A_VAR=2"', "export VBR='vbr'"]
|
||||||
|
|
||||||
|
|
||||||
def test_inheritance():
|
def test_inheritance():
|
||||||
environment_recipe = parse_environment('PATH=$PATH:/usr/local/bin')
|
environment_recipe = parse_environment("PATH=$PATH:/usr/local/bin")
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(prev_environment={'PATH': '/usr/bin'})
|
environment_dict = environment_recipe.as_dictionary(prev_environment={"PATH": "/usr/bin"})
|
||||||
environment_cmds = environment_recipe.as_shell_commands()
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
|
|
||||||
assert environment_dict == {'PATH': '/usr/bin:/usr/local/bin'}
|
assert environment_dict == {"PATH": "/usr/bin:/usr/local/bin"}
|
||||||
assert environment_cmds == ['export PATH=$PATH:/usr/local/bin']
|
assert environment_cmds == ["export PATH=$PATH:/usr/local/bin"]
|
||||||
|
|
||||||
|
|
||||||
def test_shell_eval():
|
def test_shell_eval():
|
||||||
environment_recipe = parse_environment('VAR="$(echo "a test" string)"')
|
environment_recipe = parse_environment('VAR="$(echo "a test" string)"')
|
||||||
|
|
||||||
env_copy = os.environ.copy()
|
env_copy = os.environ.copy()
|
||||||
env_copy.pop('VAR', None)
|
env_copy.pop("VAR", None)
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(prev_environment=env_copy)
|
environment_dict = environment_recipe.as_dictionary(prev_environment=env_copy)
|
||||||
environment_cmds = environment_recipe.as_shell_commands()
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
|
|
||||||
assert environment_dict['VAR'] == 'a test string'
|
assert environment_dict["VAR"] == "a test string"
|
||||||
assert environment_cmds == ['export VAR="$(echo "a test" string)"']
|
assert environment_cmds == ['export VAR="$(echo "a test" string)"']
|
||||||
|
|
||||||
|
|
||||||
def test_shell_eval_and_env():
|
def test_shell_eval_and_env():
|
||||||
environment_recipe = parse_environment('VAR="$(echo "$PREV_VAR" string)"')
|
environment_recipe = parse_environment('VAR="$(echo "$PREV_VAR" string)"')
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(prev_environment={'PREV_VAR': '1 2 3'})
|
environment_dict = environment_recipe.as_dictionary(prev_environment={"PREV_VAR": "1 2 3"})
|
||||||
environment_cmds = environment_recipe.as_shell_commands()
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
|
|
||||||
assert environment_dict == {'PREV_VAR': '1 2 3', 'VAR': '1 2 3 string'}
|
assert environment_dict == {"PREV_VAR": "1 2 3", "VAR": "1 2 3 string"}
|
||||||
assert environment_cmds == ['export VAR="$(echo "$PREV_VAR" string)"']
|
assert environment_cmds == ['export VAR="$(echo "$PREV_VAR" string)"']
|
||||||
|
|
||||||
|
|
||||||
def test_empty_var():
|
def test_empty_var():
|
||||||
environment_recipe = parse_environment('CFLAGS=')
|
environment_recipe = parse_environment("CFLAGS=")
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(prev_environment={'CFLAGS': '-Wall'})
|
environment_dict = environment_recipe.as_dictionary(prev_environment={"CFLAGS": "-Wall"})
|
||||||
environment_cmds = environment_recipe.as_shell_commands()
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
|
|
||||||
assert environment_dict == {'CFLAGS': ''}
|
assert environment_dict == {"CFLAGS": ""}
|
||||||
assert environment_cmds == ['export CFLAGS=']
|
assert environment_cmds == ["export CFLAGS="]
|
||||||
|
|
||||||
|
|
||||||
def test_no_vars():
|
def test_no_vars():
|
||||||
environment_recipe = parse_environment('')
|
environment_recipe = parse_environment("")
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(prev_environment={})
|
environment_dict = environment_recipe.as_dictionary(prev_environment={})
|
||||||
environment_cmds = environment_recipe.as_shell_commands()
|
environment_cmds = environment_recipe.as_shell_commands()
|
||||||
@@ -77,13 +77,13 @@ def test_no_vars():
|
|||||||
|
|
||||||
|
|
||||||
def test_no_vars_pass_through():
|
def test_no_vars_pass_through():
|
||||||
environment_recipe = parse_environment('')
|
environment_recipe = parse_environment("")
|
||||||
|
|
||||||
environment_dict = environment_recipe.as_dictionary(
|
environment_dict = environment_recipe.as_dictionary(
|
||||||
prev_environment={'CIBUILDWHEEL': 'awesome'}
|
prev_environment={"CIBUILDWHEEL": "awesome"}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert environment_dict == {'CIBUILDWHEEL': 'awesome'}
|
assert environment_dict == {"CIBUILDWHEEL": "awesome"}
|
||||||
|
|
||||||
|
|
||||||
def test_operators_inside_eval():
|
def test_operators_inside_eval():
|
||||||
@@ -92,16 +92,16 @@ def test_operators_inside_eval():
|
|||||||
# pass the existing process env so PATH is available
|
# pass the existing process env so PATH is available
|
||||||
environment_dict = environment_recipe.as_dictionary(os.environ.copy())
|
environment_dict = environment_recipe.as_dictionary(os.environ.copy())
|
||||||
|
|
||||||
assert environment_dict.get('SOMETHING') == 'a\nb\nc'
|
assert environment_dict.get("SOMETHING") == "a\nb\nc"
|
||||||
|
|
||||||
|
|
||||||
def test_substitution_with_backslash():
|
def test_substitution_with_backslash():
|
||||||
environment_recipe = parse_environment('PATH2="somewhere_else;$PATH1"')
|
environment_recipe = parse_environment('PATH2="somewhere_else;$PATH1"')
|
||||||
|
|
||||||
# pass the existing process env so PATH is available
|
# pass the existing process env so PATH is available
|
||||||
environment_dict = environment_recipe.as_dictionary(prev_environment={'PATH1': 'c:\\folder\\'})
|
environment_dict = environment_recipe.as_dictionary(prev_environment={"PATH1": "c:\\folder\\"})
|
||||||
|
|
||||||
assert environment_dict.get('PATH2') == 'somewhere_else;c:\\folder\\'
|
assert environment_dict.get("PATH2") == "somewhere_else;c:\\folder\\"
|
||||||
|
|
||||||
|
|
||||||
def test_awkwardly_quoted_variable():
|
def test_awkwardly_quoted_variable():
|
||||||
@@ -110,6 +110,6 @@ def test_awkwardly_quoted_variable():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# pass the existing process env so PATH is available
|
# pass the existing process env so PATH is available
|
||||||
environment_dict = environment_recipe.as_dictionary({'VAR1': 'but wait'})
|
environment_dict = environment_recipe.as_dictionary({"VAR1": "but wait"})
|
||||||
|
|
||||||
assert environment_dict.get('VAR2') == 'somethinglike thisbut waitbut waittheres moreand more!'
|
assert environment_dict.get("VAR2") == "somethinglike thisbut waitbut waittheres moreand more!"
|
||||||
|
|||||||
@@ -15,15 +15,15 @@ class ArgsInterceptor:
|
|||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
|
||||||
MOCK_PACKAGE_DIR = Path('some_package_dir')
|
MOCK_PACKAGE_DIR = Path("some_package_dir")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def mock_protection(monkeypatch):
|
def mock_protection(monkeypatch):
|
||||||
'''
|
"""
|
||||||
Ensure that a unit test will never actually run a cibuildwheel 'build'
|
Ensure that a unit test will never actually run a cibuildwheel 'build'
|
||||||
function, which shouldn't be run on a developer's machine
|
function, which shouldn't be run on a developer's machine
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def fail_on_call(*args, **kwargs):
|
def fail_on_call(*args, **kwargs):
|
||||||
raise RuntimeError("This should never be called")
|
raise RuntimeError("This should never be called")
|
||||||
@@ -31,31 +31,31 @@ def mock_protection(monkeypatch):
|
|||||||
def ignore_call(*args, **kwargs):
|
def ignore_call(*args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
monkeypatch.setattr(subprocess, 'Popen', fail_on_call)
|
monkeypatch.setattr(subprocess, "Popen", fail_on_call)
|
||||||
monkeypatch.setattr(util, 'download', fail_on_call)
|
monkeypatch.setattr(util, "download", fail_on_call)
|
||||||
monkeypatch.setattr(windows, 'build', fail_on_call)
|
monkeypatch.setattr(windows, "build", fail_on_call)
|
||||||
monkeypatch.setattr(linux, 'build', fail_on_call)
|
monkeypatch.setattr(linux, "build", fail_on_call)
|
||||||
monkeypatch.setattr(macos, 'build', fail_on_call)
|
monkeypatch.setattr(macos, "build", fail_on_call)
|
||||||
|
|
||||||
monkeypatch.setattr(Path, 'mkdir', ignore_call)
|
monkeypatch.setattr(Path, "mkdir", ignore_call)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def fake_package_dir(monkeypatch):
|
def fake_package_dir(monkeypatch):
|
||||||
'''
|
"""
|
||||||
Monkey-patch enough for the main() function to run
|
Monkey-patch enough for the main() function to run
|
||||||
'''
|
"""
|
||||||
real_path_exists = Path.exists
|
real_path_exists = Path.exists
|
||||||
|
|
||||||
def mock_path_exists(path):
|
def mock_path_exists(path):
|
||||||
if path == MOCK_PACKAGE_DIR / 'setup.py':
|
if path == MOCK_PACKAGE_DIR / "setup.py":
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return real_path_exists(path)
|
return real_path_exists(path)
|
||||||
|
|
||||||
args = ['cibuildwheel', str(MOCK_PACKAGE_DIR)]
|
args = ["cibuildwheel", str(MOCK_PACKAGE_DIR)]
|
||||||
monkeypatch.setattr(Path, 'exists', mock_path_exists)
|
monkeypatch.setattr(Path, "exists", mock_path_exists)
|
||||||
monkeypatch.setattr(sys, 'argv', args)
|
monkeypatch.setattr(sys, "argv", args)
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
@@ -65,26 +65,26 @@ def disable_print_wheels(monkeypatch):
|
|||||||
def empty_cm(*args, **kwargs):
|
def empty_cm(*args, **kwargs):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
monkeypatch.setattr(util, 'print_new_wheels', empty_cm)
|
monkeypatch.setattr(util, "print_new_wheels", empty_cm)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def allow_empty(request, monkeypatch, fake_package_dir):
|
def allow_empty(request, monkeypatch, fake_package_dir):
|
||||||
monkeypatch.setattr(sys, 'argv', fake_package_dir + ['--allow-empty'])
|
monkeypatch.setattr(sys, "argv", fake_package_dir + ["--allow-empty"])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=['linux', 'macos', 'windows'])
|
@pytest.fixture(params=["linux", "macos", "windows"])
|
||||||
def platform(request, monkeypatch):
|
def platform(request, monkeypatch):
|
||||||
platform_value = request.param
|
platform_value = request.param
|
||||||
monkeypatch.setenv('CIBW_PLATFORM', platform_value)
|
monkeypatch.setenv("CIBW_PLATFORM", platform_value)
|
||||||
|
|
||||||
if platform_value == 'windows':
|
if platform_value == "windows":
|
||||||
monkeypatch.setattr(platform_module, 'machine', lambda: 'AMD64')
|
monkeypatch.setattr(platform_module, "machine", lambda: "AMD64")
|
||||||
else:
|
else:
|
||||||
monkeypatch.setattr(platform_module, 'machine', lambda: 'x86_64')
|
monkeypatch.setattr(platform_module, "machine", lambda: "x86_64")
|
||||||
|
|
||||||
if platform_value == 'macos':
|
if platform_value == "macos":
|
||||||
monkeypatch.setattr(macos, 'get_macos_version', lambda: (11, 1))
|
monkeypatch.setattr(macos, "get_macos_version", lambda: (11, 1))
|
||||||
|
|
||||||
return platform_value
|
return platform_value
|
||||||
|
|
||||||
@@ -93,13 +93,13 @@ def platform(request, monkeypatch):
|
|||||||
def intercepted_build_args(platform, monkeypatch):
|
def intercepted_build_args(platform, monkeypatch):
|
||||||
intercepted = ArgsInterceptor()
|
intercepted = ArgsInterceptor()
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
monkeypatch.setattr(linux, 'build', intercepted)
|
monkeypatch.setattr(linux, "build", intercepted)
|
||||||
elif platform == 'macos':
|
elif platform == "macos":
|
||||||
monkeypatch.setattr(macos, 'build', intercepted)
|
monkeypatch.setattr(macos, "build", intercepted)
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
monkeypatch.setattr(windows, 'build', intercepted)
|
monkeypatch.setattr(windows, "build", intercepted)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f'unknown platform value: {platform}')
|
raise ValueError(f"unknown platform value: {platform}")
|
||||||
|
|
||||||
return intercepted
|
return intercepted
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ from cibuildwheel.util import BuildSelector
|
|||||||
|
|
||||||
|
|
||||||
def test_output_dir(platform, intercepted_build_args, monkeypatch):
|
def test_output_dir(platform, intercepted_build_args, monkeypatch):
|
||||||
OUTPUT_DIR = Path('some_output_dir')
|
OUTPUT_DIR = Path("some_output_dir")
|
||||||
|
|
||||||
monkeypatch.setenv('CIBW_OUTPUT_DIR', str(OUTPUT_DIR))
|
monkeypatch.setenv("CIBW_OUTPUT_DIR", str(OUTPUT_DIR))
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
@@ -24,16 +24,16 @@ def test_output_dir(platform, intercepted_build_args, monkeypatch):
|
|||||||
def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
|
def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
|
||||||
main()
|
main()
|
||||||
|
|
||||||
assert intercepted_build_args.args[0].output_dir == Path('wheelhouse')
|
assert intercepted_build_args.args[0].output_dir == Path("wheelhouse")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('also_set_environment', [False, True])
|
@pytest.mark.parametrize("also_set_environment", [False, True])
|
||||||
def test_output_dir_argument(also_set_environment, platform, intercepted_build_args, monkeypatch):
|
def test_output_dir_argument(also_set_environment, platform, intercepted_build_args, monkeypatch):
|
||||||
OUTPUT_DIR = Path('some_output_dir')
|
OUTPUT_DIR = Path("some_output_dir")
|
||||||
|
|
||||||
monkeypatch.setattr(sys, 'argv', sys.argv + ['--output-dir', str(OUTPUT_DIR)])
|
monkeypatch.setattr(sys, "argv", sys.argv + ["--output-dir", str(OUTPUT_DIR)])
|
||||||
if also_set_environment:
|
if also_set_environment:
|
||||||
monkeypatch.setenv('CIBW_OUTPUT_DIR', 'not_this_output_dir')
|
monkeypatch.setenv("CIBW_OUTPUT_DIR", "not_this_output_dir")
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
@@ -41,24 +41,24 @@ def test_output_dir_argument(also_set_environment, platform, intercepted_build_a
|
|||||||
|
|
||||||
|
|
||||||
def test_build_selector(platform, intercepted_build_args, monkeypatch, allow_empty):
|
def test_build_selector(platform, intercepted_build_args, monkeypatch, allow_empty):
|
||||||
BUILD = 'some build* *-selector'
|
BUILD = "some build* *-selector"
|
||||||
SKIP = 'some skip* *-selector'
|
SKIP = "some skip* *-selector"
|
||||||
|
|
||||||
monkeypatch.setenv('CIBW_BUILD', BUILD)
|
monkeypatch.setenv("CIBW_BUILD", BUILD)
|
||||||
monkeypatch.setenv('CIBW_SKIP', SKIP)
|
monkeypatch.setenv("CIBW_SKIP", SKIP)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||||
assert intercepted_build_selector('build24-this')
|
assert intercepted_build_selector("build24-this")
|
||||||
assert not intercepted_build_selector('skip65-that')
|
assert not intercepted_build_selector("skip65-that")
|
||||||
# This unit test is just testing the options of 'main'
|
# This unit test is just testing the options of 'main'
|
||||||
# Unit tests for BuildSelector are in build_selector_test.py
|
# Unit tests for BuildSelector are in build_selector_test.py
|
||||||
|
|
||||||
|
|
||||||
def test_empty_selector(platform, intercepted_build_args, monkeypatch):
|
def test_empty_selector(platform, intercepted_build_args, monkeypatch):
|
||||||
monkeypatch.setenv('CIBW_SKIP', '*')
|
monkeypatch.setenv("CIBW_SKIP", "*")
|
||||||
|
|
||||||
with pytest.raises(SystemExit) as e:
|
with pytest.raises(SystemExit) as e:
|
||||||
main()
|
main()
|
||||||
@@ -67,63 +67,63 @@ def test_empty_selector(platform, intercepted_build_args, monkeypatch):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'architecture, image, full_image',
|
"architecture, image, full_image",
|
||||||
[
|
[
|
||||||
('x86_64', None, 'quay.io/pypa/manylinux2010_x86_64:*'),
|
("x86_64", None, "quay.io/pypa/manylinux2010_x86_64:*"),
|
||||||
('x86_64', 'manylinux1', 'quay.io/pypa/manylinux1_x86_64:*'),
|
("x86_64", "manylinux1", "quay.io/pypa/manylinux1_x86_64:*"),
|
||||||
('x86_64', 'manylinux2010', 'quay.io/pypa/manylinux2010_x86_64:*'),
|
("x86_64", "manylinux2010", "quay.io/pypa/manylinux2010_x86_64:*"),
|
||||||
('x86_64', 'manylinux2014', 'quay.io/pypa/manylinux2014_x86_64:*'),
|
("x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
|
||||||
('x86_64', 'manylinux_2_24', 'quay.io/pypa/manylinux_2_24_x86_64:*'),
|
("x86_64", "manylinux_2_24", "quay.io/pypa/manylinux_2_24_x86_64:*"),
|
||||||
('x86_64', 'custom_image', 'custom_image'),
|
("x86_64", "custom_image", "custom_image"),
|
||||||
('i686', None, 'quay.io/pypa/manylinux2010_i686:*'),
|
("i686", None, "quay.io/pypa/manylinux2010_i686:*"),
|
||||||
('i686', 'manylinux1', 'quay.io/pypa/manylinux1_i686:*'),
|
("i686", "manylinux1", "quay.io/pypa/manylinux1_i686:*"),
|
||||||
('i686', 'manylinux2010', 'quay.io/pypa/manylinux2010_i686:*'),
|
("i686", "manylinux2010", "quay.io/pypa/manylinux2010_i686:*"),
|
||||||
('i686', 'manylinux2014', 'quay.io/pypa/manylinux2014_i686:*'),
|
("i686", "manylinux2014", "quay.io/pypa/manylinux2014_i686:*"),
|
||||||
('i686', 'manylinux_2_24', 'quay.io/pypa/manylinux_2_24_i686:*'),
|
("i686", "manylinux_2_24", "quay.io/pypa/manylinux_2_24_i686:*"),
|
||||||
('i686', 'custom_image', 'custom_image'),
|
("i686", "custom_image", "custom_image"),
|
||||||
('pypy_x86_64', None, 'pypywheels/manylinux2010-pypy_x86_64:*'),
|
("pypy_x86_64", None, "pypywheels/manylinux2010-pypy_x86_64:*"),
|
||||||
('pypy_x86_64', 'manylinux1', 'manylinux1'), # Does not exist
|
("pypy_x86_64", "manylinux1", "manylinux1"), # Does not exist
|
||||||
('pypy_x86_64', 'manylinux2010', 'pypywheels/manylinux2010-pypy_x86_64:*'),
|
("pypy_x86_64", "manylinux2010", "pypywheels/manylinux2010-pypy_x86_64:*"),
|
||||||
('pypy_x86_64', 'manylinux2014', 'manylinux2014'), # Does not exist (yet)
|
("pypy_x86_64", "manylinux2014", "manylinux2014"), # Does not exist (yet)
|
||||||
('pypy_x86_64', 'custom_image', 'custom_image'),
|
("pypy_x86_64", "custom_image", "custom_image"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_manylinux_images(
|
def test_manylinux_images(
|
||||||
architecture, image, full_image, platform, intercepted_build_args, monkeypatch
|
architecture, image, full_image, platform, intercepted_build_args, monkeypatch
|
||||||
):
|
):
|
||||||
if image is not None:
|
if image is not None:
|
||||||
monkeypatch.setenv('CIBW_MANYLINUX_' + architecture.upper() + '_IMAGE', image)
|
monkeypatch.setenv("CIBW_MANYLINUX_" + architecture.upper() + "_IMAGE", image)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
assert fnmatch(intercepted_build_args.args[0].manylinux_images[architecture], full_image)
|
assert fnmatch(intercepted_build_args.args[0].manylinux_images[architecture], full_image)
|
||||||
else:
|
else:
|
||||||
assert intercepted_build_args.args[0].manylinux_images is None
|
assert intercepted_build_args.args[0].manylinux_images is None
|
||||||
|
|
||||||
|
|
||||||
def get_default_repair_command(platform):
|
def get_default_repair_command(platform):
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
return 'auditwheel repair -w {dest_dir} {wheel}'
|
return "auditwheel repair -w {dest_dir} {wheel}"
|
||||||
elif platform == 'macos':
|
elif platform == "macos":
|
||||||
return 'delocate-listdeps {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}'
|
return "delocate-listdeps {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}"
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
return ''
|
return ""
|
||||||
else:
|
else:
|
||||||
raise ValueError('Unknown platform', platform)
|
raise ValueError("Unknown platform", platform)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('repair_command', [None, 'repair', 'repair -w {dest_dir} {wheel}'])
|
@pytest.mark.parametrize("repair_command", [None, "repair", "repair -w {dest_dir} {wheel}"])
|
||||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
@pytest.mark.parametrize("platform_specific", [False, True])
|
||||||
def test_repair_command(
|
def test_repair_command(
|
||||||
repair_command, platform_specific, platform, intercepted_build_args, monkeypatch
|
repair_command, platform_specific, platform, intercepted_build_args, monkeypatch
|
||||||
):
|
):
|
||||||
if repair_command is not None:
|
if repair_command is not None:
|
||||||
if platform_specific:
|
if platform_specific:
|
||||||
monkeypatch.setenv('CIBW_REPAIR_WHEEL_COMMAND_' + platform.upper(), repair_command)
|
monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND_" + platform.upper(), repair_command)
|
||||||
monkeypatch.setenv('CIBW_REPAIR_WHEEL_COMMAND', 'overwritten')
|
monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND", "overwritten")
|
||||||
else:
|
else:
|
||||||
monkeypatch.setenv('CIBW_REPAIR_WHEEL_COMMAND', repair_command)
|
monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND", repair_command)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
@@ -132,17 +132,17 @@ def test_repair_command(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'environment',
|
"environment",
|
||||||
[{}, {'something': 'value'}, {'something': 'value', 'something_else': 'other_value'}],
|
[{}, {"something": "value"}, {"something": "value", "something_else": "other_value"}],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
@pytest.mark.parametrize("platform_specific", [False, True])
|
||||||
def test_environment(environment, platform_specific, platform, intercepted_build_args, monkeypatch):
|
def test_environment(environment, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||||
env_string = ' '.join(f'{k}={v}' for k, v in environment.items())
|
env_string = " ".join(f"{k}={v}" for k, v in environment.items())
|
||||||
if platform_specific:
|
if platform_specific:
|
||||||
monkeypatch.setenv('CIBW_ENVIRONMENT_' + platform.upper(), env_string)
|
monkeypatch.setenv("CIBW_ENVIRONMENT_" + platform.upper(), env_string)
|
||||||
monkeypatch.setenv('CIBW_ENVIRONMENT', 'overwritten')
|
monkeypatch.setenv("CIBW_ENVIRONMENT", "overwritten")
|
||||||
else:
|
else:
|
||||||
monkeypatch.setenv('CIBW_ENVIRONMENT', env_string)
|
monkeypatch.setenv("CIBW_ENVIRONMENT", env_string)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
@@ -151,85 +151,85 @@ def test_environment(environment, platform_specific, platform, intercepted_build
|
|||||||
assert intercepted_environment.as_dictionary(prev_environment={}) == environment
|
assert intercepted_environment.as_dictionary(prev_environment={}) == environment
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('test_requires', [None, 'requirement other_requirement'])
|
@pytest.mark.parametrize("test_requires", [None, "requirement other_requirement"])
|
||||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
@pytest.mark.parametrize("platform_specific", [False, True])
|
||||||
def test_test_requires(
|
def test_test_requires(
|
||||||
test_requires, platform_specific, platform, intercepted_build_args, monkeypatch
|
test_requires, platform_specific, platform, intercepted_build_args, monkeypatch
|
||||||
):
|
):
|
||||||
if test_requires is not None:
|
if test_requires is not None:
|
||||||
if platform_specific:
|
if platform_specific:
|
||||||
monkeypatch.setenv('CIBW_TEST_REQUIRES_' + platform.upper(), test_requires)
|
monkeypatch.setenv("CIBW_TEST_REQUIRES_" + platform.upper(), test_requires)
|
||||||
monkeypatch.setenv('CIBW_TEST_REQUIRES', 'overwritten')
|
monkeypatch.setenv("CIBW_TEST_REQUIRES", "overwritten")
|
||||||
else:
|
else:
|
||||||
monkeypatch.setenv('CIBW_TEST_REQUIRES', test_requires)
|
monkeypatch.setenv("CIBW_TEST_REQUIRES", test_requires)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
assert intercepted_build_args.args[0].test_requires == (test_requires or '').split()
|
assert intercepted_build_args.args[0].test_requires == (test_requires or "").split()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('test_extras', [None, 'extras'])
|
@pytest.mark.parametrize("test_extras", [None, "extras"])
|
||||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
@pytest.mark.parametrize("platform_specific", [False, True])
|
||||||
def test_test_extras(test_extras, platform_specific, platform, intercepted_build_args, monkeypatch):
|
def test_test_extras(test_extras, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||||
if test_extras is not None:
|
if test_extras is not None:
|
||||||
if platform_specific:
|
if platform_specific:
|
||||||
monkeypatch.setenv('CIBW_TEST_EXTRAS_' + platform.upper(), test_extras)
|
monkeypatch.setenv("CIBW_TEST_EXTRAS_" + platform.upper(), test_extras)
|
||||||
monkeypatch.setenv('CIBW_TEST_EXTRAS', 'overwritten')
|
monkeypatch.setenv("CIBW_TEST_EXTRAS", "overwritten")
|
||||||
else:
|
else:
|
||||||
monkeypatch.setenv('CIBW_TEST_EXTRAS', test_extras)
|
monkeypatch.setenv("CIBW_TEST_EXTRAS", test_extras)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
assert intercepted_build_args.args[0].test_extras == (
|
assert intercepted_build_args.args[0].test_extras == (
|
||||||
'[' + test_extras + ']' if test_extras else ''
|
"[" + test_extras + "]" if test_extras else ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('test_command', [None, 'test --command'])
|
@pytest.mark.parametrize("test_command", [None, "test --command"])
|
||||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
@pytest.mark.parametrize("platform_specific", [False, True])
|
||||||
def test_test_command(
|
def test_test_command(
|
||||||
test_command, platform_specific, platform, intercepted_build_args, monkeypatch
|
test_command, platform_specific, platform, intercepted_build_args, monkeypatch
|
||||||
):
|
):
|
||||||
if test_command is not None:
|
if test_command is not None:
|
||||||
if platform_specific:
|
if platform_specific:
|
||||||
monkeypatch.setenv('CIBW_TEST_COMMAND_' + platform.upper(), test_command)
|
monkeypatch.setenv("CIBW_TEST_COMMAND_" + platform.upper(), test_command)
|
||||||
monkeypatch.setenv('CIBW_TEST_COMMAND', 'overwritten')
|
monkeypatch.setenv("CIBW_TEST_COMMAND", "overwritten")
|
||||||
else:
|
else:
|
||||||
monkeypatch.setenv('CIBW_TEST_COMMAND', test_command)
|
monkeypatch.setenv("CIBW_TEST_COMMAND", test_command)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
assert intercepted_build_args.args[0].test_command == test_command
|
assert intercepted_build_args.args[0].test_command == test_command
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('before_build', [None, 'before --build'])
|
@pytest.mark.parametrize("before_build", [None, "before --build"])
|
||||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
@pytest.mark.parametrize("platform_specific", [False, True])
|
||||||
def test_before_build(
|
def test_before_build(
|
||||||
before_build, platform_specific, platform, intercepted_build_args, monkeypatch
|
before_build, platform_specific, platform, intercepted_build_args, monkeypatch
|
||||||
):
|
):
|
||||||
if before_build is not None:
|
if before_build is not None:
|
||||||
if platform_specific:
|
if platform_specific:
|
||||||
monkeypatch.setenv('CIBW_BEFORE_BUILD_' + platform.upper(), before_build)
|
monkeypatch.setenv("CIBW_BEFORE_BUILD_" + platform.upper(), before_build)
|
||||||
monkeypatch.setenv('CIBW_BEFORE_BUILD', 'overwritten')
|
monkeypatch.setenv("CIBW_BEFORE_BUILD", "overwritten")
|
||||||
else:
|
else:
|
||||||
monkeypatch.setenv('CIBW_BEFORE_BUILD', before_build)
|
monkeypatch.setenv("CIBW_BEFORE_BUILD", before_build)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
assert intercepted_build_args.args[0].before_build == before_build
|
assert intercepted_build_args.args[0].before_build == before_build
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('build_verbosity', [None, 0, 2, -2, 4, -4])
|
@pytest.mark.parametrize("build_verbosity", [None, 0, 2, -2, 4, -4])
|
||||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
@pytest.mark.parametrize("platform_specific", [False, True])
|
||||||
def test_build_verbosity(
|
def test_build_verbosity(
|
||||||
build_verbosity, platform_specific, platform, intercepted_build_args, monkeypatch
|
build_verbosity, platform_specific, platform, intercepted_build_args, monkeypatch
|
||||||
):
|
):
|
||||||
if build_verbosity is not None:
|
if build_verbosity is not None:
|
||||||
if platform_specific:
|
if platform_specific:
|
||||||
monkeypatch.setenv('CIBW_BUILD_VERBOSITY_' + platform.upper(), str(build_verbosity))
|
monkeypatch.setenv("CIBW_BUILD_VERBOSITY_" + platform.upper(), str(build_verbosity))
|
||||||
monkeypatch.setenv('CIBW_BUILD_VERBOSITY', 'overwritten')
|
monkeypatch.setenv("CIBW_BUILD_VERBOSITY", "overwritten")
|
||||||
else:
|
else:
|
||||||
monkeypatch.setenv('CIBW_BUILD_VERBOSITY', str(build_verbosity))
|
monkeypatch.setenv("CIBW_BUILD_VERBOSITY", str(build_verbosity))
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
@@ -237,14 +237,14 @@ def test_build_verbosity(
|
|||||||
assert intercepted_build_args.args[0].build_verbosity == expected_verbosity
|
assert intercepted_build_args.args[0].build_verbosity == expected_verbosity
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('option_name', ['CIBW_BUILD', 'CIBW_SKIP'])
|
@pytest.mark.parametrize("option_name", ["CIBW_BUILD", "CIBW_SKIP"])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'option_value, build_selector_patterns',
|
"option_value, build_selector_patterns",
|
||||||
[
|
[
|
||||||
('*-manylinux1_*', ['*-manylinux_*']),
|
("*-manylinux1_*", ["*-manylinux_*"]),
|
||||||
('*-macosx_10_6_intel', ['*-macosx_x86_64']),
|
("*-macosx_10_6_intel", ["*-macosx_x86_64"]),
|
||||||
('*-macosx_10_9_x86_64', ['*-macosx_x86_64']),
|
("*-macosx_10_9_x86_64", ["*-macosx_x86_64"]),
|
||||||
('cp37-macosx_10_9_x86_64', ['cp37-macosx_x86_64']),
|
("cp37-macosx_10_9_x86_64", ["cp37-macosx_x86_64"]),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_build_selector_migrations(
|
def test_build_selector_migrations(
|
||||||
@@ -262,21 +262,21 @@ def test_build_selector_migrations(
|
|||||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||||
assert isinstance(intercepted_build_selector, BuildSelector)
|
assert isinstance(intercepted_build_selector, BuildSelector)
|
||||||
|
|
||||||
if option_name == 'CIBW_BUILD':
|
if option_name == "CIBW_BUILD":
|
||||||
assert intercepted_build_selector.build_patterns == build_selector_patterns
|
assert intercepted_build_selector.build_patterns == build_selector_patterns
|
||||||
else:
|
else:
|
||||||
assert intercepted_build_selector.skip_patterns == build_selector_patterns
|
assert intercepted_build_selector.skip_patterns == build_selector_patterns
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('before_all', ["", None, 'test text'])
|
@pytest.mark.parametrize("before_all", ["", None, "test text"])
|
||||||
@pytest.mark.parametrize('platform_specific', [False, True])
|
@pytest.mark.parametrize("platform_specific", [False, True])
|
||||||
def test_before_all(before_all, platform_specific, platform, intercepted_build_args, monkeypatch):
|
def test_before_all(before_all, platform_specific, platform, intercepted_build_args, monkeypatch):
|
||||||
if before_all is not None:
|
if before_all is not None:
|
||||||
if platform_specific:
|
if platform_specific:
|
||||||
monkeypatch.setenv('CIBW_BEFORE_ALL_' + platform.upper(), before_all)
|
monkeypatch.setenv("CIBW_BEFORE_ALL_" + platform.upper(), before_all)
|
||||||
monkeypatch.setenv('CIBW_BEFORE_ALL', 'overwritten')
|
monkeypatch.setenv("CIBW_BEFORE_ALL", "overwritten")
|
||||||
else:
|
else:
|
||||||
monkeypatch.setenv('CIBW_BEFORE_ALL', before_all)
|
monkeypatch.setenv("CIBW_BEFORE_ALL", before_all)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|||||||
@@ -9,29 +9,29 @@ from .conftest import MOCK_PACKAGE_DIR
|
|||||||
|
|
||||||
|
|
||||||
def test_unknown_platform_non_ci(monkeypatch, capsys):
|
def test_unknown_platform_non_ci(monkeypatch, capsys):
|
||||||
monkeypatch.delenv('CI', raising=False)
|
monkeypatch.delenv("CI", raising=False)
|
||||||
monkeypatch.delenv('BITRISE_BUILD_NUMBER', raising=False)
|
monkeypatch.delenv("BITRISE_BUILD_NUMBER", raising=False)
|
||||||
monkeypatch.delenv('AZURE_HTTP_USER_AGENT', raising=False)
|
monkeypatch.delenv("AZURE_HTTP_USER_AGENT", raising=False)
|
||||||
monkeypatch.delenv('TRAVIS', raising=False)
|
monkeypatch.delenv("TRAVIS", raising=False)
|
||||||
monkeypatch.delenv('APPVEYOR', raising=False)
|
monkeypatch.delenv("APPVEYOR", raising=False)
|
||||||
monkeypatch.delenv('GITHUB_ACTIONS', raising=False)
|
monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
|
||||||
monkeypatch.delenv('GITLAB_CI', raising=False)
|
monkeypatch.delenv("GITLAB_CI", raising=False)
|
||||||
monkeypatch.delenv('CIRCLECI', raising=False)
|
monkeypatch.delenv("CIRCLECI", raising=False)
|
||||||
monkeypatch.delenv('CIBW_PLATFORM', raising=False)
|
monkeypatch.delenv("CIBW_PLATFORM", raising=False)
|
||||||
|
|
||||||
with pytest.raises(SystemExit) as exit:
|
with pytest.raises(SystemExit) as exit:
|
||||||
main()
|
main()
|
||||||
assert exit.value.code == 2
|
assert exit.value.code == 2
|
||||||
_, err = capsys.readouterr()
|
_, err = capsys.readouterr()
|
||||||
|
|
||||||
assert 'cibuildwheel: Unable to detect platform.' in err
|
assert "cibuildwheel: Unable to detect platform." in err
|
||||||
assert 'cibuildwheel should run on your CI server' in err
|
assert "cibuildwheel should run on your CI server" in err
|
||||||
|
|
||||||
|
|
||||||
def test_unknown_platform_on_ci(monkeypatch, capsys):
|
def test_unknown_platform_on_ci(monkeypatch, capsys):
|
||||||
monkeypatch.setenv('CI', 'true')
|
monkeypatch.setenv("CI", "true")
|
||||||
monkeypatch.setattr(sys, 'platform', 'nonexistent')
|
monkeypatch.setattr(sys, "platform", "nonexistent")
|
||||||
monkeypatch.delenv('CIBW_PLATFORM', raising=False)
|
monkeypatch.delenv("CIBW_PLATFORM", raising=False)
|
||||||
|
|
||||||
with pytest.raises(SystemExit) as exit:
|
with pytest.raises(SystemExit) as exit:
|
||||||
main()
|
main()
|
||||||
@@ -42,19 +42,19 @@ def test_unknown_platform_on_ci(monkeypatch, capsys):
|
|||||||
|
|
||||||
|
|
||||||
def test_unknown_platform(monkeypatch, capsys):
|
def test_unknown_platform(monkeypatch, capsys):
|
||||||
monkeypatch.setenv('CIBW_PLATFORM', 'nonexistent')
|
monkeypatch.setenv("CIBW_PLATFORM", "nonexistent")
|
||||||
|
|
||||||
with pytest.raises(SystemExit) as exit:
|
with pytest.raises(SystemExit) as exit:
|
||||||
main()
|
main()
|
||||||
_, err = capsys.readouterr()
|
_, err = capsys.readouterr()
|
||||||
|
|
||||||
assert exit.value.code == 2
|
assert exit.value.code == 2
|
||||||
assert 'cibuildwheel: Unsupported platform: nonexistent' in err
|
assert "cibuildwheel: Unsupported platform: nonexistent" in err
|
||||||
|
|
||||||
|
|
||||||
def test_platform_argument(platform, intercepted_build_args, monkeypatch):
|
def test_platform_argument(platform, intercepted_build_args, monkeypatch):
|
||||||
monkeypatch.setenv('CIBW_PLATFORM', 'nonexistent')
|
monkeypatch.setenv("CIBW_PLATFORM", "nonexistent")
|
||||||
monkeypatch.setattr(sys, 'argv', sys.argv + ['--platform', platform])
|
monkeypatch.setattr(sys, "argv", sys.argv + ["--platform", platform])
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
@@ -72,24 +72,24 @@ def test_archs_default(platform, intercepted_build_args, monkeypatch):
|
|||||||
main()
|
main()
|
||||||
build_options = intercepted_build_args.args[0]
|
build_options = intercepted_build_args.args[0]
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
assert build_options.architectures == {Architecture.x86_64, Architecture.i686}
|
assert build_options.architectures == {Architecture.x86_64, Architecture.i686}
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
assert build_options.architectures == {Architecture.AMD64, Architecture.x86}
|
assert build_options.architectures == {Architecture.AMD64, Architecture.x86}
|
||||||
else:
|
else:
|
||||||
assert build_options.architectures == {Architecture.x86_64}
|
assert build_options.architectures == {Architecture.x86_64}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('use_env_var', [False, True])
|
@pytest.mark.parametrize("use_env_var", [False, True])
|
||||||
def test_archs_argument(platform, intercepted_build_args, monkeypatch, use_env_var):
|
def test_archs_argument(platform, intercepted_build_args, monkeypatch, use_env_var):
|
||||||
|
|
||||||
if use_env_var:
|
if use_env_var:
|
||||||
monkeypatch.setenv('CIBW_ARCHS', 'ppc64le')
|
monkeypatch.setenv("CIBW_ARCHS", "ppc64le")
|
||||||
else:
|
else:
|
||||||
monkeypatch.setenv('CIBW_ARCHS', 'unused')
|
monkeypatch.setenv("CIBW_ARCHS", "unused")
|
||||||
monkeypatch.setattr(sys, 'argv', sys.argv + ['--archs', 'ppc64le'])
|
monkeypatch.setattr(sys, "argv", sys.argv + ["--archs", "ppc64le"])
|
||||||
|
|
||||||
if platform in {'macos', 'windows'}:
|
if platform in {"macos", "windows"}:
|
||||||
with pytest.raises(SystemExit) as exit:
|
with pytest.raises(SystemExit) as exit:
|
||||||
main()
|
main()
|
||||||
assert exit.value.args == (4,)
|
assert exit.value.args == (4,)
|
||||||
@@ -101,50 +101,50 @@ def test_archs_argument(platform, intercepted_build_args, monkeypatch, use_env_v
|
|||||||
|
|
||||||
|
|
||||||
def test_archs_platform_specific(platform, intercepted_build_args, monkeypatch):
|
def test_archs_platform_specific(platform, intercepted_build_args, monkeypatch):
|
||||||
monkeypatch.setenv('CIBW_ARCHS', 'unused')
|
monkeypatch.setenv("CIBW_ARCHS", "unused")
|
||||||
monkeypatch.setenv('CIBW_ARCHS_LINUX', 'ppc64le')
|
monkeypatch.setenv("CIBW_ARCHS_LINUX", "ppc64le")
|
||||||
monkeypatch.setenv('CIBW_ARCHS_WINDOWS', 'x86')
|
monkeypatch.setenv("CIBW_ARCHS_WINDOWS", "x86")
|
||||||
monkeypatch.setenv('CIBW_ARCHS_MACOS', 'x86_64')
|
monkeypatch.setenv("CIBW_ARCHS_MACOS", "x86_64")
|
||||||
|
|
||||||
main()
|
main()
|
||||||
build_options = intercepted_build_args.args[0]
|
build_options = intercepted_build_args.args[0]
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
assert build_options.architectures == {Architecture.ppc64le}
|
assert build_options.architectures == {Architecture.ppc64le}
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
assert build_options.architectures == {Architecture.x86}
|
assert build_options.architectures == {Architecture.x86}
|
||||||
elif platform == 'macos':
|
elif platform == "macos":
|
||||||
assert build_options.architectures == {Architecture.x86_64}
|
assert build_options.architectures == {Architecture.x86_64}
|
||||||
|
|
||||||
|
|
||||||
def test_archs_platform_native(platform, intercepted_build_args, monkeypatch):
|
def test_archs_platform_native(platform, intercepted_build_args, monkeypatch):
|
||||||
monkeypatch.setenv('CIBW_ARCHS', 'native')
|
monkeypatch.setenv("CIBW_ARCHS", "native")
|
||||||
|
|
||||||
main()
|
main()
|
||||||
build_options = intercepted_build_args.args[0]
|
build_options = intercepted_build_args.args[0]
|
||||||
|
|
||||||
if platform in {'linux', 'macos'}:
|
if platform in {"linux", "macos"}:
|
||||||
assert build_options.architectures == {Architecture.x86_64}
|
assert build_options.architectures == {Architecture.x86_64}
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
assert build_options.architectures == {Architecture.AMD64}
|
assert build_options.architectures == {Architecture.AMD64}
|
||||||
|
|
||||||
|
|
||||||
def test_archs_platform_auto64(platform, intercepted_build_args, monkeypatch):
|
def test_archs_platform_auto64(platform, intercepted_build_args, monkeypatch):
|
||||||
monkeypatch.setenv('CIBW_ARCHS', 'auto64')
|
monkeypatch.setenv("CIBW_ARCHS", "auto64")
|
||||||
|
|
||||||
main()
|
main()
|
||||||
build_options = intercepted_build_args.args[0]
|
build_options = intercepted_build_args.args[0]
|
||||||
|
|
||||||
if platform in {'linux', 'macos'}:
|
if platform in {"linux", "macos"}:
|
||||||
assert build_options.architectures == {Architecture.x86_64}
|
assert build_options.architectures == {Architecture.x86_64}
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
assert build_options.architectures == {Architecture.AMD64}
|
assert build_options.architectures == {Architecture.AMD64}
|
||||||
|
|
||||||
|
|
||||||
def test_archs_platform_auto32(platform, intercepted_build_args, monkeypatch):
|
def test_archs_platform_auto32(platform, intercepted_build_args, monkeypatch):
|
||||||
monkeypatch.setenv('CIBW_ARCHS', 'auto32')
|
monkeypatch.setenv("CIBW_ARCHS", "auto32")
|
||||||
|
|
||||||
if platform == 'macos':
|
if platform == "macos":
|
||||||
with pytest.raises(SystemExit) as exit:
|
with pytest.raises(SystemExit) as exit:
|
||||||
main()
|
main()
|
||||||
assert exit.value.args == (4,)
|
assert exit.value.args == (4,)
|
||||||
@@ -154,19 +154,19 @@ def test_archs_platform_auto32(platform, intercepted_build_args, monkeypatch):
|
|||||||
|
|
||||||
build_options = intercepted_build_args.args[0]
|
build_options = intercepted_build_args.args[0]
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
assert build_options.architectures == {Architecture.i686}
|
assert build_options.architectures == {Architecture.i686}
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
assert build_options.architectures == {Architecture.x86}
|
assert build_options.architectures == {Architecture.x86}
|
||||||
|
|
||||||
|
|
||||||
def test_archs_platform_all(platform, intercepted_build_args, monkeypatch):
|
def test_archs_platform_all(platform, intercepted_build_args, monkeypatch):
|
||||||
monkeypatch.setenv('CIBW_ARCHS', 'all')
|
monkeypatch.setenv("CIBW_ARCHS", "all")
|
||||||
|
|
||||||
main()
|
main()
|
||||||
build_options = intercepted_build_args.args[0]
|
build_options = intercepted_build_args.args[0]
|
||||||
|
|
||||||
if platform == 'linux':
|
if platform == "linux":
|
||||||
assert build_options.architectures == {
|
assert build_options.architectures == {
|
||||||
Architecture.x86_64,
|
Architecture.x86_64,
|
||||||
Architecture.i686,
|
Architecture.i686,
|
||||||
@@ -174,9 +174,9 @@ def test_archs_platform_all(platform, intercepted_build_args, monkeypatch):
|
|||||||
Architecture.ppc64le,
|
Architecture.ppc64le,
|
||||||
Architecture.s390x,
|
Architecture.s390x,
|
||||||
}
|
}
|
||||||
elif platform == 'windows':
|
elif platform == "windows":
|
||||||
assert build_options.architectures == {Architecture.x86, Architecture.AMD64}
|
assert build_options.architectures == {Architecture.x86, Architecture.AMD64}
|
||||||
elif platform == 'macos':
|
elif platform == "macos":
|
||||||
assert build_options.architectures == {
|
assert build_options.architectures == {
|
||||||
Architecture.x86_64,
|
Architecture.x86_64,
|
||||||
Architecture.arm64,
|
Architecture.arm64,
|
||||||
|
|||||||
@@ -9,16 +9,16 @@ from cibuildwheel.__main__ import main
|
|||||||
|
|
||||||
@pytest.fixture(autouse=True, scope="function")
|
@pytest.fixture(autouse=True, scope="function")
|
||||||
def fake_package_dir(monkeypatch, tmp_path):
|
def fake_package_dir(monkeypatch, tmp_path):
|
||||||
'''
|
"""
|
||||||
Set up a fake project
|
Set up a fake project
|
||||||
'''
|
"""
|
||||||
|
|
||||||
local_path = tmp_path / "tmp_project"
|
local_path = tmp_path / "tmp_project"
|
||||||
local_path.mkdir()
|
local_path.mkdir()
|
||||||
|
|
||||||
local_path.joinpath("setup.py").touch()
|
local_path.joinpath("setup.py").touch()
|
||||||
|
|
||||||
monkeypatch.setattr(sys, 'argv', ['cibuildwheel', str(local_path)])
|
monkeypatch.setattr(sys, "argv", ["cibuildwheel", str(local_path)])
|
||||||
|
|
||||||
return local_path
|
return local_path
|
||||||
|
|
||||||
@@ -29,14 +29,14 @@ def test_no_override(platform, monkeypatch, intercepted_build_args):
|
|||||||
|
|
||||||
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
intercepted_build_selector = intercepted_build_args.args[0].build_selector
|
||||||
|
|
||||||
assert intercepted_build_selector('cp39-win32')
|
assert intercepted_build_selector("cp39-win32")
|
||||||
assert intercepted_build_selector('cp36-win32')
|
assert intercepted_build_selector("cp36-win32")
|
||||||
|
|
||||||
assert intercepted_build_selector.requires_python is None
|
assert intercepted_build_selector.requires_python is None
|
||||||
|
|
||||||
|
|
||||||
def test_override_env(platform, monkeypatch, intercepted_build_args):
|
def test_override_env(platform, monkeypatch, intercepted_build_args):
|
||||||
monkeypatch.setenv('CIBW_PROJECT_REQUIRES_PYTHON', '>=3.8')
|
monkeypatch.setenv("CIBW_PROJECT_REQUIRES_PYTHON", ">=3.8")
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
@@ -44,8 +44,8 @@ def test_override_env(platform, monkeypatch, intercepted_build_args):
|
|||||||
|
|
||||||
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
|
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
|
||||||
|
|
||||||
assert intercepted_build_selector('cp39-win32')
|
assert intercepted_build_selector("cp39-win32")
|
||||||
assert not intercepted_build_selector('cp36-win32')
|
assert not intercepted_build_selector("cp36-win32")
|
||||||
|
|
||||||
|
|
||||||
def test_override_setup_cfg(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
def test_override_setup_cfg(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
||||||
@@ -65,8 +65,8 @@ def test_override_setup_cfg(platform, monkeypatch, intercepted_build_args, fake_
|
|||||||
|
|
||||||
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
|
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
|
||||||
|
|
||||||
assert intercepted_build_selector('cp39-win32')
|
assert intercepted_build_selector("cp39-win32")
|
||||||
assert not intercepted_build_selector('cp36-win32')
|
assert not intercepted_build_selector("cp36-win32")
|
||||||
|
|
||||||
|
|
||||||
def test_override_pyproject_toml(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
def test_override_pyproject_toml(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
||||||
@@ -86,8 +86,8 @@ def test_override_pyproject_toml(platform, monkeypatch, intercepted_build_args,
|
|||||||
|
|
||||||
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
|
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.8")
|
||||||
|
|
||||||
assert intercepted_build_selector('cp39-win32')
|
assert intercepted_build_selector("cp39-win32")
|
||||||
assert not intercepted_build_selector('cp36-win32')
|
assert not intercepted_build_selector("cp36-win32")
|
||||||
|
|
||||||
|
|
||||||
def test_override_setup_py_simple(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
def test_override_setup_py_simple(platform, monkeypatch, intercepted_build_args, fake_package_dir):
|
||||||
@@ -111,5 +111,5 @@ def test_override_setup_py_simple(platform, monkeypatch, intercepted_build_args,
|
|||||||
|
|
||||||
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.7")
|
assert intercepted_build_selector.requires_python == SpecifierSet(">=3.7")
|
||||||
|
|
||||||
assert intercepted_build_selector('cp39-win32')
|
assert intercepted_build_selector("cp39-win32")
|
||||||
assert not intercepted_build_selector('cp36-win32')
|
assert not intercepted_build_selector("cp36-win32")
|
||||||
|
|||||||
Reference in New Issue
Block a user