diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2021-09-25 14:42:12 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-25 14:42:12 +0300 |
commit | f6ae82169cd3780d2c9cd0cae7edfea8c427ef35 (patch) | |
tree | c5134832db2202f3c273ff6575a23b5464e0a3fe | |
parent | c0efa7ab22f8900f6fa1dadf0d306ec375569c8d (diff) | |
parent | 30202a24021587b7d7ddffd8312eb5b425b3e273 (diff) | |
download | meson-f6ae82169cd3780d2c9cd0cae7edfea8c427ef35.zip meson-f6ae82169cd3780d2c9cd0cae7edfea8c427ef35.tar.gz meson-f6ae82169cd3780d2c9cd0cae7edfea8c427ef35.tar.bz2 |
Merge pull request #8773 from dcbaker/submit/rustc-enhancements-clippy
More enhancements for Rust + clippy support
-rw-r--r-- | .pylintrc | 1 | ||||
-rw-r--r-- | ci/run.ps1 | 4 | ||||
-rw-r--r-- | docs/markdown/snippets/rust-clippy-driver-support.md | 6 | ||||
-rw-r--r-- | docs/markdown/snippets/rustc-improvements.md | 6 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 10 | ||||
-rw-r--r-- | mesonbuild/compilers/__init__.py | 3 | ||||
-rw-r--r-- | mesonbuild/compilers/detect.py | 27 | ||||
-rw-r--r-- | mesonbuild/compilers/rust.py | 45 | ||||
-rw-r--r-- | test cases/rust/1 basic/clippy.toml | 1 | ||||
-rw-r--r-- | test cases/rust/1 basic/prog.rs | 3 | ||||
-rw-r--r-- | unittests/allplatformstests.py | 13 | ||||
-rw-r--r-- | unittests/baseplatformtests.py | 5 |
12 files changed, 102 insertions, 22 deletions
@@ -16,6 +16,7 @@ enable= consider-using-enumerate, dangerous-default-value, deprecated-lambda, + function-redefined, len-as-condition, literal-comparison, missing-kwoa, @@ -13,6 +13,10 @@ if ($env:arch -eq 'x64') { } elseif ($env:arch -eq 'x86') { # Switch to the x86 Rust toolchain rustup default stable-i686-pc-windows-msvc + + # Also install clippy + rustup component add clippy + # Rust puts its shared stdlib in a secret place, but it is needed to run tests. $env:Path += ";$HOME/.rustup/toolchains/stable-i686-pc-windows-msvc/bin" # Need 32-bit Python for tests that need the Python dependency diff --git a/docs/markdown/snippets/rust-clippy-driver-support.md b/docs/markdown/snippets/rust-clippy-driver-support.md new file mode 100644 index 0000000..c486473 --- /dev/null +++ b/docs/markdown/snippets/rust-clippy-driver-support.md @@ -0,0 +1,6 @@ +## Support for clippy-driver as a rustc wrapper + +Clippy is a popular linting tool for Rust, and is invoked in place of rustc as a +wrapper. Unfortunately it doesn't proxy rustc's output, so we need to have a +small wrapper around it so that Meson can correctly detect the underlying rustc, +but still display clippy diff --git a/docs/markdown/snippets/rustc-improvements.md b/docs/markdown/snippets/rustc-improvements.md new file mode 100644 index 0000000..869888c --- /dev/null +++ b/docs/markdown/snippets/rustc-improvements.md @@ -0,0 +1,6 @@ +## Improvements for the Rustc compiler + +- Werror now works, this set's `-D warnings`, which will cause rustc to error + for every warning not explicitly disabled +- warning levels have been implemented +- support for meson's pic has been enabled diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 7c97ca3..5dbbf8b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1691,20 +1691,12 @@ class NinjaBackend(backends.Backend): if cratetype in {'bin', 'dylib'}: args.extend(rustc.get_linker_always_args()) - opt_proxy = self.get_compiler_options_for_target(target) - + args += self.generate_basic_compiler_args(target, rustc, False) args += ['--crate-name', target.name] - args += rustc.get_buildtype_args(self.get_option_for_target(OptionKey('buildtype'), target)) - args += rustc.get_debug_args(self.get_option_for_target(OptionKey('debug'), target)) - args += rustc.get_optimization_args(self.get_option_for_target(OptionKey('optimization'), target)) - args += rustc.get_option_compile_args(opt_proxy) - args += self.build.get_global_args(rustc, target.for_machine) - args += self.build.get_project_args(rustc, target.subproject, target.for_machine) depfile = os.path.join(target.subdir, target.name + '.d') args += ['--emit', f'dep-info={depfile}', '--emit', 'link'] args += target.get_extra_args('rust') args += rustc.get_output_args(os.path.join(target.subdir, target.get_filename())) - args += self.environment.coredata.get_external_args(target.for_machine, rustc.language) linkdirs = mesonlib.OrderedSet() external_deps = target.external_deps.copy() for d in target.link_targets: diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index 8c5275c..98b0b5f 100644 --- a/mesonbuild/compilers/__init__.py +++ b/mesonbuild/compilers/__init__.py @@ -112,6 +112,7 @@ __all__ = [ 'PGICPPCompiler', 'PGIFortranCompiler', 'RustCompiler', + 'ClippyRustCompiler', 'CcrxCCompiler', 'CcrxCPPCompiler', 'Xc16CCompiler', @@ -241,7 +242,7 @@ from .objcpp import ( ClangObjCPPCompiler, GnuObjCPPCompiler, ) -from .rust import RustCompiler +from .rust import RustCompiler, ClippyRustCompiler from .swift import SwiftCompiler from .vala import ValaCompiler from .mixins.visualstudio import VisualStudioLikeCompiler diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 59b425b..52ad7f3 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -124,7 +124,7 @@ from .objcpp import ( GnuObjCPPCompiler, ) from .cython import CythonCompiler -from .rust import RustCompiler +from .rust import RustCompiler, ClippyRustCompiler from .swift import SwiftCompiler from .vala import ValaCompiler from .mixins.visualstudio import VisualStudioLikeCompiler @@ -952,6 +952,13 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust continue version = search_version(out) + cls: T.Type[RustCompiler] = RustCompiler + + # Clippy is a wrapper around rustc, but it doesn't have rustc in it's + # output. We can otherwise treat it as rustc. + if 'clippy' in out: + out = 'rustc' + cls = ClippyRustCompiler if 'rustc' in out: # On Linux and mac rustc will invoke gcc (clang for mac @@ -970,19 +977,21 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust 'or use the RUST_LD environment variable, otherwise meson ' 'will override your selection.') + compiler = compiler.copy() # avoid mutating the original list + if override is None: extra_args: T.Dict[str, T.Union[str, bool]] = {} always_args: T.List[str] = [] if is_link_exe: - compiler.extend(RustCompiler.use_linker_args(cc.linker.exelist[0])) + compiler.extend(cls.use_linker_args(cc.linker.exelist[0])) extra_args['direct'] = True extra_args['machine'] = cc.linker.machine else: - exelist = cc.linker.exelist.copy() + exelist = cc.linker.exelist + cc.linker.get_always_args() if 'ccache' in exelist[0]: del exelist[0] c = exelist.pop(0) - compiler.extend(RustCompiler.use_linker_args(c)) + compiler.extend(cls.use_linker_args(c)) # Also ensure that we pass any extra arguments to the linker for l in exelist: @@ -1000,12 +1009,12 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust **extra_args) # type: ignore elif 'link' in override[0]: linker = guess_win_linker(env, - override, RustCompiler, for_machine, use_linker_prefix=False) + override, cls, for_machine, use_linker_prefix=False) # rustc takes linker arguments without a prefix, and # inserts the correct prefix itself. assert isinstance(linker, VisualStudioLikeLinkerMixin) linker.direct = True - compiler.extend(RustCompiler.use_linker_args(linker.exelist[0])) + compiler.extend(cls.use_linker_args(linker.exelist[0])) else: # On linux and macos rust will invoke the c compiler for # linking, on windows it will use lld-link or link.exe. @@ -1017,10 +1026,10 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust # Of course, we're not going to use any of that, we just # need it to get the proper arguments to pass to rustc c = linker.exelist[1] if linker.exelist[0].endswith('ccache') else linker.exelist[0] - compiler.extend(RustCompiler.use_linker_args(c)) + compiler.extend(cls.use_linker_args(c)) - env.coredata.add_lang_args(RustCompiler.language, RustCompiler, for_machine, env) - return RustCompiler( + env.coredata.add_lang_args(cls.language, cls, for_machine, env) + return cls( compiler, version, for_machine, is_cross, info, exe_wrap, linker=linker) diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 2b566c8..9423b2d 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -45,6 +45,13 @@ class RustCompiler(Compiler): # rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX language = 'rust' + _WARNING_LEVELS: T.Dict[str, T.List[str]] = { + '0': ['-A', 'warnings'], + '1': [], + '2': [], + '3': ['-W', 'warnings'], + } + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool, info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None, @@ -168,3 +175,41 @@ class RustCompiler(Compiler): for a in super().get_linker_always_args(): args.extend(['-C', f'link-arg={a}']) return args + + def get_werror_args(self) -> T.List[str]: + # Use -D warnings, which makes every warning not explicitly allowed an + # error + return ['-D', 'warnings'] + + def get_warn_args(self, level: str) -> T.List[str]: + # TODO: I'm not really sure what to put here, Rustc doesn't have warning + return self._WARNING_LEVELS[level] + + def get_no_warn_args(self) -> T.List[str]: + return self._WARNING_LEVELS["0"] + + def get_pic_args(self) -> T.List[str]: + # This defaults to + return ['-C', 'relocation-model=pic'] + + def get_pie_args(self) -> T.List[str]: + # Rustc currently has no way to toggle this, it's controlled by whether + # pic is on by rustc + return [] + + +class ClippyRustCompiler(RustCompiler): + + """Clippy is a linter that wraps Rustc. + + This just provides us a different id + """ + + def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, + is_cross: bool, info: 'MachineInfo', + exe_wrapper: T.Optional['ExternalProgram'] = None, + full_version: T.Optional[str] = None, + linker: T.Optional['DynamicLinker'] = None): + super().__init__(exelist, version, for_machine, is_cross, info, + exe_wrapper, full_version, linker) + self.id = 'clippy-driver rustc' diff --git a/test cases/rust/1 basic/clippy.toml b/test cases/rust/1 basic/clippy.toml new file mode 100644 index 0000000..e9ac31b --- /dev/null +++ b/test cases/rust/1 basic/clippy.toml @@ -0,0 +1 @@ +blacklisted-names = ["foo"] diff --git a/test cases/rust/1 basic/prog.rs b/test cases/rust/1 basic/prog.rs index b171a80..f1b3d30 100644 --- a/test cases/rust/1 basic/prog.rs +++ b/test cases/rust/1 basic/prog.rs @@ -1,3 +1,4 @@ fn main() { - println!("rust compiler is working"); + let foo = "rust compiler is working"; + println!("{}", foo); } diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 7afa989..93a2e49 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -4034,3 +4034,16 @@ class AllPlatformTests(BasePlatformTests): for file, details in files.items(): with self.subTest(key='{}.{}'.format(data_type, file)): self.assertEqual(res[data_type][file], details) + + @skip_if_not_language('rust') + @unittest.skipIf(not shutil.which('clippy-driver'), 'Test requires clippy-driver') + def test_rust_clippy(self) -> None: + if self.backend is not Backend.ninja: + raise unittest.SkipTest('Rust is only supported with ninja currently') + # Wehn clippy is used, we should get an exception since a variable named + # "foo" is used, but is on our denylist + testdir = os.path.join(self.rust_test_dir, '1 basic') + self.init(testdir, extra_args=['--werror'], override_envvars={'RUSTC': 'clippy-driver'}) + with self.assertRaises(subprocess.CalledProcessError) as cm: + self.build() + self.assertIn('error: use of a blacklisted/placeholder name `foo`', cm.exception.stdout) diff --git a/unittests/baseplatformtests.py b/unittests/baseplatformtests.py index 3492785..9371395 100644 --- a/unittests/baseplatformtests.py +++ b/unittests/baseplatformtests.py @@ -69,6 +69,7 @@ class BasePlatformTests(TestCase): self.uninstall_command = get_backend_commands(self.backend) # Test directories self.common_test_dir = os.path.join(src_root, 'test cases/common') + self.rust_test_dir = os.path.join(src_root, 'test cases/rust') self.vala_test_dir = os.path.join(src_root, 'test cases/vala') self.framework_test_dir = os.path.join(src_root, 'test cases/frameworks') self.unit_test_dir = os.path.join(src_root, 'test cases/unit') @@ -135,7 +136,7 @@ class BasePlatformTests(TestCase): os.environ.update(self.orig_env) super().tearDown() - def _run(self, command, *, workdir=None, override_envvars=None): + def _run(self, command, *, workdir=None, override_envvars: T.Optional[T.Mapping[str, str]] = None): ''' Run a command while printing the stdout and stderr to stdout, and also return a copy of it @@ -164,7 +165,7 @@ class BasePlatformTests(TestCase): extra_args=None, default_args=True, inprocess=False, - override_envvars=None, + override_envvars: T.Optional[T.Mapping[str, str]] = None, workdir=None, allow_fail: bool = False) -> str: """Call `meson setup` |