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> {
|
pub struct Build<'a> {
|
||||||
|
compiler: Option<&'a str>,
|
||||||
dir: Option<&'a Path>,
|
dir: Option<&'a Path>,
|
||||||
inputs: Vec<&'a str>,
|
inputs: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
@ -8,30 +42,41 @@ pub struct Build<'a> {
|
||||||
impl<'a> Build<'a> {
|
impl<'a> Build<'a> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
compiler: None,
|
||||||
dir: None,
|
dir: None,
|
||||||
inputs: Vec::new(),
|
inputs: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(self, output_path: &Path) -> io::Result<()> {
|
pub fn compile(self, output_path: &Path) -> Result<(), Error> {
|
||||||
let mut cmd = Command::new("cc");
|
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");
|
return match self.try_compile(cmd, output_path) {
|
||||||
|
Err(e) => Err(Error::BadCompiler {
|
||||||
if let Some(path) = self.dir {
|
cmd: compiler.to_owned(),
|
||||||
cmd.current_dir(path);
|
src: e,
|
||||||
|
}),
|
||||||
|
Ok(output) => to_res(output),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.args(&self.inputs);
|
for cc in DEFAULT_COMPILERS {
|
||||||
cmd.arg("-o").arg(output_path);
|
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();
|
pub fn compiler(&mut self, compiler: Option<&'a str>) -> &mut Self {
|
||||||
|
self.compiler = compiler;
|
||||||
Ok(())
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dir(&mut self, path: &'a Path) -> &mut Self {
|
pub fn dir(&mut self, path: &'a Path) -> &mut Self {
|
||||||
|
@ -45,4 +90,17 @@ impl<'a> Build<'a> {
|
||||||
}
|
}
|
||||||
self
|
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<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let Cli { language } = Cli::parse();
|
let Cli { language } = Cli::parse();
|
||||||
|
|
||||||
let Settings { parser_dir } = get_settings()?;
|
let Settings {
|
||||||
|
parser_dir,
|
||||||
|
compiler,
|
||||||
|
} = get_settings()?;
|
||||||
|
|
||||||
let src_dir = tempfile::tempdir()?;
|
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 output_path = parser_dir.join(language).with_extension(DLL_EXTENSION);
|
||||||
|
|
||||||
let mut build = cc::Build::new();
|
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)?;
|
build.compile(&output_path)?;
|
||||||
|
|
||||||
// close explicitly to prevent premature drop
|
// close explicitly to prevent premature drop
|
||||||
|
|
|
@ -13,6 +13,9 @@ lazy_static! {
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
#[serde(default = "default_parser_dir")]
|
#[serde(default = "default_parser_dir")]
|
||||||
pub parser_dir: PathBuf,
|
pub parser_dir: PathBuf,
|
||||||
|
|
||||||
|
#[serde(alias = "cc")]
|
||||||
|
pub compiler: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_parser_dir() -> PathBuf {
|
fn default_parser_dir() -> PathBuf {
|
||||||
|
|
Loading…
Add table
Reference in a new issue