forked from yoctoproject/poky
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This new class offer the capbility to build rust tests and find them correctly. Due to non deterministic name of generated binaries, a custom parsing of build result must be performed. See rust-lang/cargo#1924 All rust project will generate a test binary even if there are not test defined in source code (the binary will just output that it ran 0 tests) (From OE-Core rev: eb4fbd360b7425c1959c05af289ee51ff1342145) Signed-off-by: Frederic Martinsons <frederic.martinsons@gmail.com> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
- Loading branch information
1 parent
4dd2753
commit 995068e
Showing
1 changed file
with
129 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
inherit cargo ptest | ||
|
||
CARGO_TEST_BINARIES_FILES ?= "${B}/test_binaries_list" | ||
|
||
# sadly generated test binary have no deterministic names (https://github.com/rust-lang/cargo/issues/1924) | ||
# which force us to parse the cargo output in json format to find those test binaries | ||
python do_compile_ptest_cargo() { | ||
import subprocess | ||
import json | ||
|
||
cargo = bb.utils.which(d.getVar("PATH"), d.getVar("CARGO", True)) | ||
cargo_build_flags = d.getVar("CARGO_BUILD_FLAGS", True) | ||
rust_flags = d.getVar("RUSTFLAGS", True) | ||
manifest_path = d.getVar("MANIFEST_PATH", True) | ||
|
||
env = os.environ.copy() | ||
env['RUSTFLAGS'] = rust_flags | ||
cmd = f"{cargo} build --tests --message-format json {cargo_build_flags}" | ||
bb.note(f"Building tests with cargo ({cmd})") | ||
|
||
try: | ||
proc = subprocess.Popen(cmd, shell=True, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | ||
except subprocess.CalledProcessError as e: | ||
bb.fatal(f"Cannot build test with cargo: {e}") | ||
|
||
lines = [] | ||
for line in proc.stdout: | ||
data = line.decode('utf-8').strip('\n') | ||
lines.append(data) | ||
bb.note(data) | ||
proc.communicate() | ||
if proc.returncode != 0: | ||
bb.fatal(f"Unable to compile test with cargo, '{cmd}' failed") | ||
|
||
# Definition of the format: https://doc.rust-lang.org/cargo/reference/external-tools.html#json-messages | ||
test_bins = [] | ||
for line in lines: | ||
try: | ||
data = json.loads(line) | ||
except json.JSONDecodeError: | ||
# skip lines that are not a json | ||
pass | ||
else: | ||
try: | ||
# Filter the test packages coming from the current manifest | ||
current_manifest_path = os.path.normpath(data['manifest_path']) | ||
project_manifest_path = os.path.normpath(manifest_path) | ||
if current_manifest_path == project_manifest_path: | ||
if data['target']['test'] or data['target']['doctest'] and data['executable']: | ||
test_bins.append(data['executable']) | ||
except KeyError as e: | ||
# skip lines that do not meet the requirements | ||
pass | ||
|
||
# All rust project will genrate at least one unit test binary | ||
# It will just run a test suite with 0 tests if the project didn't define some | ||
# So it is not expected to have an empty list here | ||
if not test_bins: | ||
bb.fatal("Unable to find any test binaries") | ||
|
||
cargo_test_binaries_file = d.getVar('CARGO_TEST_BINARIES_FILES', True) | ||
bb.note(f"Found {len(test_bins)} tests, write their path into {cargo_test_binaries_file}") | ||
with open(cargo_test_binaries_file, "w") as f: | ||
for test_bin in test_bins: | ||
f.write(f"{test_bin}\n") | ||
|
||
} | ||
|
||
python do_install_ptest_cargo() { | ||
import shutil | ||
|
||
dest_dir = d.getVar("D", True) | ||
pn = d.getVar("PN", True) | ||
ptest_path = d.getVar("PTEST_PATH", True) | ||
cargo_test_binaries_file = d.getVar('CARGO_TEST_BINARIES_FILES', True) | ||
|
||
ptest_dir = os.path.join(dest_dir, ptest_path.lstrip('/')) | ||
os.makedirs(ptest_dir, exist_ok=True) | ||
|
||
test_bins = [] | ||
with open(cargo_test_binaries_file, "r") as f: | ||
for line in f.readlines(): | ||
test_bins.append(line.strip('\n')) | ||
|
||
test_paths = [] | ||
for test_bin in test_bins: | ||
shutil.copy2(test_bin, ptest_dir) | ||
test_paths.append(os.path.join(ptest_path, os.path.basename(test_bin))) | ||
|
||
ptest_script = os.path.join(ptest_dir, "run-ptest") | ||
if os.path.exists(ptest_script): | ||
with open(ptest_script, "a") as f: | ||
f.write(f"\necho \"\"\n") | ||
f.write(f"echo \"## starting to run rust tests ##\"\n") | ||
for test_path in test_paths: | ||
f.write(f"{test_path}\n") | ||
else: | ||
with open(ptest_script, "a") as f: | ||
f.write("#!/bin/sh\n") | ||
for test_path in test_paths: | ||
f.write(f"{test_path}\n") | ||
os.chmod(ptest_script, 0o755) | ||
|
||
# this is chown -R root:root ${D}${PTEST_PATH} | ||
for root, dirs, files in os.walk(ptest_dir): | ||
for d in dirs: | ||
shutil.chown(os.path.join(root, d), "root", "root") | ||
for f in files: | ||
shutil.chown(os.path.join(root, f), "root", "root") | ||
} | ||
|
||
do_install_ptest_cargo[dirs] = "${B}" | ||
do_install_ptest_cargo[doc] = "Create or update the run-ptest script with rust test binaries generated" | ||
do_compile_ptest_cargo[dirs] = "${B}" | ||
do_compile_ptest_cargo[doc] = "Generate rust test binaries through cargo" | ||
|
||
addtask compile_ptest_cargo after do_compile before do_compile_ptest_base | ||
addtask install_ptest_cargo after do_install_ptest_base before do_package | ||
|
||
python () { | ||
if not bb.data.inherits_class('native', d) and not bb.data.inherits_class('cross', d): | ||
d.setVarFlag('do_install_ptest_cargo', 'fakeroot', '1') | ||
d.setVarFlag('do_install_ptest_cargp', 'umask', '022') | ||
|
||
# Remove all '*ptest_cargo' tasks when ptest is not enabled | ||
if not(d.getVar('PTEST_ENABLED') == "1"): | ||
for i in ['do_compile_ptest_cargo', 'do_install_ptest_cargo']: | ||
bb.build.deltask(i, d) | ||
} |