aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDelilah Hoare <delilah@dhoare.me>2022-03-10 23:24:53 +1100
committerCole Faust <colecfaust@gmail.com>2022-11-28 12:42:27 -0800
commitd521068b030be1a6b2df61149a06d5f6bcda399e (patch)
treec0e7ee8c27cac5873d2b4fb439f78ae20149b4d0
parenta4f36671fe86d5f535a26047f275b5f356c5e6e6 (diff)
downloadkati-d521068b030be1a6b2df61149a06d5f6bcda399e.tar.gz
Implement $? for ninja
-rw-r--r--src/command.cc48
-rw-r--r--src/command.h3
-rw-r--r--src/eval.h9
-rw-r--r--src/newer.cc53
-rw-r--r--src/ninja.cc2
5 files changed, 101 insertions, 14 deletions
diff --git a/src/command.cc b/src/command.cc
index 41393ab..ca8a86e 100644
--- a/src/command.cc
+++ b/src/command.cc
@@ -127,14 +127,34 @@ void AutoStarVar::Eval(Evaluator*, string* s) const {
pat.Stem(n->output.str()).AppendToString(s);
}
-void AutoQuestionVar::Eval(Evaluator*, string* s) const {
+void AutoQuestionVar::Eval(Evaluator* ev, string* s) const {
unordered_set<StringPiece> seen;
- WordWriter ww(s);
- double target_age = GetTimestamp(ce_->current_dep_node()->output.str());
- for (Symbol ai : ce_->current_dep_node()->actual_inputs) {
- if (seen.insert(ai.str()).second
- && GetTimestamp(ai.str()) > target_age) {
+
+ if (ev->avoid_io()) {
+ // Postpone checking timestamps to the start of rule execution using
+ // the helper application `ckati-newer'.
+ *s += "$KATI_NEW_INPUTS";
+ if (ev->env_vars().find("KATI_NEW_INPUTS") == ev->env_vars().end()) {
+ string def;
+
+ WordWriter ww(&def);
+ ww.Write("$(ckati-newer");
+ ww.Write(ce_->current_dep_node()->output.str());
+ for (Symbol ai : ce_->current_dep_node()->actual_inputs) {
+ if (seen.insert(ai.str()).second) {
+ ww.Write(ai.str());
+ }
+ }
+ def += ")";
+ ev->set_env_var("KATI_NEW_INPUTS", def);
+ }
+ } else {
+ WordWriter ww(s);
+ double target_age = GetTimestamp(ce_->current_dep_node()->output.str());
+ for (Symbol ai : ce_->current_dep_node()->actual_inputs) {
+ if (seen.insert(ai.str()).second && GetTimestamp(ai.str()) > target_age) {
ww.Write(ai.str());
+ }
}
}
}
@@ -193,11 +213,7 @@ CommandEvaluator::CommandEvaluator(Evaluator* ev) : ev_(ev) {
INSERT_AUTO_VAR(AutoHatVar, "^");
INSERT_AUTO_VAR(AutoPlusVar, "+");
INSERT_AUTO_VAR(AutoStarVar, "*");
- if (!g_flags.generate_ninja) {
- INSERT_AUTO_VAR(AutoQuestionVar, "?");
- } else {
- INSERT_AUTO_VAR(AutoNotImplementedVar, "?");
- }
+ INSERT_AUTO_VAR(AutoQuestionVar, "?");
// TODO: Implement them.
INSERT_AUTO_VAR(AutoNotImplementedVar, "%");
INSERT_AUTO_VAR(AutoNotImplementedVar, "|");
@@ -242,8 +258,15 @@ std::vector<Command> CommandEvaluator::Eval(const DepNode& n) {
continue;
}
- if (!ev_->delayed_output_commands().empty()) {
+ if (!ev_->delayed_output_commands().empty() || !ev_->env_vars().empty()) {
std::vector<Command> output_commands;
+ for (auto& var : ev_->env_vars()) {
+ Command& c = output_commands.emplace_back(n.output);
+ c.cmd = var.first + "=" + var.second + " && export " + var.first;
+ c.echo = false;
+ c.ignore_error = false;
+ c.updates_environment = true;
+ }
for (const string& cmd : ev_->delayed_output_commands()) {
Command& c = output_commands.emplace_back(n.output);
c.cmd = cmd;
@@ -254,6 +277,7 @@ std::vector<Command> CommandEvaluator::Eval(const DepNode& n) {
result.swap(output_commands);
copy(output_commands.begin(), output_commands.end(), back_inserter(result));
ev_->clear_delayed_output_commands();
+ ev_->clear_env_vars();
}
ev_->set_current_scope(NULL);
diff --git a/src/command.h b/src/command.h
index 017f6bb..215502f 100644
--- a/src/command.h
+++ b/src/command.h
@@ -25,11 +25,12 @@ struct DepNode;
class Evaluator;
struct Command {
- explicit Command(Symbol o) : output(o), echo(true), ignore_error(false) {}
+ explicit Command(Symbol o) : output(o), echo(true), ignore_error(false), updates_environment(false) {}
Symbol output;
string cmd;
bool echo;
bool ignore_error;
+ bool updates_environment;
};
class CommandEvaluator {
diff --git a/src/eval.h b/src/eval.h
index fc7fcff..1959ef3 100644
--- a/src/eval.h
+++ b/src/eval.h
@@ -173,6 +173,14 @@ class Evaluator {
}
void clear_delayed_output_commands() { delayed_output_commands_.clear(); }
+ const unordered_map<string, string>& env_vars() const {
+ return env_vars_;
+ }
+ void set_env_var(const string& name, const string& value) {
+ env_vars_.insert_or_assign(name, value);
+ }
+ void clear_env_vars() { env_vars_.clear(); }
+
static const SymbolSet& used_undefined_vars() { return used_undefined_vars_; }
int eval_depth() const { return eval_depth_; }
@@ -270,6 +278,7 @@ class Evaluator {
// Commands which should run at ninja-time (i.e., info, warning, and
// error).
vector<string> delayed_output_commands_;
+ unordered_map<string, string> env_vars_;
Symbol posix_sym_;
bool is_posix_;
diff --git a/src/newer.cc b/src/newer.cc
new file mode 100644
index 0000000..ef3f535
--- /dev/null
+++ b/src/newer.cc
@@ -0,0 +1,53 @@
+// Copyright 2022 Google Inc. All rights reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build ignore
+
+// This is the only source file for ckati-newer, a helper utility
+// invoked by generated Ninja files to evaluate `$?`.
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+double GetTimestamp(const char* filename) {
+ struct stat st;
+ if (stat(filename, &st) < 0) {
+ return -2.0;
+ }
+#if defined(__linux__)
+ return st.st_mtime + st.st_mtim.tv_nsec * 0.001 * 0.001 * 0.001;
+#else
+ return st.st_mtime;
+#endif
+}
+
+int main(int argc, const char* argv[]) {
+ if (argc < 2) {
+ return 1;
+ }
+ double target_age = GetTimestamp(argv[1]);
+ bool first = true;
+ for (int i = 2; i < argc; i++) {
+ if (GetTimestamp(argv[i]) > target_age) {
+ if (first) {
+ first = false;
+ } else {
+ putchar(' ');
+ }
+ puts(argv[i]);
+ }
+ }
+ return 0;
+}
diff --git a/src/ninja.cc b/src/ninja.cc
index ee4a87f..aa6a08f 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -414,7 +414,7 @@ class NinjaGenerator {
while (isspace(*in))
in++;
- bool needs_subshell = (command_count > 1 || c.ignore_error);
+ bool needs_subshell = (command_count > 1 || c.ignore_error) && !c.updates_environment;
if (needs_subshell)
*cmd_buf += '(';