Skip to content

Commit 2b1dbe1

Browse files
committed
coin: ci code improvements for build and test
The current patch modifies the structure for the code in charge of building and testing pyside in the CI. Instructions within 'coin_test_instructions.py' and 'coin_build_instructions.py' were duplicated, and other slightly modified, so the code was standarized and placed in 'coin/instructions_utils.py', so the first two files could be heavily simplified. A couple of helper functions were removed from build_scripts/utils.py in order to reduce the code in the general build utilities for the project. Pick-to: 6.8 Change-Id: I0cd4bc51edb2e28c5e81a0be8a27be6cb4027bfd Reviewed-by: Simo Fält <[email protected]>
1 parent d42c7cc commit 2b1dbe1

File tree

4 files changed

+285
-306
lines changed

4 files changed

+285
-306
lines changed

build_scripts/utils.py

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -945,48 +945,6 @@ def get_python_dict(python_script_path):
945945
raise
946946

947947

948-
def get_qtci_virtualEnv(python_ver, host, hostArch, targetArch):
949-
_pExe = "python"
950-
_env = f"{os.environ.get('PYSIDE_VIRTUALENV') or 'env'+python_ver}"
951-
env_python = f"{_env}/bin/python"
952-
env_pip = f"{_env}/bin/pip"
953-
954-
if host == "Windows":
955-
log.info("New virtualenv to build {targetArch} in {hostArch} host")
956-
_pExe = "python.exe"
957-
# With windows we are creating building 32-bit target in 64-bit host
958-
if hostArch == "X86_64" and targetArch == "X86":
959-
if python_ver.startswith("3"):
960-
var = f"PYTHON{python_ver}-32_PATH"
961-
log.info(f"Try to find python from {var} env variable")
962-
_path = Path(os.getenv(var, ""))
963-
_pExe = _path / "python.exe"
964-
if not _pExe.is_file():
965-
log.warning(f"Can't find python.exe from {_pExe}, using default python3")
966-
_pExe = Path(os.getenv("PYTHON3_32_PATH")) / "python.exe"
967-
else:
968-
_pExe = Path(os.getenv("PYTHON2_32_PATH")) / "python.exe"
969-
else:
970-
if python_ver.startswith("3"):
971-
var = f"PYTHON{python_ver}-64_PATH"
972-
log.info(f"Try to find python from {var} env variable")
973-
_path = Path(os.getenv(var, ""))
974-
_pExe = _path / "python.exe"
975-
if not _pExe.is_file():
976-
log.warning(f"Can't find python.exe from {_pExe}, using default python3")
977-
_pExe = Path(os.getenv("PYTHON3_PATH")) / "python.exe"
978-
env_python = f"{_env}\\Scripts\\python.exe"
979-
env_pip = f"{_env}\\Scripts\\pip.exe"
980-
else:
981-
_pExe = f"python{python_ver}"
982-
try:
983-
run_instruction([_pExe, "--version"], f"Failed to guess python version {_pExe}")
984-
except Exception as e:
985-
print(f"Exception {type(e).__name__}: {e}")
986-
_pExe = "python3"
987-
return (_pExe, _env, env_pip, env_python)
988-
989-
990948
def run_instruction(instruction, error, initial_env=None):
991949
if initial_env is None:
992950
initial_env = os.environ
@@ -997,26 +955,6 @@ def run_instruction(instruction, error, initial_env=None):
997955
exit(result)
998956

999957

