diff options
Diffstat (limited to 'src/makefile')
-rw-r--r-- | src/makefile/command_line.rs | 24 | ||||
-rw-r--r-- | src/makefile/input.rs | 22 | ||||
-rw-r--r-- | src/makefile/macro.rs | 85 |
3 files changed, 121 insertions, 10 deletions
diff --git a/src/makefile/command_line.rs b/src/makefile/command_line.rs index ec2c115..1b4b4b4 100644 --- a/src/makefile/command_line.rs +++ b/src/makefile/command_line.rs @@ -1,16 +1,20 @@ use std::env; use std::fmt; -use std::io; use std::process::{Command, ExitStatus}; -use eyre::bail; +use eyre::{bail, Error}; -use crate::makefile::target::Target; -use crate::makefile::token::{Token, TokenString}; -use crate::makefile::Makefile; +use super::r#macro::Set as MacroSet; +use super::target::Target; +use super::token::{Token, TokenString}; +use super::Makefile; // inspired by python's subprocess module -fn execute_command_line(command_line: &str, ignore_errors: bool) -> Result<ExitStatus, io::Error> { +fn execute_command_line( + command_line: &str, + ignore_errors: bool, + macros: &MacroSet, +) -> Result<ExitStatus, Error> { let (program, args) = if cfg!(windows) { let cmd = env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".into()); let args = vec!["/c", command_line]; @@ -24,7 +28,11 @@ fn execute_command_line(command_line: &str, ignore_errors: bool) -> Result<ExitS }; (sh, args) }; - Command::new(program).args(args).status() + let mut command = Command::new(program); + command.args(args); + #[cfg(feature = "full")] + command.envs(macros.resolve_exports()?); + Ok(command.status()?) } #[derive(PartialEq, Eq, Clone, Debug)] @@ -108,7 +116,7 @@ impl CommandLine { return Ok(()); } - let return_value = execute_command_line(&execution_line, ignore_error); + let return_value = execute_command_line(&execution_line, ignore_error, &file.macros); let errored = return_value.map_or(true, |status| !status.success()); if errored { // apparently there was an error. do we care? diff --git a/src/makefile/input.rs b/src/makefile/input.rs index 13ced8e..7651306 100644 --- a/src/makefile/input.rs +++ b/src/makefile/input.rs @@ -16,6 +16,8 @@ use super::command_line::CommandLine; #[cfg(feature = "full")] use super::conditional::{Line as ConditionalLine, State as ConditionalState}; use super::inference_rules::InferenceRule; +#[cfg(feature = "full")] +use super::r#macro::ExportConfig; use super::r#macro::{Macro, Set as MacroSet, Source as MacroSource}; use super::target::Target; use super::token::{tokenize, Token, TokenString}; @@ -295,12 +297,27 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> { LineType::Macro => self.read_macro(line_tokens, line_number)?, LineType::Unknown => { if !line_tokens.is_empty() { + // TODO handle assignments here #[cfg(feature = "full")] if line_tokens.starts_with("export") { - log::error!("export directive not supported yet"); + let mut line_tokens = line_tokens; + line_tokens.strip_prefix("export"); + if line_tokens.is_empty() { + self.macros.exported = ExportConfig::all_but(); + } else { + let exported = self.expand_macros(&line_tokens)?; + self.macros.exported.add_all(exported.split_whitespace()); + } continue; } else if line_tokens.starts_with("unexport") { - log::error!("unexport directive not supported yet"); + let mut line_tokens = line_tokens; + line_tokens.strip_prefix("unexport"); + if line_tokens.is_empty() { + self.macros.exported = ExportConfig::only(); + } else { + let exported = self.expand_macros(&line_tokens)?; + self.macros.exported.remove_all(exported.split_whitespace()); + } continue; } bail!( @@ -423,6 +440,7 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> { let (targets, not_targets) = line_tokens .split_once(':') .ok_or_else(|| eyre!("read_rule couldn't find a ':' on line {}", line_number))?; + // TODO handle rule-specific variables let targets = self.expand_macros(&targets)?; let targets = targets.split_whitespace().collect::<Vec<_>>(); let (prerequisites, mut commands) = match not_targets.split_once(';') { diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs index 2c8747c..5e7ea9c 100644 --- a/src/makefile/macro.rs +++ b/src/makefile/macro.rs @@ -1,6 +1,8 @@ #[cfg(feature = "full")] use std::cell::RefCell; use std::collections::HashMap; +#[cfg(feature = "full")] +use std::collections::HashSet; use std::env; use std::fmt; #[cfg(feature = "full")] @@ -33,6 +35,64 @@ pub trait LookupInternal: for<'a> Fn(&'a str) -> Result<String> {} impl<F: for<'a> Fn(&'a str) -> Result<String>> LookupInternal for F {} +#[cfg(feature = "full")] +#[derive(Clone)] +pub enum ExportConfig { + Only(HashSet<String>), + AllBut(HashSet<String>), +} + +#[cfg(feature = "full")] +impl ExportConfig { + pub fn all_but() -> Self { + Self::AllBut(HashSet::new()) + } + + pub fn only() -> Self { + Self::Only(HashSet::new()) + } + + pub fn add_all<'a, I: IntoIterator<Item = &'a str>>(&mut self, iter: I) { + match self { + Self::Only(exported) => { + exported.extend(iter.into_iter().map(|x| x.to_owned())); + } + Self::AllBut(not_exported) => { + for added in iter { + not_exported.remove(added); + } + } + } + } + + pub fn remove_all<'a, I: IntoIterator<Item = &'a str>>(&mut self, iter: I) { + match self { + Self::Only(exported) => { + for removed in iter { + exported.remove(removed); + } + } + Self::AllBut(not_exported) => { + not_exported.extend(iter.into_iter().map(|x| x.into())); + } + } + } + + fn same_type(&self) -> Self { + match self { + Self::Only(_) => Self::only(), + Self::AllBut(_) => Self::all_but(), + } + } + + fn should_export(&self, x: &str) -> bool { + match self { + Self::Only(exported) => exported.contains(x), + Self::AllBut(not_exported) => !not_exported.contains(x), + } + } +} + #[derive(Clone)] pub struct Set<'parent, 'lookup> { parent: Option<&'parent Set<'parent, 'lookup>>, @@ -40,6 +100,8 @@ pub struct Set<'parent, 'lookup> { lookup_internal: Option<&'lookup dyn LookupInternal>, #[cfg(feature = "full")] pub to_eval: Rc<RefCell<Vec<String>>>, + #[cfg(feature = "full")] + pub exported: ExportConfig, } impl<'parent, 'lookup> Set<'parent, 'lookup> { @@ -50,6 +112,8 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { lookup_internal: None, #[cfg(feature = "full")] to_eval: Rc::new(RefCell::new(Vec::new())), + #[cfg(feature = "full")] + exported: ExportConfig::only(), } } @@ -209,6 +273,8 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { lookup_internal: Some(lookup), #[cfg(feature = "full")] to_eval: Rc::clone(&self.to_eval), + #[cfg(feature = "full")] + exported: self.exported.same_type(), } } @@ -219,8 +285,27 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { lookup_internal: None, #[cfg(feature = "full")] to_eval: Rc::clone(&self.to_eval), + #[cfg(feature = "full")] + exported: self.exported.same_type(), } } + + #[cfg(feature = "full")] + pub fn resolve_exports(&self) -> Result<Vec<(&str, String)>> { + let own_exports = self + .data + .iter() + .filter(|(name, _)| self.exported.should_export(name)) + .map(|(name, value)| self.expand(&value.text).map(|text| (name.as_ref(), text))) + .collect::<Result<Vec<_>>>()?; + Ok(if let Some(parent) = self.parent { + let mut parent_exports = parent.resolve_exports()?; + parent_exports.extend(own_exports); + parent_exports + } else { + own_exports + }) + } } impl fmt::Display for Set<'_, '_> { |