| 1 | |
| 2 | |
| 3 |
|
| 4 | This script is used to build and install the perf python binding.
|
| 5 | It handles compiler-specific flags, especially for clang, and configures
|
| 6 | the setuptools Extension.
|
| 7 |
|
| 8 |
|
| 9 | import os
|
| 10 | import shlex
|
| 11 | import shutil
|
| 12 | import subprocess
|
| 13 | import sysconfig
|
| 14 |
|
| 15 | from setuptools import setup, Extension
|
| 16 | from setuptools.command.build_ext import build_ext as _build_ext
|
| 17 | from setuptools.command.install_lib import install_lib as _install_lib
|
| 18 |
|
| 19 |
|
| 20 | def clang_has_option(cc: str, cc_args: list[str], src_feature_tests: str, option: str) -> bool:
|
| 21 | |
| 22 |
|
| 23 | Args:
|
| 24 | cc: The compiler executable.
|
| 25 | cc_args: Compiler arguments from CC environment variable.
|
| 26 | src_feature_tests: Path to the feature tests directory.
|
| 27 | option: The compiler option to check (e.g., "-mcet").
|
| 28 |
|
| 29 | Returns:
|
| 30 | True if the option is supported, False otherwise.
|
| 31 |
|
| 32 | error_substrings = (
|
| 33 | b"unknown argument",
|
| 34 | b"is not supported",
|
| 35 | b"unknown warning option"
|
| 36 | )
|
| 37 | cmd = [cc] + cc_args + [
|
| 38 | option,
|
| 39 | "-o", "/dev/null",
|
| 40 | os.path.join(src_feature_tests, "test-hello.c")
|
| 41 | ]
|
| 42 | try:
|
| 43 | res = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, check=False)
|
| 44 | cc_output = res.stderr.splitlines()
|
| 45 | except OSError:
|
| 46 | return False
|
| 47 | return not any(any(error in line for error in error_substrings) for line in cc_output)
|
| 48 |
|
| 49 |
|
| 50 | def filter_clang_options(cc: str, cc_args: list[str], src_feature_tests: str) -> None:
|
| 51 | |
| 52 |
|
| 53 | Args:
|
| 54 | cc: The compiler executable.
|
| 55 | cc_args: Compiler arguments from CC environment variable.
|
| 56 | src_feature_tests: Path to the feature tests directory.
|
| 57 |
|
| 58 | config_vars = sysconfig.get_config_vars()
|
| 59 | for var in ('CFLAGS', 'OPT'):
|
| 60 | if var not in config_vars:
|
| 61 | continue
|
| 62 |
|
| 63 | |
| 64 | flags = shlex.split(config_vars[var])
|
| 65 |
|
| 66 | |
| 67 | flags = [f for f in flags if not f.startswith("-specs=")]
|
| 68 |
|
| 69 | options = (
|
| 70 | "-mcet",
|
| 71 | "-fcf-protection",
|
| 72 | "-fstack-clash-protection",
|
| 73 | "-fstack-protector-strong",
|
| 74 | "-fno-semantic-interposition",
|
| 75 | "-ffat-lto-objects",
|
| 76 | "-ftree-loop-distribute-patterns",
|
| 77 | "-gno-variable-location-views"
|
| 78 | )
|
| 79 | for option in options:
|
| 80 | if not clang_has_option(cc, cc_args, src_feature_tests, option):
|
| 81 | |
| 82 | flags = [f for f in flags if not f.startswith(option)]
|
| 83 |
|
| 84 | |
| 85 | config_vars[var] = shlex.join(flags)
|
| 86 |
|
| 87 |
|
| 88 | class BuildExt(_build_ext):
|
| 89 |
|
| 90 |
|
| 91 | def __init__(self, *args, **kwargs):
|
| 92 | self.build_lib = None
|
| 93 | self.build_temp = None
|
| 94 | super().__init__(*args, **kwargs)
|
| 95 |
|
| 96 | def finalize_options(self) -> None:
|
| 97 | _build_ext.finalize_options(self)
|
| 98 | build_lib = os.getenv('PYTHON_EXTBUILD_LIB')
|
| 99 | build_tmp = os.getenv('PYTHON_EXTBUILD_TMP')
|
| 100 | if build_lib:
|
| 101 | self.build_lib = build_lib
|
| 102 | if build_tmp:
|
| 103 | self.build_temp = build_tmp
|
| 104 |
|
| 105 |
|
| 106 | class InstallLib(_install_lib):
|
| 107 |
|
| 108 |
|
| 109 | def __init__(self, *args, **kwargs):
|
| 110 | self.build_dir = None
|
| 111 | super().__init__(*args, **kwargs)
|
| 112 |
|
| 113 | def finalize_options(self) -> None:
|
| 114 | _install_lib.finalize_options(self)
|
| 115 | build_lib = os.getenv('PYTHON_EXTBUILD_LIB')
|
| 116 | if build_lib:
|
| 117 | self.build_dir = build_lib
|
| 118 |
|
| 119 | def run(self):
|
| 120 | _install_lib.run(self)
|
| 121 | srctree = os.getenv('srctree', '.')
|
| 122 | src_perf = os.path.join(srctree, 'tools/perf')
|
| 123 | shutil.copy2(os.path.join(src_perf, 'python/perf.pyi'), self.install_dir)
|
| 124 |
|
| 125 |
|
| 126 | def main() -> None:
|
| 127 |
|
| 128 | cc_env = os.getenv("CC")
|
| 129 | assert cc_env, "Environment variable CC not set"
|
| 130 |
|
| 131 | |
| 132 | cc_tokens = shlex.split(cc_env)
|
| 133 | cc = cc_tokens[0]
|
| 134 | cc_args = cc_tokens[1:]
|
| 135 |
|
| 136 | |
| 137 | try:
|
| 138 | cc_info = subprocess.run(
|
| 139 | [cc, "-v"], stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, check=False
|
| 140 | )
|
| 141 | cc_is_clang = b"clang version" in cc_info.stderr
|
| 142 | except OSError as e:
|
| 143 | raise RuntimeError(f"Failed to execute compiler '{cc}': {e}") from e
|
| 144 |
|
| 145 | srctree = os.getenv('srctree')
|
| 146 | assert srctree, "Environment variable srctree, for the Linux sources, not set"
|
| 147 | src_feature_tests = f'{srctree}/tools/build/feature'
|
| 148 |
|
| 149 | if cc_is_clang:
|
| 150 | filter_clang_options(cc, cc_args, src_feature_tests)
|
| 151 |
|
| 152 | |
| 153 | cflags = [
|
| 154 | '-fno-strict-aliasing',
|
| 155 | '-Wno-write-strings',
|
| 156 | '-Wno-unused-parameter',
|
| 157 | '-Wno-redundant-decls'
|
| 158 | ]
|
| 159 | if cc_is_clang:
|
| 160 | cflags += ["-Wno-unused-command-line-argument"]
|
| 161 | if clang_has_option(
|
| 162 | cc, cc_args, src_feature_tests, "-Wno-cast-function-type-mismatch"
|
| 163 | ):
|
| 164 | cflags += ["-Wno-cast-function-type-mismatch"]
|
| 165 | else:
|
| 166 | cflags += ['-Wno-cast-function-type']
|
| 167 |
|
| 168 | |
| 169 | cflags += ["-Wno-declaration-after-statement"]
|
| 170 |
|
| 171 | src_perf = f'{srctree}/tools/perf'
|
| 172 |
|
| 173 | perf = Extension(
|
| 174 | 'perf',
|
| 175 | sources=[os.path.join(src_perf, 'util/python.c')],
|
| 176 | include_dirs=['util/include'],
|
| 177 | extra_compile_args=cflags,
|
| 178 | )
|
| 179 |
|
| 180 | setup(
|
| 181 | name='perf',
|
| 182 | version='0.1',
|
| 183 | description='Interface with the Linux profiling infrastructure',
|
| 184 | author='Arnaldo Carvalho de Melo',
|
| 185 | author_email='[email protected]',
|
| 186 | license='GPLv2',
|
| 187 | url='http://perf.wiki.kernel.org',
|
| 188 | ext_modules=[perf],
|
| 189 | cmdclass={'build_ext': BuildExt, 'install_lib': InstallLib},
|
| 190 | )
|
| 191 |
|
| 192 |
|
| 193 | if __name__ == '__main__':
|
| 194 | main()
|