import dataclasses
import pathlib
import textwrap
import unittest
import modulemap_config
from generate_system_modulemap import (
AllowList,
Header,
calculate_transitive_headers,
combine_modulemaps,
parse_depfile,
parse_modulemap,
split_modulemap,
modify_modulemap,
)
_TESTDATA = (pathlib.Path(__file__).parent / 'testdata').resolve()
_LIBCXX = _TESTDATA / 'libc++'
_SYSROOT = _TESTDATA / 'sysroot'
_CHROMIUM = pathlib.Path(__file__).parents[3]
_CLANG = _CHROMIUM / 'third_party/llvm-build/Release+Asserts/bin/clang++'
_WANT_MODULEMAP = """module "//system" [system] {
module root {
requires gnuinlineasm, !systemz
header "../../libc++/root.h"
module submodule {
requires arm64
requires x64, gnuinlineasm
header "../../libc++/subdir/requires_unmet.h"
}
textual header "../../libc++/textual.h"
private header "../../libc++/private.h"
}
module "never_referenced.h" {
textual header "../../sysroot/usr/include/never_referenced.h"
export *
}
module "public_root.h" {
header "../../sysroot/usr/include/public_root.h"
export *
}
module "sysroot.h" {
header "../../sysroot/usr/include/sysroot.h"
export *
}
module "public_subdir.h" {
textual header \
"../../sysroot/usr/include/x86_64-linux-gnu/bits/public_subdir.h"
export *
}
}
"""
_MODULES = """
module foo {
module submodule {
header "bar.h"
}
}
explicit framework module "bar" [system] {
header "bar.h"
}
"""
_WANT_SPLIT_MODULEMAP = {
"foo": """module foo {
module submodule {
header "bar.h"
}
}""",
"bar": """explicit framework module "bar" [system] {
header "bar.h"
}"""
}
class GenerateSysrootModulemapTest(unittest.TestCase):
maxDiff = None
def test_allowlist(self):
hdrs = [
modulemap_config.Header('normal.h'),
modulemap_config.Header('only_on_win.h',
module_name='only_on_win',
exports=['*', 'other']),
modulemap_config.Header('textual.h', textual=True),
modulemap_config.Header('win_lazy.h', lazy=True),
modulemap_config.Header('win_textual.h', textual=True),
]
al = AllowList(hdrs)
self.assertEqual(
al.includes(),
['normal.h', 'only_on_win.h', 'textual.h', 'win_textual.h'])
self.assertIsNone(al.textual('normal.h'))
self.assertTrue(al.textual('textual.h'))
self.assertTrue(al.textual('win_textual.h'))
self.assertTrue(al.textual('bits/some.h'))
self.assertFalse(al.private('normal.h'))
self.assertTrue(al.private('unknown.h'))
self.assertEqual(al.module_name('only_on_win.h'), 'only_on_win')
self.assertIsNone(al.module_name('normal.h'))
self.assertEqual(al.exports('only_on_win.h'), ['*', 'other'])
self.assertEqual(al.exports('normal.h'), ['*'])
def test_generate_system_modulemap(self):
common_dir, headers = parse_modulemap(_TESTDATA / 'gen/module.modulemap',
{}, set())
self.assertEqual(common_dir, _LIBCXX)
self.assertEqual(headers, [
Header(path=pathlib.Path('root.h'),
private=False,
textual=False,
requires=['gnuinlineasm', '!systemz']),
Header(path=pathlib.Path('subdir/requires_unmet.h'),
private=False,
textual=False,
requires=[
'gnuinlineasm', '!systemz', 'arm64', 'x64', 'gnuinlineasm'
]),
Header(path=pathlib.Path('textual.h'),
private=False,
textual=True,
requires=['gnuinlineasm', '!systemz']),
Header(path=pathlib.Path('private.h'),
private=True,
textual=False,
requires=['gnuinlineasm', '!systemz']),
])
deps = calculate_transitive_headers(
clang_args=[
str(_CLANG), '-isystem',
str(_LIBCXX), f'--sysroot={_SYSROOT}', '--target=x86_64-linux-gnu'
],
include_dirs=[(common_dir, headers)],
sysroot_dirs=[
_SYSROOT / 'usr/include', _SYSROOT / 'usr/include/x86_64-linux-gnu'
],
allowlist=AllowList([
modulemap_config.Header('bits/public_subdir.h'),
modulemap_config.Header('public_root.h'),
modulemap_config.AllowedHeader('sysroot.h'),
modulemap_config.AllowedHeader('never_referenced.h'),
]),
target_os='linux',
target_cpu='x64',
)
self.assertEqual(sorted(deps), [
Header(
path=_LIBCXX / 'subdir/bits/missing.h', private=True, textual=True),
Header(path=_LIBCXX / 'subdir/missing.h', private=True, textual=True),
Header(path=_SYSROOT / 'usr/include/never_referenced.h',
private=False,
textual=True),
Header(path=_SYSROOT / 'usr/include/public_root.h',
private=False,
textual=False),
Header(path=_SYSROOT / 'usr/include/sysroot.h',
private=False,
textual=False),
Header(
path=_SYSROOT / 'usr/include/x86_64-linux-gnu/bits/public_subdir.h',
private=False,
textual=True),
Header(path=_SYSROOT / 'usr/include/x86_64-linux-gnu/bits/sysroot.h',
private=True,
textual=True),
])
combined = combine_modulemaps(
out=_TESTDATA / 'want/subdir' / 'module.modulemap',
modulemaps=[_TESTDATA / 'gen/module.modulemap'],
headers=deps,
module_name='//system',
extra_modules=[],
modify_modules={},
modified_modules=set())
self.assertEqual(combined, _WANT_MODULEMAP)
def test_find_modules(self):
modules = split_modulemap(_MODULES)
self.assertCountEqual(modules.keys(), _WANT_SPLIT_MODULEMAP.keys())
for k, v in _WANT_SPLIT_MODULEMAP.items():
self.assertEqual(modules[k], v)
def test_parse_depfile(self):
self.assertEqual(
parse_depfile(r"""dummy.o: \
/path/to/header1.h \
/path/to/header2.h \
/path/to/header\ with\ spaces.h \
/path/to/header3.h
"""), [
pathlib.Path('/path/to/header1.h'),
pathlib.Path('/path/to/header2.h'),
pathlib.Path('/path/to/header with spaces.h'),
pathlib.Path('/path/to/header3.h'),
])
def test_modify_modulemap(self):
modulemap = textwrap.dedent("""\
module foo {
module submodule {
header "bar.h"
}
}""")
modified_modules = set()
content = modify_modulemap(modulemap, {
'foo': lambda s: '',
'unused': lambda s: '',
}, modified_modules)
self.assertEqual(content, '')
self.assertEqual(modified_modules, {'foo'})
modified_modules = set()
content = modify_modulemap(
modulemap, {'foo.submodule': lambda s: s[:-1] + ' export *\n }'},
modified_modules)
self.assertEqual(
content,
textwrap.dedent("""\
module foo {
module submodule {
header "bar.h"
export *
}
}"""))
self.assertIn('foo.submodule', modified_modules)
modified_modules = set()
content = modify_modulemap(
modulemap, {
'foo.submodule': lambda s: s[:-1] + ' export submodule\n }',
'foo': lambda s: s[:-1] + ' export foo\n}',
'other.submodule': lambda s: '',
}, modified_modules)
self.assertEqual(
content,
textwrap.dedent("""\
module foo {
module submodule {
header "bar.h"
export submodule
}
export foo
}"""))
self.assertIn('foo.submodule', modified_modules)
self.assertIn('foo', modified_modules)
self.assertNotIn('other.submodule', modified_modules)
if __name__ == '__main__':
unittest.main()