Skip to content

Commit 1c96a60

Browse files
committed
ci.py first version
1 parent b66d067 commit 1c96a60

File tree

1 file changed

+340
-0
lines changed

1 file changed

+340
-0
lines changed

tools/ci.py

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
#!/usr/bin/python3
2+
3+
from pathlib import Path
4+
from typing import List, Union
5+
import subprocess
6+
import os
7+
import stat
8+
from shutil import rmtree, copytree, ignore_patterns
9+
import argparse
10+
11+
12+
_is_windows = os.name == 'nt'
13+
_boost_root = Path(os.path.expanduser('~')).joinpath('boost-root')
14+
_b2_command = str(_boost_root.joinpath('b2'))
15+
16+
17+
def _run(args: List[str]) -> None:
18+
print('+ ', args, flush=True)
19+
subprocess.run(args, check=True)
20+
21+
22+
def _mkdir_and_cd(path: Path) -> None:
23+
os.makedirs(str(path), exist_ok=True)
24+
os.chdir(str(path))
25+
26+
27+
def _cmake_bool(value: bool) -> str:
28+
return 'ON' if value else 'OFF'
29+
30+
31+
def _remove_readonly(func, path, _):
32+
os.chmod(path, stat.S_IWRITE)
33+
func(path)
34+
35+
36+
def _build_prefix_path(*paths: Union[str, Path]) -> str:
37+
return ';'.join(str(p) for p in paths)
38+
39+
40+
def _str2bool(v: Union[bool, str]) -> bool:
41+
if isinstance(v, bool):
42+
return v
43+
elif v == '1':
44+
return True
45+
elif v == '0':
46+
return False
47+
else:
48+
raise argparse.ArgumentTypeError('Boolean value expected.')
49+
50+
51+
def _deduce_boost_branch() -> str:
52+
# Are we in GitHub Actions?
53+
if os.environ.get('GITHUB_ACTIONS') is not None:
54+
ci = 'GitHub Actions'
55+
ref = os.environ.get('GITHUB_BASE_REF', '') or os.environ.get('GITHUB_REF', '')
56+
res = 'master' if ref == 'master' or ref.endswith('/master') else 'develop'
57+
elif os.environ.get('DRONE') is not None:
58+
ref = os.environ.get('DRONE_BRANCH', '')
59+
ci = 'Drone'
60+
res = 'master' if ref == 'master' else 'develop'
61+
else:
62+
ci = 'Unknown'
63+
ref = ''
64+
res = 'develop'
65+
66+
print('+ Found CI {}, ref={}, deduced branch {}'.format(ci, ref, res))
67+
68+
return res
69+
70+
71+
def _install_boost(
72+
source_dir: Path
73+
) -> None:
74+
assert source_dir.is_absolute()
75+
assert not _boost_root.exists()
76+
lib_dir = _boost_root.joinpath('libs', 'redis')
77+
branch = _deduce_boost_branch()
78+
79+
# Clone Boost
80+
_run(['git', 'clone', '-b', branch, '--depth', '1', 'https://github.com/boostorg/boost.git', str(_boost_root)])
81+
os.chdir(str(_boost_root))
82+
83+
# Put our library inside boost root
84+
if lib_dir.exists():
85+
rmtree(str(lib_dir), onerror=_remove_readonly)
86+
copytree(
87+
str(source_dir),
88+
str(lib_dir),
89+
ignore=ignore_patterns('__build*__', '.git'),
90+
dirs_exist_ok=True
91+
)
92+
93+
# Install Boost dependencies
94+
_run(["git", "config", "submodule.fetchJobs", "8"])
95+
_run(["git", "submodule", "update", "-q", "--init", "tools/boostdep"])
96+
_run(["python", "tools/boostdep/depinst/depinst.py", "--include", "examples", "redis"])
97+
98+
# Bootstrap
99+
if _is_windows:
100+
_run(['cmd', '/q', '/c', 'bootstrap.bat'])
101+
else:
102+
_run(['bash', 'bootstrap.sh'])
103+
_run([_b2_command, 'headers'])
104+
105+
106+
def _build_b2_distro(
107+
install_prefix: Path
108+
):
109+
os.chdir(str(_boost_root))
110+
_run([
111+
_b2_command,
112+
'--prefix={}'.format(install_prefix),
113+
'--with-system',
114+
'-d0',
115+
'install'
116+
])
117+
118+
119+
def _run_cmake_superproject_tests(
120+
install_prefix: Path,
121+
generator: str,
122+
build_type: str,
123+
cxxstd: str,
124+
build_shared_libs: bool = False
125+
):
126+
_mkdir_and_cd(_boost_root.joinpath('__build_cmake_test__'))
127+
_run([
128+
'cmake',
129+
'-G',
130+
generator,
131+
'-DCMAKE_BUILD_TYPE={}'.format(build_type),
132+
'-DCMAKE_CXX_STANDARD={}'.format(cxxstd),
133+
'-DBOOST_INCLUDE_LIBRARIES=redis',
134+
'-DBUILD_SHARED_LIBS={}'.format(_cmake_bool(build_shared_libs)),
135+
'-DCMAKE_INSTALL_PREFIX={}'.format(install_prefix),
136+
'-DBUILD_TESTING=ON',
137+
'-DBoost_VERBOSE=ON',
138+
'-DCMAKE_INSTALL_MESSAGE=NEVER',
139+
'..'
140+
])
141+
_run(['cmake', '--build', '.', '--target', 'tests', '--config', build_type])
142+
_run(['ctest', '--output-on-failure', '--build-config', build_type])
143+
144+
145+
def _install_cmake_distro(build_type: str):
146+
_run(['cmake', '--build', '.', '--target', 'install', '--config', build_type])
147+
148+
149+
def _run_cmake_standalone_tests(
150+
b2_distro: Path,
151+
generator: str,
152+
build_type: str,
153+
cxxstd: str,
154+
build_shared_libs: bool = False
155+
):
156+
_mkdir_and_cd(_boost_root.joinpath('libs', 'redis', '__build_standalone__'))
157+
_run([
158+
'cmake',
159+
'-DCMAKE_PREFIX_PATH={}'.format(_build_prefix_path(b2_distro)),
160+
'-DCMAKE_BUILD_TYPE={}'.format(build_type),
161+
'-DBUILD_SHARED_LIBS={}'.format(_cmake_bool(build_shared_libs)),
162+
'-DCMAKE_CXX_STANDARD={}'.format(cxxstd),
163+
'-G',
164+
generator,
165+
'..'
166+
])
167+
_run(['cmake', '--build', '.'])
168+
_run(['ctest', '--output-on-failure', '--build-config', build_type])
169+
170+
171+
def _run_cmake_add_subdirectory_tests(
172+
generator: str,
173+
build_type: str,
174+
build_shared_libs: bool = False
175+
):
176+
test_folder = _boost_root.joinpath('libs', 'redis', 'test', 'cmake_test', '__build_cmake_subdir_test__')
177+
_mkdir_and_cd(test_folder)
178+
_run([
179+
'cmake',
180+
'-G',
181+
generator,
182+
'-DBOOST_CI_INSTALL_TEST=OFF',
183+
'-DCMAKE_BUILD_TYPE={}'.format(build_type),
184+
'-DBUILD_SHARED_LIBS={}'.format(_cmake_bool(build_shared_libs)),
185+
'..'
186+
])
187+
_run(['cmake', '--build', '.', '--config', build_type])
188+
_run(['ctest', '--output-on-failure', '--build-config', build_type])
189+
190+
191+
def _run_cmake_find_package_tests(
192+
cmake_distro: Path,
193+
generator: str,
194+
build_type: str,
195+
build_shared_libs: bool = False
196+
):
197+
_mkdir_and_cd(_boost_root.joinpath('libs', 'redis', 'test', 'cmake_test', '__build_cmake_install_test__'))
198+
_run([
199+
'cmake',
200+
'-G',
201+
generator,
202+
'-DBOOST_CI_INSTALL_TEST=ON',
203+
'-DCMAKE_BUILD_TYPE={}'.format(build_type),
204+
'-DBUILD_SHARED_LIBS={}'.format(_cmake_bool(build_shared_libs)),
205+
'-DCMAKE_PREFIX_PATH={}'.format(_build_prefix_path(cmake_distro)),
206+
'..'
207+
])
208+
_run(['cmake', '--build', '.', '--config', build_type])
209+
_run(['ctest', '--output-on-failure', '--build-config', build_type])
210+
211+
212+
def _run_cmake_b2_find_package_tests(
213+
b2_distro: Path,
214+
generator: str,
215+
build_type: str,
216+
build_shared_libs: bool = False
217+
):
218+
_mkdir_and_cd(_boost_root.joinpath('libs', 'redis', 'test', 'cmake_b2_test', '__build_cmake_b2_test__'))
219+
_run([
220+
'cmake',
221+
'-G',
222+
generator,
223+
'-DCMAKE_PREFIX_PATH={}'.format(_build_prefix_path(b2_distro)),
224+
'-DCMAKE_BUILD_TYPE={}'.format(build_type),
225+
'-DBUILD_SHARED_LIBS={}'.format(_cmake_bool(build_shared_libs)),
226+
'-DBUILD_TESTING=ON',
227+
'..'
228+
])
229+
_run(['cmake', '--build', '.', '--config', build_type])
230+
_run(['ctest', '--output-on-failure', '--build-config', build_type])
231+
232+
233+
def _run_b2_tests(
234+
toolset: str,
235+
cxxstd: str,
236+
variant: str,
237+
stdlib: str = 'native',
238+
address_model: str = '64',
239+
address_sanitizer: bool = False,
240+
undefined_sanitizer: bool = False,
241+
):
242+
os.chdir(str(_boost_root))
243+
_run([
244+
_b2_command,
245+
'--abbreviate-paths',
246+
'toolset={}'.format(toolset),
247+
'cxxstd={}'.format(cxxstd),
248+
'address-model={}'.format(address_model),
249+
'variant={}'.format(variant),
250+
'stdlib={}'.format(stdlib),
251+
] + (['address-sanitizer=norecover'] if address_sanitizer else []) # can only be disabled by omitting the arg
252+
+ (['undefined-sanitizer=norecover'] if undefined_sanitizer else []) # can only be disabled by omitting the arg
253+
+ [
254+
'warnings-as-errors=on',
255+
'-j4',
256+
'libs/redis/test',
257+
'libs/redis/example'
258+
])
259+
260+
# Get Boost
261+
# Generate "pre-built" b2 distro
262+
# Build the library, run the tests, and install, from the superproject
263+
# Library tests, using the b2 Boost distribution generated before (this tests our normal dev workflow)
264+
# Subdir tests, using add_subdirectory() (lib can be consumed using add_subdirectory)
265+
# Subdir tests, using find_package with the library installed in the previous step
266+
# (library can be consumed using find_package on a distro built by cmake)
267+
268+
# Subdir tests, using find_package with the b2 distribution
269+
# (library can be consumed using find_package on a distro built by b2)
270+
271+
272+
273+
def main():
274+
parser = argparse.ArgumentParser()
275+
subparsers = parser.add_subparsers()
276+
277+
subp = subparsers.add_parser('install-boost')
278+
subp.add_argument('--source-dir', type=Path, required=True)
279+
subp.set_defaults(func=_install_boost)
280+
281+
subp = subparsers.add_parser('build-b2-distro')
282+
subp.add_argument('--install-prefix', type=Path, required=True)
283+
subp.set_defaults(func=_build_b2_distro)
284+
285+
subp = subparsers.add_parser('run-cmake-superproject-tests')
286+
subp.add_argument('--install-prefix', type=Path, required=True)
287+
subp.add_argument('--generator', default='Unix Makefiles')
288+
subp.add_argument('--build-type', default='Debug')
289+
subp.add_argument('--cxxstd', default='20')
290+
subp.add_argument('--build-shared-libs', type=_str2bool, default=False)
291+
subp.set_defaults(func=_run_cmake_superproject_tests)
292+
293+
subp = subparsers.add_parser('install-cmake-distro')
294+
subp.add_argument('--build-type', default='Debug')
295+
subp.set_defaults(func=_install_cmake_distro)
296+
297+
subp = subparsers.add_parser('run-cmake-standalone-tests')
298+
subp.add_argument('--b2-distro', type=Path, required=True)
299+
subp.add_argument('--generator', default='Unix Makefiles')
300+
subp.add_argument('--build-type', default='Debug')
301+
subp.add_argument('--cxxstd', default='20')
302+
subp.add_argument('--build-shared-libs', type=_str2bool, default=False)
303+
subp.set_defaults(func=_run_cmake_standalone_tests)
304+
305+
subp = subparsers.add_parser('run-cmake-add-subdirectory-tests')
306+
subp.add_argument('--generator', default='Unix Makefiles')
307+
subp.add_argument('--build-type', default='Debug')
308+
subp.add_argument('--build-shared-libs', type=_str2bool, default=False)
309+
subp.set_defaults(func=_run_cmake_add_subdirectory_tests)
310+
311+
subp = subparsers.add_parser('run-cmake-find-package-tests')
312+
subp.add_argument('--cmake-distro', type=Path, required=True)
313+
subp.add_argument('--generator', default='Unix Makefiles')
314+
subp.add_argument('--build-type', default='Debug')
315+
subp.add_argument('--build-shared-libs', type=_str2bool, default=False)
316+
subp.set_defaults(func=_run_cmake_find_package_tests)
317+
318+
subp = subparsers.add_parser('run-cmake-b2-find-package-tests')
319+
subp.add_argument('--cmake-distro', type=Path, required=True)
320+
subp.add_argument('--generator', default='Unix Makefiles')
321+
subp.add_argument('--build-type', default='Debug')
322+
subp.add_argument('--build-shared-libs', type=_str2bool, default=False)
323+
subp.set_defaults(func=_run_cmake_b2_find_package_tests)
324+
325+
subp = subparsers.add_parser('run-b2-tests')
326+
subp.add_argument('--toolset', required=True)
327+
subp.add_argument('--cxxstd', default='20')
328+
subp.add_argument('--variant', default='debug,release')
329+
subp.add_argument('--stdlib', default='native')
330+
subp.add_argument('--address-model', default='64')
331+
subp.add_argument('--address-sanitizer', type=_str2bool, default=False)
332+
subp.add_argument('--undefined-sanitizer', type=_str2bool, default=False)
333+
subp.set_defaults(func=_run_b2_tests)
334+
335+
args = parser.parse_args()
336+
args.func(**{k: v for k, v in vars(args).items() if k != 'func'})
337+
338+
339+
if __name__ == '__main__':
340+
main()

0 commit comments

Comments
 (0)