1000-
def get_ci_qtpaths_path(ci_install_dir, ci_host_os):
1001-
qtpaths_path = f"--qtpaths={ci_install_dir}"
1002-
if ci_host_os == "MacOS":
1003-
return f"{qtpaths_path}/bin/qtpaths"
1004-
elif ci_host_os == "Windows":
1005-
return f"{qtpaths_path}\\bin\\qtpaths.exe"
1006-
else:
1007-
return f"{qtpaths_path}/bin/qtpaths"
1008-
1009-
1010-
def get_ci_qmake_path(ci_install_dir, ci_host_os):
1011-
qmake_path = f"--qmake={ci_install_dir}"
1012-
if ci_host_os == "MacOS":
1013-
return f"{qmake_path}/bin/qmake"
1014-
elif ci_host_os == "Windows":
1015-
return f"{qmake_path}\\bin\\qmake.exe"
1016-
else:
1017-
return f"{qmake_path}/bin/qmake"
1018-
1019-
1020958
def parse_cmake_conf_assignments_by_key(source_dir):
1021959
"""
1022960
Parses a .cmake.conf file that contains set(foo "bar") assignments

coin/instructions_utils.py

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
# Copyright (C) 2022 The Qt Company Ltd.
2+
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3+
from __future__ import annotations
4+
5+
import calendar
6+
import datetime
7+
import os
8+
import site
9+
import sys
10+
from pathlib import Path
11+
12+
from build_scripts.options import has_option, option_value
13+
from build_scripts.utils import (parse_cmake_conf_assignments_by_key,
14+
remove_tree, run_instruction)
15+
16+
17+
class CI:
18+
def __init__(self):
19+
# Values must match COIN thrift
20+
self.HOST_OS = option_value("os")
21+
self.TARGET_OS = option_value("targetOs")
22+
self.HOST_ARCH = option_value("hostArch")
23+
self.TARGET_ARCH = option_value("targetArch")
24+
self.HOST_OS_VER = option_value("osVer")
25+
self.ENV_INSTALL_DIR = option_value("instdir")
26+
self.ENV_AGENT_DIR = option_value("agentdir") or "."
27+
self.COMPILER = option_value("compiler")
28+
self.USE_SCCACHE = option_value("compiler-launcher")
29+
self.INTEGRATION_ID = option_value("coinIntegrationId") or str(
30+
calendar.timegm(datetime.datetime.now().timetuple())
31+
)
32+
self.FEATURES = []
33+
_ci_features = option_value("features")
34+
if _ci_features is not None:
35+
for f in _ci_features.split(", "):
36+
self.FEATURES.append(f)
37+
self.RELEASE_CONF = has_option("packaging")
38+
self.TEST_PHASE = option_value("phase")
39+
if self.TEST_PHASE not in ["ALL", "BUILD"]:
40+
self.TEST_PHASE = "ALL"
41+
42+
43+
def get_ci_exe_path(ci_install_dir, ci_host_os, qtexe):
44+
"""
45+
qtexe can only be 'qmake' or 'qtpaths'
46+
"""
47+
ext = ""
48+
if ci_host_os == "Windows":
49+
ext = ".exe"
50+
51+
_path = Path(ci_install_dir) / "bin" / f"{qtexe}{ext}"
52+
53+
return f"--{qtexe}={_path}"
54+
55+
56+
def get_env_or_raise(name: str) -> str:
57+
o = os.getenv(name)
58+
if o is None:
59+
raise Exception(f"Variable not defined: {name}")
60+
return o
61+
62+
63+
def get_qtci_virtualenv(python_ver, log, host, host_arch, target_arch):
64+
_exe = "python"
65+
_env = os.environ.get("PYSIDE_VIRTUALENV") or f"env{python_ver}"
66+
env_python = f"{_env}/bin/python"
67+
env_pip = f"{_env}/bin/pip"
68+
69+
if host == "Windows":
70+
log.info("New virtualenv to build {target_arch} in {host_arch} host")
71+
_exe = "python.exe"
72+
if python_ver.startswith("3"):
73+
var = f"PYTHON{python_ver}-64_PATH"
74+
log.info(f"Try to find python from {var} env variable")
75+
_path = Path(os.getenv(var, ""))
76+
_exe = _path / "python.exe"
77+
if not _exe.is_file():
78+
log.warning(f"Can't find python.exe from {_exe}, using default python3")
79+
_exe = Path(get_env_or_raise("PYTHON3_PATH")) / "python.exe"
80+
env_python = rf"{_env}\Scripts\python.exe"
81+
env_pip = rf"{_env}\Scripts\pip.exe"
82+
else:
83+
_exe = f"python{python_ver}"
84+
try:
85+
run_instruction([_exe, "--version"], f"Failed to guess python version {_exe}")
86+
except Exception as e:
87+
print(f"Exception {type(e).__name__}: {e}")
88+
_exe = "python3"
89+
return (_exe, _env, env_pip, env_python)
90+
91+
92+
def get_current_script_path():
93+
"""Returns the absolute path containing this script."""
94+
try:
95+
this_file = __file__
96+
except NameError:
97+
this_file = sys.argv[0]
98+
this_file = Path(this_file).resolve()
99+
return this_file.parents[0]
100+
101+
102+
def is_snapshot_build():
103+
"""
104+
Returns True if project needs to be built with --snapshot-build
105+
106+
This is true if the version found in .cmake.conf is not a
107+
pre-release version (no alphas, betas).
108+
109+
This eliminates the need to remove the --snapshot-build option
110+
on a per-release branch basis (less things to remember to do
111+
for a release).
112+
"""
113+
# This returns pyside-setup/coin/ so we go one level down
114+
# to get the root of the repo
115+
setup_script_dir = get_current_script_path()
116+
pyside_project_dir = setup_script_dir / ".." / "sources" / "pyside6"
117+
118+
d = parse_cmake_conf_assignments_by_key(str(pyside_project_dir))
119+
release_version_type = d.get("pyside_PRE_RELEASE_VERSION_TYPE")
120+
pre_release_version = d.get("pyside_PRE_RELEASE_VERSION")
121+
if pre_release_version and release_version_type:
122+
return True
123+
return False
124+
125+
126+
def get_architecture(ci):
127+
return "32" if ci.TARGET_ARCH == "X86" else "64"
128+
129+
130+
def get_python_version(ci):
131+
python_ver = "3"
132+
if ci.TARGET_OS == "Linux" and ci.HOST_ARCH != "aarch64":
133+
python_ver = "3.11"
134+
elif ci.TARGET_OS == "Windows":
135+
python_ver = "3.10.0"
136+
return python_ver
137+
138+
139+
def remove_variables(vars):
140+
for env_var in vars:
141+
if os.environ.get(env_var):
142+
del os.environ[env_var]
143+
144+
145+
def setup_virtualenv(python, exe, env, pip, log):
146+
run_instruction(
147+
[str(python), "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
148+
"Failed to pin virtualenv",
149+
)
150+
# installing to user base might not be in PATH by default.
151+
env_path = Path(str(site.USER_BASE)) / "bin"
152+
v_env = env_path / "virtualenv"
153+
if sys.platform == "win32":
154+
env_path = os.path.join(site.USER_BASE, "Scripts")
155+
v_env = os.path.join(env_path, "virtualenv.exe")
156+
try:
157+
run_instruction([str(v_env), "--version"], "Using default virtualenv")
158+
except Exception as e:
159+
log.info("Failed to use the default virtualenv")
160+
log.info(f"{type(e).__name__}: {e}")
161+
v_env = "virtualenv"
162+
run_instruction([str(v_env), "-p", str(exe), str(env)], "Failed to create virtualenv")
163+
# Pip is always upgraded when CI template is provisioned,
164+
# upgrading it in later phase may cause perm issue
165+
run_instruction(
166+
[str(pip), "install", "-r", "requirements.txt"], "Failed to install dependencies"
167+
)
168+
169+
170+
def call_setup(python_ver, ci, phase, log, buildnro=0):
171+
print("call_setup")
172+
print("python_ver", python_ver)
173+
print("phase", phase)
174+
exe, env, pip, env_python = get_qtci_virtualenv(
175+
python_ver, log, ci.HOST_OS, ci.HOST_ARCH, ci.TARGET_ARCH
176+
)
177+
178+
if phase not in ["BUILD", "TEST"]:
179+
sys.exit(1)
180+
181+
remove_tree(env, True)
182+
# Pinning the virtualenv before creating one
183+
# Use pip3 if possible while pip seems to install the virtualenv to wrong dir in some OS
184+
python = "python3"
185+
if sys.platform == "win32":
186+
python = Path(get_env_or_raise("PYTHON3_PATH")) / "python.exe"
187+
188+
if phase == "BUILD":
189+
setup_virtualenv(python, exe, env, pip, log)
190+
elif phase == "TEST":
191+
192+
if ci.HOST_OS == "MacOS" and ci.HOST_ARCH == "ARM64":
193+
v_env = "virtualenv"
194+
run_instruction([str(v_env), "-p", str(exe), str(env)], "Failed to create virtualenv")
195+
run_instruction(
196+
[pip, "install", "-r", "requirements.txt"], "Failed to install dependencies"
197+
)
198+
else:
199+
setup_virtualenv(python, exe, env, pip, log)
200+
# Install distro to replace missing platform.linux_distribution() in python3.8
201+
run_instruction([pip, "install", "distro"], "Failed to install distro")
202+
203+
if phase == "BUILD":
204+
cmd = [
205+
env_python,
206+
"-u",
207+
"setup.py",
208+
"build",
209+
"--standalone",
210+
"--unity",
211+
"--build-tests",
212+
"--log-level=verbose",
213+
"--limited-api=yes",
214+
]
215+
216+
if ci.TARGET_ARCH == "X86_64-ARM64":
217+
cmd += ["--macos-arch='x86_64;arm64'"]
218+
219+
if ci.USE_SCCACHE:
220+
cmd += [f"--compiler-launcher={ci.USE_SCCACHE}"]
221+
222+
if is_snapshot_build():
223+
cmd += ["--snapshot-build"]
224+
225+
qtpaths_path = get_ci_exe_path(ci.ENV_INSTALL_DIR, ci.HOST_OS, "qtpaths")
226+
cmd.append(qtpaths_path)
227+
228+
# Due to certain older CMake versions generating very long paths
229+
# (at least with CMake 3.6.2) when using the export() function,
230+
# pass the shorter paths option on Windows so we don't hit
231+
# the path character length limit (260).
232+
if ci.HOST_OS == "Windows":
233+
cmd += ["--shorter-paths"]
234+
235+
cmd += ["--package-timestamp=" + ci.INTEGRATION_ID]
236+
237+
env = os.environ
238+
run_instruction(cmd, "Failed to run setup.py for build", initial_env=env)
239+
elif phase == "TEST":
240+
cmd = [
241+
env_python,
242+
"testrunner.py",
243+
"test",
244+
"--blacklist",
245+
"build_history/blacklist.txt",
246+
f"--buildno={buildnro}",
247+
]
248+
run_instruction(cmd, "Failed to run testrunner.py")
249+
250+
qmake_path = get_ci_exe_path(ci.ENV_INSTALL_DIR, ci.HOST_OS, "qmake")
251+
252+
# Try to install built wheels, and build some buildable examples.
253+
if ci.RELEASE_CONF:
254+
wheel_tester_path = os.path.join("testing", "wheel_tester.py")
255+
# Run the test for the new set of wheels
256+
cmd = [env_python, wheel_tester_path, qmake_path, "--wheels-dir=dist", "--new"]
257+
run_instruction(cmd, "Error while running wheel_tester.py on new wheels")

0 commit comments

Comments
 (0)