unittests: Test env2mfile's dpkg_architecture_to_machine_info

This test parses several possible outputs of dpkg-architecture and
asserts that they produce the expected MachineInfo.

To avoid depending on suitable cross-tools being installed, use
unittest.mock to override locate_path with a version that pretends that
all of the tools we're interested in are in /usr/bin.
Similarly, use mock environment variables to exercise what happens
when we have those set.

The test data used here exercises most variations:

* big- and little-endianness
* GNU CPU (x86_64) differing from dpkg CPU (amd64)
* Linux, kFreeBSD and Hurd
* special-cased architectures: x86, arm, mips64el, ppc64el

expected_compilers() intentionally doesn't assume that every compiler
is gcc (even though they all are, right now), because #13721 proposes
adding valac which does not take a gcc suffix.

Signed-off-by: Simon McVittie <smcv@debian.org>
pull/13866/head
Simon McVittie 2 months ago committed by Jussi Pakkanen
parent 6224a7f48e
commit 1c11a04f94
  1. 391
      unittests/internaltests.py

@ -4,6 +4,7 @@
from configparser import ConfigParser from configparser import ConfigParser
from pathlib import Path from pathlib import Path
from unittest import mock from unittest import mock
import argparse
import contextlib import contextlib
import io import io
import json import json
@ -13,6 +14,7 @@ import pickle
import stat import stat
import subprocess import subprocess
import tempfile import tempfile
import textwrap
import typing as T import typing as T
import unittest import unittest
@ -23,6 +25,7 @@ import mesonbuild.dependencies.factory
import mesonbuild.envconfig import mesonbuild.envconfig
import mesonbuild.environment import mesonbuild.environment
import mesonbuild.modules.gnome import mesonbuild.modules.gnome
import mesonbuild.scripts.env2mfile
from mesonbuild import coredata from mesonbuild import coredata
from mesonbuild.compilers.c import ClangCCompiler, GnuCCompiler from mesonbuild.compilers.c import ClangCCompiler, GnuCCompiler
from mesonbuild.compilers.cpp import VisualStudioCPPCompiler from mesonbuild.compilers.cpp import VisualStudioCPPCompiler
@ -1715,3 +1718,391 @@ class InternalTests(unittest.TestCase):
for raw, expected in cases: for raw, expected in cases:
with self.subTest(raw): with self.subTest(raw):
self.assertEqual(OptionKey.from_string(raw), expected) self.assertEqual(OptionKey.from_string(raw), expected)
def test_env2mfile_deb(self) -> None:
MachineInfo = mesonbuild.scripts.env2mfile.MachineInfo
to_machine_info = mesonbuild.scripts.env2mfile.dpkg_architecture_to_machine_info
# For testing purposes, behave as though all cross-programs
# exist in /usr/bin
def locate_path(program: str) -> T.List[str]:
if os.path.isabs(program):
return [program]
return ['/usr/bin/' + program]
def expected_compilers(
gnu_tuple: str,
gcc_suffix: str = '',
) -> T.Dict[str, T.List[str]]:
return {
'c': [f'/usr/bin/{gnu_tuple}-gcc{gcc_suffix}'],
'cpp': [f'/usr/bin/{gnu_tuple}-g++{gcc_suffix}'],
'objc': [f'/usr/bin/{gnu_tuple}-gobjc{gcc_suffix}'],
'objcpp': [f'/usr/bin/{gnu_tuple}-gobjc++{gcc_suffix}'],
}
def expected_binaries(gnu_tuple: str) -> T.Dict[str, T.List[str]]:
return {
'ar': [f'/usr/bin/{gnu_tuple}-ar'],
'strip': [f'/usr/bin/{gnu_tuple}-strip'],
'objcopy': [f'/usr/bin/{gnu_tuple}-objcopy'],
'ld': [f'/usr/bin/{gnu_tuple}-ld'],
'cmake': ['/usr/bin/cmake'],
'pkg-config': [f'/usr/bin/{gnu_tuple}-pkg-config'],
'cups-config': ['/usr/bin/cups-config'],
}
for title, dpkg_arch, gccsuffix, env, expected in [
(
# s390x is an example of the common case where the
# Meson CPU name, the GNU CPU name, the dpkg architecture
# name and uname -m all agree.
# (alpha, m68k, ppc64, riscv64, sh4, sparc64 are similar)
's390x-linux-gnu',
# Output of `dpkg-architecture -a...`, filtered to
# only the DEB_HOST_ parts because that's all we use
textwrap.dedent(
'''
DEB_HOST_ARCH=s390x
DEB_HOST_ARCH_ABI=base
DEB_HOST_ARCH_BITS=64
DEB_HOST_ARCH_CPU=s390x
DEB_HOST_ARCH_ENDIAN=big
DEB_HOST_ARCH_LIBC=gnu
DEB_HOST_ARCH_OS=linux
DEB_HOST_GNU_CPU=s390x
DEB_HOST_GNU_SYSTEM=linux-gnu
DEB_HOST_GNU_TYPE=s390x-linux-gnu
DEB_HOST_MULTIARCH=s390x-linux-gnu
'''
),
'',
{'PATH': '/usr/bin'},
MachineInfo(
compilers=expected_compilers('s390x-linux-gnu'),
binaries=expected_binaries('s390x-linux-gnu'),
properties={},
compile_args={},
link_args={},
cmake={
'CMAKE_C_COMPILER': ['/usr/bin/s390x-linux-gnu-gcc'],
'CMAKE_CXX_COMPILER': ['/usr/bin/s390x-linux-gnu-g++'],
'CMAKE_SYSTEM_NAME': 'Linux',
'CMAKE_SYSTEM_PROCESSOR': 's390x',
},
system='linux',
subsystem='linux',
kernel='linux',
cpu='s390x',
cpu_family='s390x',
endian='big',
),
),
# Debian amd64 vs. GNU, Meson, etc. x86_64.
# arm64/aarch64, hppa/parisc, i386/i686/x86, loong64/loongarch64,
# powerpc/ppc are similar.
(
'x86_64-linux-gnu',
textwrap.dedent(
'''
DEB_HOST_ARCH=amd64
DEB_HOST_ARCH_ABI=base
DEB_HOST_ARCH_BITS=64
DEB_HOST_ARCH_CPU=amd64
DEB_HOST_ARCH_ENDIAN=little
DEB_HOST_ARCH_LIBC=gnu
DEB_HOST_ARCH_OS=linux
DEB_HOST_GNU_CPU=x86_64
DEB_HOST_GNU_SYSTEM=linux-gnu
DEB_HOST_GNU_TYPE=x86_64-linux-gnu
DEB_HOST_MULTIARCH=x86_64-linux-gnu
'''
),
'',
{'PATH': '/usr/bin'},
MachineInfo(
compilers=expected_compilers('x86_64-linux-gnu'),
binaries=expected_binaries('x86_64-linux-gnu'),
properties={},
compile_args={},
link_args={},
cmake={
'CMAKE_C_COMPILER': ['/usr/bin/x86_64-linux-gnu-gcc'],
'CMAKE_CXX_COMPILER': ['/usr/bin/x86_64-linux-gnu-g++'],
'CMAKE_SYSTEM_NAME': 'Linux',
'CMAKE_SYSTEM_PROCESSOR': 'x86_64',
},
system='linux',
subsystem='linux',
kernel='linux',
# TODO: In native builds we get x86_64, but in
# cross-builds it's amd64
cpu='TODO',
cpu_family='x86_64',
endian='little',
),
),
(
'arm-linux-gnueabihf with non-default gcc and environment',
textwrap.dedent(
'''
DEB_HOST_ARCH=armhf
DEB_HOST_ARCH_ABI=eabihf
DEB_HOST_ARCH_BITS=32
DEB_HOST_ARCH_CPU=arm
DEB_HOST_ARCH_ENDIAN=little
DEB_HOST_ARCH_LIBC=gnu
DEB_HOST_ARCH_OS=linux
DEB_HOST_GNU_CPU=arm
DEB_HOST_GNU_SYSTEM=linux-gnueabihf
DEB_HOST_GNU_TYPE=arm-linux-gnueabihf
DEB_HOST_MULTIARCH=arm-linux-gnueabihf
'''
),
'-12',
{
'PATH': '/usr/bin',
'CPPFLAGS': '-DNDEBUG',
'CFLAGS': '-std=c99',
'CXXFLAGS': '-std=c++11',
'OBJCFLAGS': '-fobjc-exceptions',
'OBJCXXFLAGS': '-fobjc-nilcheck',
'LDFLAGS': '-Wl,-O1',
},
MachineInfo(
compilers=expected_compilers('arm-linux-gnueabihf', '-12'),
binaries=expected_binaries('arm-linux-gnueabihf'),
properties={},
compile_args={
'c': ['-DNDEBUG', '-std=c99'],
'cpp': ['-DNDEBUG', '-std=c++11'],
'objc': ['-DNDEBUG', '-fobjc-exceptions'],
'objcpp': ['-DNDEBUG', '-fobjc-nilcheck'],
},
link_args={
'c': ['-std=c99', '-Wl,-O1'],
'cpp': ['-std=c++11', '-Wl,-O1'],
'objc': ['-fobjc-exceptions', '-Wl,-O1'],
'objcpp': ['-fobjc-nilcheck', '-Wl,-O1'],
},
cmake={
'CMAKE_C_COMPILER': ['/usr/bin/arm-linux-gnueabihf-gcc-12'],
'CMAKE_CXX_COMPILER': ['/usr/bin/arm-linux-gnueabihf-g++-12'],
'CMAKE_SYSTEM_NAME': 'Linux',
'CMAKE_SYSTEM_PROCESSOR': 'armv7l',
},
system='linux',
subsystem='linux',
kernel='linux',
# In a native build this would often be armv8l
# (the version of the running CPU) but the architecture
# baseline in Debian is officially ARMv7
cpu='arm7hlf',
cpu_family='arm',
endian='little',
),
),
(
'special cases for i386 (i686, x86) and Hurd',
textwrap.dedent(
'''
DEB_HOST_ARCH=hurd-i386
DEB_HOST_ARCH_ABI=base
DEB_HOST_ARCH_BITS=32
DEB_HOST_ARCH_CPU=i386
DEB_HOST_ARCH_ENDIAN=little
DEB_HOST_ARCH_LIBC=gnu
DEB_HOST_ARCH_OS=hurd
DEB_HOST_GNU_CPU=i686
DEB_HOST_GNU_SYSTEM=gnu
DEB_HOST_GNU_TYPE=i686-gnu
DEB_HOST_MULTIARCH=i386-gnu
'''
),
'',
{'PATH': '/usr/bin'},
MachineInfo(
compilers=expected_compilers('i686-gnu'),
binaries=expected_binaries('i686-gnu'),
properties={},
compile_args={},
link_args={},
cmake={
'CMAKE_C_COMPILER': ['/usr/bin/i686-gnu-gcc'],
'CMAKE_CXX_COMPILER': ['/usr/bin/i686-gnu-g++'],
'CMAKE_SYSTEM_NAME': 'GNU',
'CMAKE_SYSTEM_PROCESSOR': 'i686',
},
# TODO: Currently hurd, but should be gnu as per
# https://mesonbuild.com/Reference-tables.html
system='TODO',
subsystem='TODO',
# TODO: Currently linux, but should be gnu/hurd/mach?
# https://github.com/mesonbuild/meson/issues/13740
kernel='TODO',
# TODO: Currently hurd-i386, but should be i686
cpu='TODO',
cpu_family='x86',
endian='little',
),
),
(
'special cases for amd64 (x86_64) and kFreeBSD',
textwrap.dedent(
'''
DEB_HOST_ARCH=kfreebsd-amd64
DEB_HOST_ARCH_ABI=base
DEB_HOST_ARCH_BITS=64
DEB_HOST_ARCH_CPU=x86_amd64
DEB_HOST_ARCH_ENDIAN=little
DEB_HOST_ARCH_LIBC=gnu
DEB_HOST_ARCH_OS=kfreebsd
DEB_HOST_GNU_CPU=x86_64
DEB_HOST_GNU_SYSTEM=kfreebsd-gnu
DEB_HOST_GNU_TYPE=x86_64-kfreebsd-gnu
DEB_HOST_MULTIARCH=x86_64-kfreebsd-gnu
'''
),
'',
{'PATH': '/usr/bin'},
MachineInfo(
compilers=expected_compilers('x86_64-kfreebsd-gnu'),
binaries=expected_binaries('x86_64-kfreebsd-gnu'),
properties={},
compile_args={},
link_args={},
cmake={
'CMAKE_C_COMPILER': ['/usr/bin/x86_64-kfreebsd-gnu-gcc'],
'CMAKE_CXX_COMPILER': ['/usr/bin/x86_64-kfreebsd-gnu-g++'],
'CMAKE_SYSTEM_NAME': 'kFreeBSD',
'CMAKE_SYSTEM_PROCESSOR': 'x86_64',
},
system='kfreebsd',
subsystem='kfreebsd',
# TODO: Currently linux but should be freebsd
kernel='TODO',
# TODO: Currently kfreebsd-amd64 but should be x86_64
cpu='TODO',
cpu_family='x86_64',
endian='little',
),
),
(
'special case for mips64el',
textwrap.dedent(
'''
DEB_HOST_ARCH=mips64el
DEB_HOST_ARCH_ABI=abi64
DEB_HOST_ARCH_BITS=64
DEB_HOST_ARCH_CPU=mips64el
DEB_HOST_ARCH_ENDIAN=little
DEB_HOST_ARCH_LIBC=gnu
DEB_HOST_ARCH_OS=linux
DEB_HOST_GNU_CPU=mips64el
DEB_HOST_GNU_SYSTEM=linux-gnuabi64
DEB_HOST_GNU_TYPE=mips64el-linux-gnuabi64
DEB_HOST_MULTIARCH=mips64el-linux-gnuabi64
'''
),
'',
{'PATH': '/usr/bin'},
MachineInfo(
compilers=expected_compilers('mips64el-linux-gnuabi64'),
binaries=expected_binaries('mips64el-linux-gnuabi64'),
properties={},
compile_args={},
link_args={},
cmake={
'CMAKE_C_COMPILER': ['/usr/bin/mips64el-linux-gnuabi64-gcc'],
'CMAKE_CXX_COMPILER': ['/usr/bin/mips64el-linux-gnuabi64-g++'],
'CMAKE_SYSTEM_NAME': 'Linux',
'CMAKE_SYSTEM_PROCESSOR': 'mips64',
},
system='linux',
subsystem='linux',
kernel='linux',
cpu='mips64',
cpu_family='mips64',
endian='little',
),
),
(
'special case for ppc64el',
textwrap.dedent(
'''
DEB_HOST_ARCH=ppc64el
DEB_HOST_ARCH_ABI=base
DEB_HOST_ARCH_BITS=64
DEB_HOST_ARCH_CPU=ppc64el
DEB_HOST_ARCH_ENDIAN=little
DEB_HOST_ARCH_LIBC=gnu
DEB_HOST_ARCH_OS=linux
DEB_HOST_GNU_CPU=powerpc64le
DEB_HOST_GNU_SYSTEM=linux-gnu
DEB_HOST_GNU_TYPE=powerpc64le-linux-gnu
DEB_HOST_MULTIARCH=powerpc64le-linux-gnu
'''
),
'',
{'PATH': '/usr/bin'},
MachineInfo(
compilers=expected_compilers('powerpc64le-linux-gnu'),
binaries=expected_binaries('powerpc64le-linux-gnu'),
properties={},
compile_args={},
link_args={},
cmake={
'CMAKE_C_COMPILER': ['/usr/bin/powerpc64le-linux-gnu-gcc'],
'CMAKE_CXX_COMPILER': ['/usr/bin/powerpc64le-linux-gnu-g++'],
'CMAKE_SYSTEM_NAME': 'Linux',
'CMAKE_SYSTEM_PROCESSOR': 'ppc64le',
},
system='linux',
subsystem='linux',
kernel='linux',
# TODO: Currently ppc64el, but native builds have ppc64le,
# and maybe it should be ppc64 in both cases?
# https://github.com/mesonbuild/meson/issues/13741
cpu='TODO',
cpu_family='ppc64',
endian='little',
),
),
]:
with self.subTest(title), \
unittest.mock.patch.dict('os.environ', env, clear=True), \
unittest.mock.patch('mesonbuild.scripts.env2mfile.locate_path') as mock_locate_path:
mock_locate_path.side_effect = locate_path
options = argparse.Namespace()
options.gccsuffix = gccsuffix
actual = to_machine_info(dpkg_arch, options)
if expected.system == 'TODO':
print(f'TODO: {title}: system() -> {actual.system}')
else:
self.assertEqual(actual.system, expected.system)
if expected.subsystem == 'TODO':
print(f'TODO: {title}: subsystem() -> {actual.subsystem}')
else:
self.assertEqual(actual.subsystem, expected.subsystem)
if expected.kernel == 'TODO':
print(f'TODO: {title}: kernel() -> {actual.kernel}')
else:
self.assertEqual(actual.kernel, expected.kernel)
if expected.cpu == 'TODO':
print(f'TODO: {title}: cpu() -> {actual.cpu}')
else:
self.assertEqual(actual.cpu, expected.cpu)
self.assertEqual(actual.cpu_family, expected.cpu_family)
self.assertEqual(actual.endian, expected.endian)
self.assertEqual(actual.compilers, expected.compilers)
self.assertEqual(actual.binaries, expected.binaries)
self.assertEqual(actual.properties, expected.properties)
self.assertEqual(actual.compile_args, expected.compile_args)
self.assertEqual(actual.link_args, expected.link_args)
self.assertEqual(actual.cmake, expected.cmake)

Loading…
Cancel
Save