automatically find C compiler and let user set one
This commit is contained in:
parent
0cd8af9c81
commit
01090ac792
3 changed files with 83 additions and 16 deletions
86
src/cc.rs
86
src/cc.rs
|
@ -1,6 +1,40 @@
|
|||
use std::{io, path::Path, process::Command};
|
||||
use std::{
|
||||
io,
|
||||
path::Path,
|
||||
process::{Command, ExitStatus, Output},
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// no compiler specified, all defaults failed
|
||||
#[error("couldn't find a C compiler")]
|
||||
NoCompiler,
|
||||
|
||||
/// user specified a compiler and it didn't work
|
||||
#[error("couldn't execute `{cmd}`: {src}")]
|
||||
BadCompiler { cmd: String, src: io::Error },
|
||||
|
||||
/// user specified a compiler and it's nonsense
|
||||
#[error("compiler can't be empty")]
|
||||
EmptyCompiler,
|
||||
|
||||
/// compiler ran but failed for some reason
|
||||
#[error("compiler failed with {status}\n\n{}", String::from_utf8_lossy(.stderr))]
|
||||
ExecFailed { status: ExitStatus, stderr: Vec<u8> },
|
||||
}
|
||||
|
||||
fn to_res(Output { status, stderr, .. }: Output) -> Result<(), Error> {
|
||||
status
|
||||
.success()
|
||||
.then_some(())
|
||||
.ok_or(Error::ExecFailed { status, stderr })
|
||||
}
|
||||
|
||||
// there's probably a platform dependent better order for searching these. too bad !
|
||||
const DEFAULT_COMPILERS: [&str; 3] = ["cc", "gcc", "clang"];
|
||||
|
||||
pub struct Build<'a> {
|
||||
compiler: Option<&'a str>,
|
||||
dir: Option<&'a Path>,
|
||||
inputs: Vec<&'a str>,
|
||||
}
|
||||
|
@ -8,30 +42,41 @@ pub struct Build<'a> {
|
|||
impl<'a> Build<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
compiler: None,
|
||||
dir: None,
|
||||
inputs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(self, output_path: &Path) -> io::Result<()> {
|
||||
let mut cmd = Command::new("cc");
|
||||
pub fn compile(self, output_path: &Path) -> Result<(), Error> {
|
||||
if let Some(compiler) = self.compiler {
|
||||
let mut parts = compiler.split_whitespace();
|
||||
let mut cmd = Command::new(parts.next().ok_or(Error::EmptyCompiler)?);
|
||||
cmd.args(parts);
|
||||
|
||||
cmd.arg("-fpic").arg("-shared").arg("-std=c11");
|
||||
|
||||
if let Some(path) = self.dir {
|
||||
cmd.current_dir(path);
|
||||
return match self.try_compile(cmd, output_path) {
|
||||
Err(e) => Err(Error::BadCompiler {
|
||||
cmd: compiler.to_owned(),
|
||||
src: e,
|
||||
}),
|
||||
Ok(output) => to_res(output),
|
||||
};
|
||||
}
|
||||
|
||||
cmd.args(&self.inputs);
|
||||
cmd.arg("-o").arg(output_path);
|
||||
for cc in DEFAULT_COMPILERS {
|
||||
let cmd = Command::new(cc);
|
||||
|
||||
dbg!(&cmd);
|
||||
if let Ok(output) = self.try_compile(cmd, output_path) {
|
||||
return to_res(output);
|
||||
}
|
||||
}
|
||||
|
||||
let mut child = cmd.spawn()?;
|
||||
Err(Error::NoCompiler)
|
||||
}
|
||||
|
||||
let _ = child.wait();
|
||||
|
||||
Ok(())
|
||||
pub fn compiler(&mut self, compiler: Option<&'a str>) -> &mut Self {
|
||||
self.compiler = compiler;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn dir(&mut self, path: &'a Path) -> &mut Self {
|
||||
|
@ -45,4 +90,17 @@ impl<'a> Build<'a> {
|
|||
}
|
||||
self
|
||||
}
|
||||
|
||||
fn try_compile(&self, mut cmd: Command, output_path: &Path) -> io::Result<Output> {
|
||||
cmd.arg("-fpic").arg("-shared").arg("-std=c11");
|
||||
|
||||
if let Some(path) = self.dir {
|
||||
cmd.current_dir(path);
|
||||
}
|
||||
|
||||
cmd.args(&self.inputs);
|
||||
cmd.arg("-o").arg(output_path);
|
||||
|
||||
cmd.output()
|
||||
}
|
||||
}
|
||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -19,7 +19,10 @@ struct Cli {
|
|||
fn main() -> anyhow::Result<()> {
|
||||
let Cli { language } = Cli::parse();
|
||||
|
||||
let Settings { parser_dir } = get_settings()?;
|
||||
let Settings {
|
||||
parser_dir,
|
||||
compiler,
|
||||
} = get_settings()?;
|
||||
|
||||
let src_dir = tempfile::tempdir()?;
|
||||
|
||||
|
@ -37,7 +40,10 @@ fn main() -> anyhow::Result<()> {
|
|||
let output_path = parser_dir.join(language).with_extension(DLL_EXTENSION);
|
||||
|
||||
let mut build = cc::Build::new();
|
||||
build.dir(&repo_path).input_files(files);
|
||||
build
|
||||
.compiler(compiler.as_deref())
|
||||
.dir(&repo_path)
|
||||
.input_files(files);
|
||||
build.compile(&output_path)?;
|
||||
|
||||
// close explicitly to prevent premature drop
|
||||
|
|
|
@ -13,6 +13,9 @@ lazy_static! {
|
|||
pub struct Settings {
|
||||
#[serde(default = "default_parser_dir")]
|
||||
pub parser_dir: PathBuf,
|
||||
|
||||
#[serde(alias = "cc")]
|
||||
pub compiler: Option<String>,
|
||||
}
|
||||
|
||||
fn default_parser_dir() -> PathBuf {
|
||||
|
|
Loading…
Add table
Reference in a new issue