aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-prod (mdb) <android-build-team-robot@google.com>2020-06-25 21:44:24 +0000
committerandroid-build-prod (mdb) <android-build-team-robot@google.com>2020-06-25 21:44:24 +0000
commitb9334d42d579ce8f1733ae2980208abbddabdc53 (patch)
treea26d7e9d90ee0b905988c1d79f24b6ab11616c5c
parent7e482956494082e23e56a74ab44896001fcbc106 (diff)
parente184ff147273cfd25d37692dba4be72eca571e9f (diff)
downloadplatform_build_kati-b9334d42d579ce8f1733ae2980208abbddabdc53.tar.gz
platform_build_kati-b9334d42d579ce8f1733ae2980208abbddabdc53.tar.bz2
platform_build_kati-b9334d42d579ce8f1733ae2980208abbddabdc53.zip
Snap for 6629315 from e184ff147273cfd25d37692dba4be72eca571e9f to build-tools-release
Change-Id: I2abeee0c1aac66b955349dacc282f401f537bdf3
-rw-r--r--.github/workflows/cpp-ci.yml44
-rw-r--r--.travis.yml36
-rw-r--r--README.md2
-rw-r--r--affinity.cc4
-rwxr-xr-xclang-format-check3
-rw-r--r--dep.cc47
-rw-r--r--dep.h2
-rw-r--r--eval.cc22
-rw-r--r--file_cache.h1
-rw-r--r--fileutil_bench.cc2
-rw-r--r--find_test.cc1
-rw-r--r--flags.cc2
-rw-r--r--flags.h1
-rw-r--r--ninja.cc10
-rw-r--r--ninja_test.cc3
-rw-r--r--rule.h1
-rwxr-xr-xruntest.rb142
-rw-r--r--string_piece.cc4
-rw-r--r--testcase/empty_target_specific_var.mk2
-rw-r--r--testcase/empty_target_specific_var2.mk2
-rw-r--r--testcase/multiline_arg.mk2
-rwxr-xr-xtestcase/ninja_regen_filefunc_read.sh4
-rw-r--r--testcase/ninja_validations.sh51
23 files changed, 243 insertions, 145 deletions
diff --git a/.github/workflows/cpp-ci.yml b/.github/workflows/cpp-ci.yml
new file mode 100644
index 0000000..c076cf1
--- /dev/null
+++ b/.github/workflows/cpp-ci.yml
@@ -0,0 +1,44 @@
+name: Build and Test
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ env:
+ CXX: clang++-9
+ CLANG_FORMAT: clang-format-9
+
+ steps:
+ - uses: actions/checkout@v2.2.0
+ - name: install ninja
+ run: |
+ mkdir -p ${GITHUB_WORKSPACE}/ninja-bin; cd ${GITHUB_WORKSPACE}/ninja-bin
+ wget https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-linux.zip
+ unzip ninja-linux.zip
+ rm ninja-linux.zip
+ echo "::add-path::${GITHUB_WORKSPACE}/ninja-bin"
+ - name: make
+ run: make -j4 ckati ckati_tests
+ - name: clang format
+ run: ./clang-format-check
+ - name: run standalone tests
+ run: ruby runtest.rb -c
+ - name: run ninja tests
+ run: ruby runtest.rb -c -n
+ - name: run ninja all targets tests
+ run: ruby runtest.rb -c -n -a
+ - name: run ninja unit tests
+ run: ./ninja_test
+ - name: run stringpiece unit tests
+ run: ./string_piece_test
+ - name: run strutil unit tests
+ run: ./strutil_test
+ - name: run find unit tests
+ run: ./find_test
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index e2d9e21..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-language: cpp
-
-dist: trusty
-sudo: required
-
-compiler:
- - clang
-
-addons:
- apt:
- update: true
- sources:
- - ubuntu-toolchain-r-test
- - llvm-toolchain-trusty-7
- packages:
- - clang-7
- - clang-format-7
- - realpath
-
-cache: apt
-
-before_script:
- - wget https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-linux.zip
- - unzip ninja-linux.zip -d ~/bin
-
-script:
- - export CXX=clang++-7
- - make -j4 ckati ckati_tests
- - ./clang-format-check
- - ruby runtest.rb -c
- - ruby runtest.rb -c -n
- - ruby runtest.rb -c -n -a
- - ./ninja_test
- - ./string_piece_test
- - ./strutil_test
- - ./find_test
diff --git a/README.md b/README.md
index 029a57d..ee8b51f 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
kati
====
-[![Build Status](https://travis-ci.org/google/kati.svg?branch=master)](http://travis-ci.org/google/kati)
+[![Build and Test](https://github.com/google/kati/workflows/Build%20and%20Test/badge.svg)](https://github.com/google/kati/actions)
kati is an experimental GNU make clone.
The main goal of this tool is to speed-up incremental build of Android.
diff --git a/affinity.cc b/affinity.cc
index 92320ee..9e855f3 100644
--- a/affinity.cc
+++ b/affinity.cc
@@ -21,12 +21,12 @@
#ifdef __linux__
-#include <random>
-
#include <sched.h>
#include <sys/types.h>
#include <unistd.h>
+#include <random>
+
void SetAffinityForSingleThread() {
cpu_set_t cs;
CPU_ZERO(&cs);
diff --git a/clang-format-check b/clang-format-check
index 7eaa1c2..c0a01f2 100755
--- a/clang-format-check
+++ b/clang-format-check
@@ -1,7 +1,6 @@
#!/usr/bin/env bash
-CLANG_FORMAT="clang-format-7"
-if [ -z "$(which $CLANG_FORMAT)" ]; then
+if [ -z "$CLANG_FORMAT" ]; then
CLANG_FORMAT="clang-format"
fi
diff --git a/dep.cc b/dep.cc
index a4eb2d2..3289d7a 100644
--- a/dep.cc
+++ b/dep.cc
@@ -142,6 +142,7 @@ bool IsSuffixRule(Symbol output) {
struct RuleMerger {
vector<const Rule*> rules;
vector<pair<Symbol, RuleMerger*>> implicit_outputs;
+ vector<Symbol> validations;
const Rule* primary_rule;
const RuleMerger* parent;
Symbol parent_sym;
@@ -154,6 +155,8 @@ struct RuleMerger {
implicit_outputs.push_back(make_pair(output, merger));
}
+ void AddValidation(Symbol validation) { validations.push_back(validation); }
+
void SetImplicitOutput(Symbol output, Symbol p, const RuleMerger* merger) {
if (!merger->primary_rule) {
ERROR("*** implicit output `%s' on phony target `%s'", output.c_str(),
@@ -252,6 +255,10 @@ struct RuleMerger {
FillDepNodeFromRule(output, r, n);
}
}
+
+ for (auto& validation : validations) {
+ n->actual_validations.push_back(validation);
+ }
}
};
@@ -279,7 +286,8 @@ class DepBuilder {
implicit_rules_(new RuleTrie()),
depfile_var_name_(Intern(".KATI_DEPFILE")),
implicit_outputs_var_name_(Intern(".KATI_IMPLICIT_OUTPUTS")),
- ninja_pool_var_name_(Intern(".KATI_NINJA_POOL")) {
+ ninja_pool_var_name_(Intern(".KATI_NINJA_POOL")),
+ validations_var_name_(Intern(".KATI_VALIDATIONS")) {
ScopedTimeReporter tr("make dep (populate)");
PopulateRules(rules);
// TODO?
@@ -412,17 +420,26 @@ class DepBuilder {
continue;
}
auto var = vars->Lookup(implicit_outputs_var_name_);
- if (!var->IsDefined()) {
- continue;
+ if (var->IsDefined()) {
+ string implicit_outputs;
+ var->Eval(ev_, &implicit_outputs);
+
+ for (StringPiece output : WordScanner(implicit_outputs)) {
+ Symbol sym = Intern(TrimLeadingCurdir(output));
+ rules_[sym].SetImplicitOutput(sym, p.first, &p.second);
+ p.second.AddImplicitOutput(sym, &rules_[sym]);
+ }
}
- string implicit_outputs;
- var->Eval(ev_, &implicit_outputs);
+ var = vars->Lookup(validations_var_name_);
+ if (var->IsDefined()) {
+ string validations;
+ var->Eval(ev_, &validations);
- for (StringPiece output : WordScanner(implicit_outputs)) {
- Symbol sym = Intern(TrimLeadingCurdir(output));
- rules_[sym].SetImplicitOutput(sym, p.first, &p.second);
- p.second.AddImplicitOutput(sym, &rules_[sym]);
+ for (StringPiece validation : WordScanner(validations)) {
+ Symbol sym = Intern(TrimLeadingCurdir(validation));
+ p.second.AddValidation(sym);
+ }
}
}
}
@@ -684,6 +701,7 @@ class DepBuilder {
if (name == depfile_var_name_) {
n->depfile_var = new_var;
} else if (name == implicit_outputs_var_name_) {
+ } else if (name == validations_var_name_) {
} else if (name == ninja_pool_var_name_) {
n->ninja_pool_var = new_var;
} else {
@@ -790,6 +808,16 @@ class DepBuilder {
n->order_onlys.push_back({input, c});
}
+ for (Symbol validation : n->actual_validations) {
+ if (!g_flags.use_ninja_validations) {
+ ERROR_LOC(
+ n->loc,
+ ".KATI_VALIDATIONS not allowed without --use_ninja_validations");
+ }
+ DepNode* c = BuildPlan(validation, output);
+ n->validations.push_back({validation, c});
+ }
+
// Block on werror_writable/werror_phony_looks_real, because otherwise we
// can't rely on is_phony being valid for this check.
if (!n->is_phony && n->cmds.empty() && g_flags.werror_writable &&
@@ -867,6 +895,7 @@ class DepBuilder {
Symbol depfile_var_name_;
Symbol implicit_outputs_var_name_;
Symbol ninja_pool_var_name_;
+ Symbol validations_var_name_;
};
void MakeDep(Evaluator* ev,
diff --git a/dep.h b/dep.h
index 963447b..3ab52ac 100644
--- a/dep.h
+++ b/dep.h
@@ -39,6 +39,7 @@ struct DepNode {
vector<Value*> cmds;
vector<NamedDepNode> deps;
vector<NamedDepNode> order_onlys;
+ vector<NamedDepNode> validations;
bool has_rule;
bool is_default_target;
bool is_phony;
@@ -46,6 +47,7 @@ struct DepNode {
vector<Symbol> implicit_outputs;
vector<Symbol> actual_inputs;
vector<Symbol> actual_order_only_inputs;
+ vector<Symbol> actual_validations;
Vars* rule_vars;
Var* depfile_var;
Var* ninja_pool_var;
diff --git a/eval.cc b/eval.cc
index aaad21f..3772763 100644
--- a/eval.cc
+++ b/eval.cc
@@ -300,26 +300,10 @@ void Evaluator::EvalRule(const RuleStmt* stmt) {
return;
}
- // "test: =foo" is questionable but a valid rule definition (not a
- // target specific variable).
- // See https://github.com/google/kati/issues/83
- string buf;
if (!separator_pos) {
- KATI_WARN_LOC(loc_,
- "defining a target which starts with `=', "
- "which is not probably what you meant");
- buf = after_targets.as_string();
- if (stmt->sep == RuleStmt::SEP_SEMICOLON) {
- buf += ';';
- } else if (stmt->sep == RuleStmt::SEP_EQ ||
- stmt->sep == RuleStmt::SEP_FINALEQ) {
- buf += '=';
- }
- if (stmt->rhs) {
- buf += stmt->rhs->Eval(this);
- }
- after_targets = buf;
- separator_pos = string::npos;
+ // We used to make this a warning and otherwise accept it, but Make 4.1
+ // calls this out as an error, so let's follow.
+ Error("*** empty variable name.");
}
Rule* rule = new Rule();
diff --git a/file_cache.h b/file_cache.h
index e344946..fac2077 100644
--- a/file_cache.h
+++ b/file_cache.h
@@ -16,7 +16,6 @@
#define FILE_CACHE_H_
#include <string>
-
#include <unordered_set>
using namespace std;
diff --git a/fileutil_bench.cc b/fileutil_bench.cc
index 367c125..429d004 100644
--- a/fileutil_bench.cc
+++ b/fileutil_bench.cc
@@ -15,7 +15,9 @@
// +build ignore
#include <benchmark/benchmark.h>
+
#include <cstdio>
+
#include "fileutil.h"
static void BM_RunCommand(benchmark::State& state) {
diff --git a/find_test.cc b/find_test.cc
index c9c86b2..e8d521c 100644
--- a/find_test.cc
+++ b/find_test.cc
@@ -18,6 +18,7 @@
#include <stdlib.h>
#include <unistd.h>
+
#include <string>
#include "fileutil.h"
diff --git a/flags.cc b/flags.cc
index bcc1b4f..329e0fd 100644
--- a/flags.cc
+++ b/flags.cc
@@ -108,6 +108,8 @@ void Flags::Parse(int argc, char** argv) {
no_ninja_prelude = true;
} else if (!strcmp(arg, "--use_ninja_phony_output")) {
use_ninja_phony_output = true;
+ } else if (!strcmp(arg, "--use_ninja_validations")) {
+ use_ninja_validations = true;
} else if (!strcmp(arg, "--werror_find_emulator")) {
werror_find_emulator = true;
} else if (!strcmp(arg, "--werror_overriding_commands")) {
diff --git a/flags.h b/flags.h
index 279e7fe..066fb38 100644
--- a/flags.h
+++ b/flags.h
@@ -44,6 +44,7 @@ struct Flags {
bool no_builtin_rules;
bool no_ninja_prelude;
bool use_ninja_phony_output;
+ bool use_ninja_validations;
bool werror_find_emulator;
bool werror_overriding_commands;
bool warn_implicit_rules;
diff --git a/ninja.cc b/ninja.cc
index f53b15b..9b6989b 100644
--- a/ninja.cc
+++ b/ninja.cc
@@ -255,6 +255,9 @@ class NinjaGenerator {
for (auto const& d : node->order_onlys) {
PopulateNinjaNode(d.second);
}
+ for (auto const& d : node->validations) {
+ PopulateNinjaNode(d.second);
+ }
}
StringPiece TranslateCommand(const char* in, string* cmd_buf) {
@@ -569,6 +572,13 @@ class NinjaGenerator {
*o << " " << EscapeBuildTarget(d.first).c_str();
}
}
+ if (!node->validations.empty()) {
+ *o << " |@";
+ for (auto const& d : node->validations) {
+ *o << " " << EscapeBuildTarget(d.first).c_str();
+ }
+ }
+
*o << "\n";
string pool;
diff --git a/ninja_test.cc b/ninja_test.cc
index 2a7fdc1..15b0693 100644
--- a/ninja_test.cc
+++ b/ninja_test.cc
@@ -14,10 +14,11 @@
// +build ignore
+#include "ninja.h"
+
#include <assert.h>
#include "log.h"
-#include "ninja.h"
#include "testutil.h"
namespace {
diff --git a/rule.h b/rule.h
index b8dc4a1..237eb02 100644
--- a/rule.h
+++ b/rule.h
@@ -51,6 +51,7 @@ class Rule {
vector<Symbol> inputs;
vector<Symbol> order_only_inputs;
vector<Symbol> output_patterns;
+ vector<Symbol> validations;
bool is_double_colon;
bool is_suffix_rule;
vector<Value*> cmds;
diff --git a/runtest.rb b/runtest.rb
index a3c4197..173c66d 100755
--- a/runtest.rb
+++ b/runtest.rb
@@ -26,21 +26,21 @@ while true
test_serialization = true
ARGV.shift
elsif ARGV[0] == '-c'
- ckati = true
+ $ckati = true
ARGV.shift
ENV['KATI_VARIANT'] = 'c'
elsif ARGV[0] == '-n'
- via_ninja = true
+ $via_ninja = true
ARGV.shift
ENV['NINJA_STATUS'] = 'NINJACMD: '
elsif ARGV[0] == '-a'
gen_all_targets = true
ARGV.shift
elsif ARGV[0] == '-v'
- show_failing = true
+ $show_failing = true
ARGV.shift
elsif ARGV[0] == "-q"
- hide_passing = true
+ $hide_passing = true
ARGV.shift
else
break
@@ -76,10 +76,10 @@ def move_circular_dep(l)
circ + l
end
-expected_failures = []
-unexpected_passes = []
-failures = []
-passes = []
+$expected_failures = []
+$unexpected_passes = []
+$failures = []
+$passes = []
if !ARGV.empty?
test_files = ARGV.map do |test|
@@ -187,35 +187,67 @@ end
bash_var = ' SHELL=/bin/bash'
-run_make_test = proc do |mk|
- c = File.read(mk)
+def is_expected_failure(f)
+ c = File.read(f)
expected_failure = false
- if c =~ /\A# TODO(?:\(([-a-z|]+)\))?/
- if $1
- todos = $1.split('|')
- if todos.include?('go') && !ckati
+ if c =~ /\A(#!\/bin\/bash\n)?# TODO(?:\(([-a-z|]+)\))?/
+ if $2
+ todos = $2.split('|')
+ if todos.include?('go') && !$ckati
expected_failure = true
end
- if todos.include?('c') && ckati
+ if todos.include?('c') && $ckati
expected_failure = true
end
- if todos.include?('go-ninja') && !ckati && via_ninja
+ if todos.include?('go-ninja') && !$ckati && $via_ninja
expected_failure = true
end
- if todos.include?('c-ninja') && ckati && via_ninja
+ if todos.include?('c-ninja') && $ckati && $via_ninja
expected_failure = true
end
- if todos.include?('c-exec') && ckati && !via_ninja
+ if todos.include?('c-exec') && $ckati && !$via_ninja
expected_failure = true
end
- if todos.include?('ninja') && via_ninja
+ if todos.include?('ninja') && $via_ninja
expected_failure = true
end
else
expected_failure = true
end
end
+ return expected_failure
+end
+
+report_result = proc do |name, expected, output, expected_failure|
+ if expected != output
+ if expected_failure
+ if !$hide_passing
+ puts "#{name}: FAIL (expected)"
+ end
+ $expected_failures << name
+ else
+ puts "#{name}: FAIL"
+ $failures << name
+ end
+ if !expected_failure || $show_failing
+ puts `diff -u out.make out.kati`
+ end
+ else
+ if expected_failure
+ puts "#{name}: PASS (unexpected)"
+ $unexpected_passes << name
+ else
+ if !$hide_passing
+ puts "#{name}: PASS"
+ end
+ $passes << name
+ end
+ end
+end
+run_make_test = proc do |mk|
+ expected_failure = is_expected_failure(mk)
+ c = File.read(mk)
run_in_testdir(mk) do |name|
File.open("Makefile", 'w') do |ofile|
ofile.print(c)
@@ -235,13 +267,13 @@ run_make_test = proc do |mk|
cleanup
testcases.each do |tc|
cmd = 'make'
- if via_ninja || is_silent_test
+ if $via_ninja || is_silent_test
cmd += ' -s'
end
cmd += bash_var
cmd += " #{tc} 2>&1"
res = IO.popen(cmd, 'r:binary', &:read)
- res = normalize_make_log(res, mk, via_ninja)
+ res = normalize_make_log(res, mk, $via_ninja)
expected += "=== #{tc} ===\n" + res
expected_files = get_output_filenames
expected += "\n=== FILES ===\n#{expected_files * "\n"}\n"
@@ -251,14 +283,14 @@ run_make_test = proc do |mk|
testcases.each do |tc|
json = "#{tc.empty? ? 'test' : tc}"
cmd = "../../kati -save_json=#{json}.json -log_dir=. --use_find_emulator"
- if ckati
+ if $ckati
cmd = "../../ckati --use_find_emulator"
end
- if via_ninja
+ if $via_ninja
cmd += ' --ninja'
end
if gen_all_targets
- if !ckati || !via_ninja
+ if !$ckati || !$via_ninja
raise "-a should be used with -c -n"
end
cmd += ' --gen_all_targets'
@@ -272,7 +304,7 @@ run_make_test = proc do |mk|
end
cmd += " 2>&1"
res = IO.popen(cmd, 'r:binary', &:read)
- if via_ninja && File.exist?('build.ninja') && File.exists?('ninja.sh')
+ if $via_ninja && File.exist?('build.ninja') && File.exists?('ninja.sh')
cmd = './ninja.sh -j1 -v'
if gen_all_targets
cmd += " #{tc}"
@@ -295,30 +327,7 @@ run_make_test = proc do |mk|
exit 1
end
- if expected != output
- if expected_failure
- if !hide_passing
- puts "#{name}: FAIL (expected)"
- end
- expected_failures << name
- else
- puts "#{name}: FAIL"
- failures << name
- end
- if !expected_failure || show_failing
- puts `diff -u out.make out.kati`
- end
- else
- if expected_failure
- puts "#{name}: PASS (unexpected)"
- unexpected_passes << name
- else
- if !hide_passing
- puts "#{name}: PASS"
- end
- passes << name
- end
- end
+ report_result.call(name, expected, output, expected_failure)
if name !~ /^err_/ && test_serialization && !expected_failure
testcases.each do |tc|
@@ -343,10 +352,12 @@ end
run_shell_test = proc do |sh|
is_ninja_test = sh =~ /\/ninja_/
- if is_ninja_test && (!ckati || !via_ninja)
+ if is_ninja_test && (!$ckati || !$via_ninja)
next
end
+ expected_failure = is_expected_failure(sh)
+
run_in_testdir(sh) do |name|
cleanup
cmd = "bash ../../#{sh} make"
@@ -358,13 +369,13 @@ run_shell_test = proc do |sh|
cleanup
if is_ninja_test
- if ckati
+ if $ckati
cmd = "bash ../../#{sh} ../../ckati --ninja --regen"
else
next
end
else
- if ckati
+ if $ckati
cmd = "bash ../../#{sh} ../../ckati"
else
cmd = "bash ../../#{sh} ../../kati --use_cache -log_dir=."
@@ -382,16 +393,7 @@ run_shell_test = proc do |sh|
File.open('out.make', 'w'){|ofile|ofile.print(expected)}
File.open('out.kati', 'w'){|ofile|ofile.print(output)}
- if expected != output
- puts "#{name}: FAIL"
- puts `diff -u out.make out.kati`
- failures << name
- else
- if !hide_passing
- puts "#{name}: PASS"
- end
- passes << name
- end
+ report_result.call(name, expected, output, expected_failure)
end
end
@@ -407,31 +409,31 @@ end
puts
-if !expected_failures.empty? && !hide_passing
+if !$expected_failures.empty? && !$hide_passing
puts "=== Expected failures ==="
- expected_failures.each do |n|
+ $expected_failures.each do |n|
puts n
end
end
-if !unexpected_passes.empty?
+if !$unexpected_passes.empty?
puts "=== Unexpected passes ==="
- unexpected_passes.each do |n|
+ $unexpected_passes.each do |n|
puts n
end
end
-if !failures.empty?
+if !$failures.empty?
puts "=== Failures ==="
- failures.each do |n|
+ $failures.each do |n|
puts n
end
end
puts
-if !unexpected_passes.empty? || !failures.empty?
- puts "FAIL! (#{failures.size + unexpected_passes.size} fails #{passes.size} passes)"
+if !$unexpected_passes.empty? || !$failures.empty?
+ puts "FAIL! (#{$failures.size + $unexpected_passes.size} fails #{$passes.size} passes)"
exit 1
else
puts 'PASS!'
diff --git a/string_piece.cc b/string_piece.cc
index e739219..32a7be0 100644
--- a/string_piece.cc
+++ b/string_piece.cc
@@ -19,6 +19,8 @@
// +build ignore
+#include "string_piece.h"
+
#include <ctype.h>
#include <limits.h>
#include <stdint.h>
@@ -26,8 +28,6 @@
#include <algorithm>
#include <ostream>
-#include "string_piece.h"
-
typedef StringPiece::size_type size_type;
bool operator==(const StringPiece& x, const StringPiece& y) {
diff --git a/testcase/empty_target_specific_var.mk b/testcase/empty_target_specific_var.mk
index fd9e6c1..5683eb2 100644
--- a/testcase/empty_target_specific_var.mk
+++ b/testcase/empty_target_specific_var.mk
@@ -4,4 +4,4 @@ test: =foo
var==foo
$(var):
- echo PASS
+ echo FAIL
diff --git a/testcase/empty_target_specific_var2.mk b/testcase/empty_target_specific_var2.mk
index 6defb52..960a615 100644
--- a/testcase/empty_target_specific_var2.mk
+++ b/testcase/empty_target_specific_var2.mk
@@ -8,4 +8,4 @@ $(call var)
eq_one:==1
$(eq_one):
- echo PASS
+ echo FAIL
diff --git a/testcase/multiline_arg.mk b/testcase/multiline_arg.mk
index 1e64318..0bfdf01 100644
--- a/testcase/multiline_arg.mk
+++ b/testcase/multiline_arg.mk
@@ -1,3 +1,5 @@
+# TODO(c-ninja): We're exporting `(echo )` for the last line, while make a kati(w/o ninja) uses `echo \`
+
SHELL:=/bin/bash
define func
diff --git a/testcase/ninja_regen_filefunc_read.sh b/testcase/ninja_regen_filefunc_read.sh
index 7fc3a9f..01fb7e6 100755
--- a/testcase/ninja_regen_filefunc_read.sh
+++ b/testcase/ninja_regen_filefunc_read.sh
@@ -26,7 +26,11 @@ sleep_if_necessary() {
}
cat <<EOF > Makefile
+# Make 4.1 does not support file reading, which was added in 4.2
+# We don't actually care though, since we're just testing kati's regen
+ifdef KATI
A := \$(file <file_a)
+endif
all:
echo foo
EOF
diff --git a/testcase/ninja_validations.sh b/testcase/ninja_validations.sh
new file mode 100644
index 0000000..cd77a01
--- /dev/null
+++ b/testcase/ninja_validations.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+# TODO(ninja): enable once upstream ninja supports validations
+#
+# Copyright 2015 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.
+
+set -e
+
+mk="$@"
+
+cat <<EOF >Makefile
+all: a c
+
+a:
+ echo a
+
+b:
+ echo b
+
+a: .KATI_VALIDATIONS := b
+
+c:
+ echo c
+
+d: c
+ echo d
+
+c: .KATI_VALIDATIONS := d
+EOF
+
+all="a b c d"
+if echo "${mk}" | grep kati > /dev/null; then
+ mk="${mk} --use_ninja_validations"
+ all="a c"
+fi
+${mk} -j1 $all
+if [ -e ninja.sh ]; then
+ ./ninja.sh -j1 -w dupbuild=err $all
+fi
+