Linting conan packages using the conan test command
When building conan packages for iOS I want to know if packages have been built as specified. This has sometimes not been the case.
For example, a package for iOS arm64 should contain binaries that are for this architecture and have bitcode enabled. Some packages need special options so that happens, and I want to know about issues as soon as possible. Certainly before consuming some package as a dependency and/or letting users find out about problems.
Using conan test to check a package
Luckily, Conan has a convenient way to test a package. There is the conan test
command.
In short, the most important arguments for this command are:
-
a test folder that contains a
conanfile.py
that consumes a package -
a reference to a package
-
some other options where profiles are most important
Check the conan test help page to get a detailed description of all options. |
Usually tests are part of a recipe and check that a recipe can be consumed. But no one says it cannot be used to also verify other things.
Implementing a iOS package check
For linting iOS binaries I added a test package that checks all libraries in a package if they have the expected arch and contain bitcode.
Some custom validation methods
One way to find out if a binary was built with bitcode support is this:
def check_bitcode(file, arch):
cmd = ["otool", "-arch" , arch, "-l" , file]
output = subprocess.check_output(cmd, text=True)
bitcode_flag="__bitcode"
for line in output.splitlines():
if bitcode_flag in line:
return True
return False
This is an example of how to get the supported architectures of a binary on macOS.
def get_file_archs(file):
cmd = ["lipo", "-archs", file]
result = run(cmd, stdout=PIPE, stderr=PIPE, text=True)
if result.returncode != 0:
raise ValueError(f"lipo -archs returns errno {result.returncode}, {result.stderr}")
return result.stdout.split()
I won’t go into much detail about what those two scripts do since such macOS internals are not part of this post.
The important part is that you do whatever you want to verify files in a package.
The conanfile to test a package
The conanfile.py in the test folder looks like that:
import os, sys
from conans import ConanFile
from pathlib import Path
from ioschecks import get_file_archs, check_bitcode
class TestPackage(ConanFile):
settings = "os", "arch"
def build(self):
pass # suppress warning message
def test(self):
if self.settings.os != "iOS":
return
name = self.display_name.split()[0].split("/")[0]
lib_paths = self.deps_cpp_info[name].lib_paths
libs = self.deps_cpp_info[name].libs
static_libs = list(map(lambda lib: f"lib{lib}.a", libs))
pkg_libs = []
for lib in static_libs:
for libdir in lib_paths:
libfile = f"{libdir}/{lib}"
if Path(libfile).is_file():
pkg_libs.append(libfile)
continue
assert len(pkg_libs) == len(libs), "not all libs found in package"
for lib in pkg_libs:
if self.settings.arch == "armv8":
check_arch = "arm64"
elif self.settings.arch == "x86_64":
check_arch = "x86_64"
assert check_arch in get_file_archs(lib), \
f"Expected arch {check_arch} not found in {lib}"
assert check_bitcode(lib, check_arch), \
f"Bitcode flag not found for {lib}"
This is a very specific test, not a generic universal one. It expects the tested package to have static libraries and checks for the architecture in the profile and that all libraries have been built with bitcode support.
If you want other tests, just implement them as extra checks. I recommend keeping the test files small and run multiple tests if multiple checks are required.
Running the test
Testing a local conan package looks like that:
conan test bitcodecheck/ djinni-support-lib/1.1.0@ --profile:build default --profile:host ios-arm64
This tests the local ios arm64 (armv8) build for the djinni support lib.
The conantest.py
is in the bitcodecheck
folder, and of course, the profiles that were used to build that package are also provided.
Add more verification into your conan dependencies build chain
Such tests, as shown above, can easily be added to CI and can help catch problems in conan packages as early as possible.
The conan test command is awesome!