aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2021-04-06 16:20:09 -0600
committerMelody Horn <melody@boringcactus.com>2021-04-06 16:20:09 -0600
commit110029ce3b7f205cce01f7d74dd2c8777f860c31 (patch)
tree2f5ca5070463678880c2b9893fc567acb63398df
parentcffe9f4085cf28e9a1e62346cfb31ed0470a49ff (diff)
downloadmakers-110029ce3b7f205cce01f7d74dd2c8777f860c31.tar.gz
makers-110029ce3b7f205cce01f7d74dd2c8777f860c31.zip
implement exported variables
-rw-r--r--src/makefile/command_line.rs24
-rw-r--r--src/makefile/input.rs22
-rw-r--r--src/makefile/macro.rs85
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<'_, '_> {