156 lines
5.0 KiB
Rust
156 lines
5.0 KiB
Rust
use std::{
|
|
env, fs,
|
|
path::{Path, PathBuf},
|
|
process::Command,
|
|
};
|
|
|
|
const SUPPORTED_COMPILE_FIXTURES: &[&str] = &[
|
|
"../examples/add.slo",
|
|
"../examples/array-direct-scalars-value-flow.slo",
|
|
"../examples/array-direct-scalars.slo",
|
|
"../examples/array-enum.slo",
|
|
"../examples/array-struct-elements.slo",
|
|
"../examples/array-struct-fields.slo",
|
|
"../examples/array-string-value-flow.slo",
|
|
"../examples/array-string.slo",
|
|
"../examples/array-value-flow.slo",
|
|
"../examples/array.slo",
|
|
"../examples/enum-payload-structs.slo",
|
|
"../examples/if.slo",
|
|
"../examples/local-variables.slo",
|
|
"../examples/option-result-flow.slo",
|
|
"../examples/option-result-match.slo",
|
|
"../examples/option-result-payload.slo",
|
|
"../examples/option-result.slo",
|
|
"../examples/owned-string-concat.slo",
|
|
"../examples/f64-to-i32-result.slo",
|
|
"../examples/f64-to-i64-result.slo",
|
|
"../examples/f64-to-string.slo",
|
|
"../examples/print-bool.slo",
|
|
"../examples/standard-runtime.slo",
|
|
"../examples/string-parse-bool-result.slo",
|
|
"../examples/string-parse-i64-result.slo",
|
|
"../examples/string-parse-f64-result.slo",
|
|
"../examples/string-print.slo",
|
|
"../examples/string-value-flow.slo",
|
|
"../examples/struct-value-flow.slo",
|
|
"../examples/struct.slo",
|
|
"../examples/unsafe.slo",
|
|
"../examples/while.slo",
|
|
];
|
|
|
|
#[test]
|
|
#[ignore = "requires clang or GLAGOL_CLANG; optional promotion smoke for emitted LLVM IR"]
|
|
fn emitted_supported_fixture_ir_compiles_with_clang_when_available() {
|
|
let Some(clang) = find_clang() else {
|
|
eprintln!("skipping LLVM smoke test: set GLAGOL_CLANG or install clang");
|
|
return;
|
|
};
|
|
|
|
let manifest = Path::new(env!("CARGO_MANIFEST_DIR"));
|
|
let runtime = manifest.join("../runtime/runtime.c");
|
|
let temp_dir = env::temp_dir().join(format!("glagol-llvm-smoke-{}", std::process::id()));
|
|
fs::create_dir_all(&temp_dir)
|
|
.unwrap_or_else(|err| panic!("create `{}`: {}", temp_dir.display(), err));
|
|
|
|
for fixture in SUPPORTED_COMPILE_FIXTURES {
|
|
let ir = emit_fixture_ir(manifest, fixture);
|
|
let stem = fixture_stem(fixture);
|
|
let ir_path = temp_dir.join(format!("{}.ll", stem));
|
|
let exe_path = temp_dir.join(stem);
|
|
fs::write(&ir_path, ir)
|
|
.unwrap_or_else(|err| panic!("write `{}`: {}", ir_path.display(), err));
|
|
|
|
let mut command = Command::new(&clang);
|
|
command
|
|
.arg(&runtime)
|
|
.arg(&ir_path)
|
|
.arg("-o")
|
|
.arg(&exe_path)
|
|
.current_dir(manifest);
|
|
configure_clang_runtime_env(&mut command, &clang);
|
|
let output = command
|
|
.output()
|
|
.unwrap_or_else(|err| panic!("run `{}`: {}", clang.display(), err));
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
output.status.success(),
|
|
"clang rejected emitted LLVM for `{}`\nstdout:\n{}\nstderr:\n{}",
|
|
fixture,
|
|
stdout,
|
|
stderr
|
|
);
|
|
}
|
|
}
|
|
|
|
fn emit_fixture_ir(manifest: &Path, fixture: &str) -> String {
|
|
let output = Command::new(env!("CARGO_BIN_EXE_glagol"))
|
|
.arg(fixture)
|
|
.current_dir(manifest)
|
|
.output()
|
|
.unwrap_or_else(|err| panic!("run glagol on `{}`: {}", fixture, err));
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
output.status.success(),
|
|
"glagol rejected `{}`\nstdout:\n{}\nstderr:\n{}",
|
|
fixture,
|
|
stdout,
|
|
stderr
|
|
);
|
|
assert!(
|
|
stderr.is_empty(),
|
|
"glagol wrote stderr for `{}`: {}",
|
|
fixture,
|
|
stderr
|
|
);
|
|
stdout.into_owned()
|
|
}
|
|
|
|
fn find_clang() -> Option<PathBuf> {
|
|
if let Some(path) = env::var_os("GLAGOL_CLANG").filter(|value| !value.is_empty()) {
|
|
return Some(PathBuf::from(path));
|
|
}
|
|
|
|
let hermetic_clang = PathBuf::from("/tmp/glagol-clang-root/usr/bin/clang");
|
|
if hermetic_clang.is_file() {
|
|
return Some(hermetic_clang);
|
|
}
|
|
|
|
find_on_path("clang")
|
|
}
|
|
|
|
fn find_on_path(program: &str) -> Option<PathBuf> {
|
|
let path = env::var_os("PATH")?;
|
|
env::split_paths(&path)
|
|
.map(|dir| dir.join(program))
|
|
.find(|candidate| candidate.is_file())
|
|
}
|
|
|
|
fn configure_clang_runtime_env(command: &mut Command, clang: &Path) {
|
|
if !clang.starts_with("/tmp/glagol-clang-root") {
|
|
return;
|
|
}
|
|
|
|
let root = Path::new("/tmp/glagol-clang-root");
|
|
let lib64 = root.join("usr/lib64");
|
|
let lib = root.join("usr/lib");
|
|
let mut paths = vec![lib64, lib];
|
|
|
|
if let Some(existing) = env::var_os("LD_LIBRARY_PATH") {
|
|
paths.extend(env::split_paths(&existing));
|
|
}
|
|
|
|
let joined = env::join_paths(paths).expect("join LD_LIBRARY_PATH");
|
|
command.env("LD_LIBRARY_PATH", joined);
|
|
}
|
|
|
|
fn fixture_stem(fixture: &str) -> String {
|
|
Path::new(fixture)
|
|
.file_stem()
|
|
.and_then(|stem| stem.to_str())
|
|
.expect("fixture has utf-8 file stem")
|
|
.replace('-', "_")
|
|
}
|