Skip to content

Commit b048695

Browse files
authored
Fix tests to run on Cairo 1.18.0 (#3416)
* Add a script to build and install cairo * Update gui tests for cairo 1.18.0 * update script to set env vars * Make the script run with plain python * Prefer the recently built one in pkg-config * Skip the built if it's windows * CI: build and install latest cairo * CI: only run when cache is missed * Disable compiling tests while building cairo * update poetry lock file * Display the cairo version when running pytest * fixup * tests: skip graphical test when cairo is old * fix the path to find the pkgconfig files on linux * set the LD_LIBRARY_PATH too only then it'll work on linux * fixup * small fixup * Move the script inside `.github/scripts` folder * Make the minimum cairo version a constant * Seperate setting env vars to a sperate step this seem to have broken when cache is hit
1 parent 8320cdd commit b048695

File tree

227 files changed

+272
-27
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

227 files changed

+272
-27
lines changed

.github/scripts/ci_build_cairo.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Logic is as follows:
2+
# 1. Download cairo source code: https://cairographics.org/releases/cairo-<version>.tar.xz
3+
# 2. Verify the downloaded file using the sha256sums file: https://cairographics.org/releases/cairo-<version>.tar.xz.sha256sum
4+
# 3. Extract the downloaded file.
5+
# 4. Create a virtual environment and install meson and ninja.
6+
# 5. Run meson build in the extracted directory. Also, set required prefix.
7+
# 6. Run meson compile -C build.
8+
# 7. Run meson install -C build.
9+
10+
import hashlib
11+
import logging
12+
import os
13+
import subprocess
14+
import sys
15+
import tarfile
16+
import tempfile
17+
import typing
18+
import urllib.request
19+
from contextlib import contextmanager
20+
from pathlib import Path
21+
from sys import stdout
22+
23+
CAIRO_VERSION = "1.18.0"
24+
CAIRO_URL = f"https://cairographics.org/releases/cairo-{CAIRO_VERSION}.tar.xz"
25+
CAIRO_SHA256_URL = f"{CAIRO_URL}.sha256sum"
26+
27+
VENV_NAME = "meson-venv"
28+
BUILD_DIR = "build"
29+
INSTALL_PREFIX = Path(__file__).parent.parent / "third_party" / "cairo"
30+
31+
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
32+
logger = logging.getLogger(__name__)
33+
34+
35+
def is_ci():
36+
return os.getenv("CI", None) is not None
37+
38+
39+
def download_file(url, path):
40+
logger.info(f"Downloading {url} to {path}")
41+
block_size = 1024 * 1024
42+
with urllib.request.urlopen(url) as response, open(path, "wb") as file:
43+
while True:
44+
data = response.read(block_size)
45+
if not data:
46+
break
47+
file.write(data)
48+
49+
50+
def verify_sha256sum(path, sha256sum):
51+
with open(path, "rb") as file:
52+
file_hash = hashlib.sha256(file.read()).hexdigest()
53+
if file_hash != sha256sum:
54+
raise Exception("SHA256SUM does not match")
55+
56+
57+
def extract_tar_xz(path, directory):
58+
with tarfile.open(path) as file:
59+
file.extractall(directory)
60+
61+
62+
def run_command(command, cwd=None, env=None):
63+
process = subprocess.Popen(command, cwd=cwd, env=env)
64+
process.communicate()
65+
if process.returncode != 0:
66+
raise Exception("Command failed")
67+
68+
69+
@contextmanager
70+
def gha_group(title: str) -> typing.Generator:
71+
if not is_ci():
72+
yield
73+
return
74+
print(f"\n::group::{title}")
75+
stdout.flush()
76+
try:
77+
yield
78+
finally:
79+
print("::endgroup::")
80+
stdout.flush()
81+
82+
83+
def set_env_var_gha(name: str, value: str) -> None:
84+
if not is_ci():
85+
return
86+
env_file = os.getenv("GITHUB_ENV", None)
87+
if env_file is None:
88+
return
89+
with open(env_file, "a") as file:
90+
file.write(f"{name}={value}\n")
91+
stdout.flush()
92+
93+
94+
def get_ld_library_path(prefix: Path) -> str:
95+
# given a prefix, the ld library path can be found at
96+
# <prefix>/lib/* or sometimes just <prefix>/lib
97+
# this function returns the path to the ld library path
98+
99+
# first, check if the ld library path exists at <prefix>/lib/*
100+
ld_library_paths = list(prefix.glob("lib/*"))
101+
if len(ld_library_paths) == 1:
102+
return ld_library_paths[0].absolute().as_posix()
103+
104+
# if the ld library path does not exist at <prefix>/lib/*,
105+
# return <prefix>/lib
106+
ld_library_path = prefix / "lib"
107+
if ld_library_path.exists():
108+
return ld_library_path.absolute().as_posix()
109+
return ""
110+
111+
112+
def main():
113+
if sys.platform == "win32":
114+
logger.info("Skipping build on windows")
115+
return
116+
117+
with tempfile.TemporaryDirectory() as tmpdir:
118+
with gha_group("Downloading and Extracting Cairo"):
119+
logger.info(f"Downloading cairo version {CAIRO_VERSION}")
120+
download_file(CAIRO_URL, os.path.join(tmpdir, "cairo.tar.xz"))
121+
122+
logger.info("Downloading cairo sha256sum")
123+
download_file(CAIRO_SHA256_URL, os.path.join(tmpdir, "cairo.sha256sum"))
124+
125+
logger.info("Verifying cairo sha256sum")
126+
with open(os.path.join(tmpdir, "cairo.sha256sum")) as file:
127+
sha256sum = file.read().split()[0]
128+
verify_sha256sum(os.path.join(tmpdir, "cairo.tar.xz"), sha256sum)
129+
130+
logger.info("Extracting cairo")
131+
extract_tar_xz(os.path.join(tmpdir, "cairo.tar.xz"), tmpdir)
132+
133+
with gha_group("Installing meson and ninja"):
134+
logger.info("Creating virtual environment")
135+
run_command([sys.executable, "-m", "venv", os.path.join(tmpdir, VENV_NAME)])
136+
137+
logger.info("Installing meson and ninja")
138+
run_command(
139+
[
140+
os.path.join(tmpdir, VENV_NAME, "bin", "pip"),
141+
"install",
142+
"meson",
143+
"ninja",
144+
]
145+
)
146+
147+
env_vars = {
148+
# add the venv bin directory to PATH so that meson can find ninja
149+
"PATH": f"{os.path.join(tmpdir, VENV_NAME, 'bin')}{os.pathsep}{os.environ['PATH']}",
150+
}
151+
152+
with gha_group("Building and Installing Cairo"):
153+
logger.info("Running meson setup")
154+
run_command(
155+
[
156+
os.path.join(tmpdir, VENV_NAME, "bin", "meson"),
157+
"setup",
158+
BUILD_DIR,
159+
f"--prefix={INSTALL_PREFIX.absolute().as_posix()}",
160+
"--buildtype=release",
161+
"-Dtests=disabled",
162+
],
163+
cwd=os.path.join(tmpdir, f"cairo-{CAIRO_VERSION}"),
164+
env=env_vars,
165+
)
166+
167+
logger.info("Running meson compile")
168+
run_command(
169+
[
170+
os.path.join(tmpdir, VENV_NAME, "bin", "meson"),
171+
"compile",
172+
"-C",
173+
BUILD_DIR,
174+
],
175+
cwd=os.path.join(tmpdir, f"cairo-{CAIRO_VERSION}"),
176+
env=env_vars,
177+
)
178+
179+
logger.info("Running meson install")
180+
run_command(
181+
[
182+
os.path.join(tmpdir, VENV_NAME, "bin", "meson"),
183+
"install",
184+
"-C",
185+
BUILD_DIR,
186+
],
187+
cwd=os.path.join(tmpdir, f"cairo-{CAIRO_VERSION}"),
188+
env=env_vars,
189+
)
190+
191+
logger.info(f"Successfully built cairo and installed it to {INSTALL_PREFIX}")
192+
193+
194+
if __name__ == "__main__":
195+
if "--set-env-vars" in sys.argv:
196+
with gha_group("Setting environment variables"):
197+
# append the pkgconfig directory to PKG_CONFIG_PATH
198+
set_env_var_gha(
199+
"PKG_CONFIG_PATH",
200+
f"{Path(get_ld_library_path(INSTALL_PREFIX), 'pkgconfig').as_posix()}{os.pathsep}"
201+
f'{os.getenv("PKG_CONFIG_PATH", "")}',
202+
)
203+
set_env_var_gha(
204+
"LD_LIBRARY_PATH",
205+
f"{get_ld_library_path(INSTALL_PREFIX)}{os.pathsep}"
206+
f'{os.getenv("LD_LIBRARY_PATH", "")}',
207+
)
208+
sys.exit(0)
209+
main()

.github/workflows/ci.yml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,22 @@ jobs:
7676
# start xvfb in background
7777
sudo /usr/bin/Xvfb $DISPLAY -screen 0 1280x1024x24 &
7878
79+
- name: Setup Cairo Cache
80+
uses: actions/cache@v3
81+
id: cache-cairo
82+
if: runner.os == 'Linux' || runner.os == 'macOS'
83+
with:
84+
path: ${{ github.workspace }}/third_party
85+
key: ${{ runner.os }}-dependencies-cairo-${{ hashFiles('.github/scripts/ci_build_cairo.py') }}
86+
87+
- name: Build and install Cairo (Linux and macOS)
88+
if: (runner.os == 'Linux' || runner.os == 'macOS') && steps.cache-cairo.outputs.cache-hit != 'true'
89+
run: python .github/scripts/ci_build_cairo.py
90+
91+
- name: Set env vars for Cairo (Linux and macOS)
92+
if: runner.os == 'Linux' || runner.os == 'macOS'
93+
run: python .github/scripts/ci_build_cairo.py --set-env-vars
94+
7995
- name: Setup macOS cache
8096
uses: actions/cache@v3
8197
id: cache-macos
@@ -103,10 +119,6 @@ jobs:
103119
export PATH="$oriPath"
104120
echo "Completed TinyTeX"
105121
106-
- name: Install cairo (MacOS)
107-
if: runner.os == 'macOS'
108-
run: brew install cairo
109-
110122
- name: Add macOS dependencies to PATH
111123
if: runner.os == 'macOS'
112124
shell: bash

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,6 @@ dist/
131131

132132
/media_dir.txt
133133
# ^TODO: Remove the need for this with a proper config file
134+
135+
# Ignore the built dependencies
136+
third_party/*

conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
except ModuleNotFoundError: # windows
1212
pass
1313

14+
import cairo
1415
import moderngl
1516

1617
# If it is running Doctest the current directory
@@ -39,6 +40,7 @@ def pytest_report_header(config):
3940
info = ctx.info
4041
ctx.release()
4142
return (
43+
f"\nCairo Version: {cairo.cairo_version()}",
4244
"\nOpenGL information",
4345
"------------------",
4446
f"vendor: {info['GL_VENDOR'].strip()}",

manim/utils/testing/frames_comparison.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from pathlib import Path
66
from typing import Callable
77

8+
import cairo
9+
import pytest
810
from _pytest.fixtures import FixtureRequest
911

1012
from manim import Scene
@@ -25,6 +27,7 @@
2527
SCENE_PARAMETER_NAME = "scene"
2628
_tests_root_dir_path = Path(__file__).absolute().parents[2]
2729
PATH_CONTROL_DATA = _tests_root_dir_path / Path("control_data", "graphical_units_data")
30+
MIN_CAIRO_VERSION = 11800
2831

2932

3033
def frames_comparison(
@@ -81,6 +84,12 @@ def decorator_maker(tested_scene_construct):
8184
@functools.wraps(tested_scene_construct)
8285
# The "request" parameter is meant to be used as a fixture by pytest. See below.
8386
def wrapper(*args, request: FixtureRequest, tmp_path, **kwargs):
87+
# check for cairo version
88+
if (
89+
renderer_class is CairoRenderer
90+
and cairo.cairo_version() < MIN_CAIRO_VERSION
91+
):
92+
pytest.skip("Cairo version is too old. Skipping cairo graphical tests.")
8493
# Wraps the test_function to a construct method, to "freeze" the eventual additional arguments (parametrizations fixtures).
8594
construct = functools.partial(tested_scene_construct, *args, **kwargs)
8695

poetry.lock

Lines changed: 33 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)