diff --git a/Cargo.toml b/Cargo.toml index 0dfd40efa..642c82329 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ members = [ "block2", "block-sys", "tests", + "compile-tests", ] diff --git a/compile-tests/Cargo.toml b/compile-tests/Cargo.toml new file mode 100644 index 000000000..e04331b55 --- /dev/null +++ b/compile-tests/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "compile-tests" +version = "0.1.0" +edition = "2018" +publish = false + +repository = "https://github.com/madsmtm/objc2" +license = "MIT" + +[dependencies] +compiletest_rs = "0.7" +cargo_metadata = "0.14" diff --git a/compile-tests/assembly/test_msg_send_zero_cost.rs b/compile-tests/assembly/test_msg_send_zero_cost.rs new file mode 100644 index 000000000..80861b292 --- /dev/null +++ b/compile-tests/assembly/test_msg_send_zero_cost.rs @@ -0,0 +1,19 @@ +// Test that msg_send! is inlined into an objc_msgSend +// +// assembly-output: emit-asm +// only-x86 +// compile-flags: -Copt-level=2 -Clto=off + +#![crate_type = "lib"] + +use objc2::runtime::{Class, Object, Sel}; +use objc2::MessageReceiver; + +// CHECK-LABEL: handle: +// CHECK-NOT: j +// CHECK-NOT: call +// CHECK: jmp _objc_msgSend +#[no_mangle] +pub fn handle(obj: &Class, sel: Sel) -> *mut Object { + unsafe { MessageReceiver::send_message(&obj, sel, ()).unwrap() } +} diff --git a/compile-tests/build.rs b/compile-tests/build.rs new file mode 100644 index 000000000..0c69ff1b6 --- /dev/null +++ b/compile-tests/build.rs @@ -0,0 +1,13 @@ +use std::env; + +fn main() { + // For compile tests + println!( + "cargo:rustc-env=BUILD_PROFILE={}", + env::var("PROFILE").unwrap() + ); + println!( + "cargo:rustc-env=BUILD_TARGET={}", + env::var("TARGET").unwrap() + ); +} diff --git a/compile-tests/src/main.rs b/compile-tests/src/main.rs new file mode 100644 index 000000000..37645b78a --- /dev/null +++ b/compile-tests/src/main.rs @@ -0,0 +1,84 @@ +use cargo_metadata::camino::Utf8PathBuf; +use cargo_metadata::Message; +use std::env; +use std::io; +use std::path::Path; +use std::process::Command; +use std::process::Stdio; + +use compiletest_rs::{common::Mode, run_tests, Config}; + +fn get_rlib<'a>(filenames: impl IntoIterator) -> &'a Utf8PathBuf { + filenames + .into_iter() + .find(|name| name.extension() == Some("rlib")) + .expect("An rlib") +} + +fn main() -> io::Result<()> { + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + + // Build objc2 + let result = Command::new("cargo") + .current_dir(&manifest_dir.parent().unwrap()) + .arg("build") + .arg("-pobjc2") + .arg("--message-format=json-render-diagnostics") + .args(std::env::args().skip(1)) + .stdout(Stdio::piped()) + .output()?; + + // Extract metadata from build + let artifacts: Vec<_> = cargo_metadata::Message::parse_stream(&*result.stdout) + .filter_map(|message| { + if let Message::CompilerArtifact(artifact) = message.unwrap() { + if artifact.target.kind == ["lib"] && !artifact.profile.test { + return Some(artifact); + } + } + None + }) + .collect(); + let dep_dir = get_rlib(&artifacts[0].filenames).parent().unwrap(); + let flags = artifacts + .iter() + .map(|artifact| { + format!( + " --extern {name}={rlib}", + name = artifact.target.name, + rlib = get_rlib(&artifact.filenames), + ) + }) + .collect::(); + + let mut config = Config::default(); + config.target_rustcflags = Some(format!( + "-L dependency={dep}{flags}", + dep = dep_dir, + flags = flags, + )); + config.llvm_filecheck = Some( + env::var("FILECHECK") + .unwrap_or("FileCheck".to_string()) + .into(), + ); + config.edition = Some("2018".into()); + config.verbose = matches!( + std::env::var("CARGO_TERM_VERBOSE").as_deref(), + Ok("true" | "1") + ) || std::env::args().any(|val| val == "--verbose" || val == "-v"); + + // Run UI tests + config.src_base = manifest_dir.join("ui"); + config.mode = Mode::Ui; + run_tests(&config); + config.mode = Mode::CompileFail; + run_tests(&config); + + // Run Codegen tests + config.src_base = manifest_dir.join("assembly"); + config.mode = Mode::Assembly; + run_tests(&config); + + Ok(()) +} diff --git a/compile-tests/ui/msg_send_no_return_type.rs b/compile-tests/ui/msg_send_no_return_type.rs new file mode 100644 index 000000000..4bdfe470f --- /dev/null +++ b/compile-tests/ui/msg_send_no_return_type.rs @@ -0,0 +1,11 @@ +// Test that forgetting to annotate the return type fails +// See https://github.com/SSheldon/rust-objc/issues/62 +use objc2::{class, msg_send}; + +fn main() { + unsafe { + let cls = class!(NSObject); + msg_send![cls, new]; + //~^ ERROR type annotations needed + } +} diff --git a/compile-tests/ui/msg_send_no_return_type.stderr b/compile-tests/ui/msg_send_no_return_type.stderr new file mode 100644 index 000000000..59935c4f7 --- /dev/null +++ b/compile-tests/ui/msg_send_no_return_type.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/msg_send_no_return_type.rs:8:9 + | +8 | msg_send![cls, new]; + | ^^^^^^^^^^^^^^^^^^^ consider giving `result` a type + | + = note: this error originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`.