diff options
| author | Colin Cross <ccross@android.com> | 2020-06-25 12:56:43 -0700 |
|---|---|---|
| committer | Colin Cross <ccross@android.com> | 2020-06-25 12:56:43 -0700 |
| commit | e184ff147273cfd25d37692dba4be72eca571e9f (patch) | |
| tree | a26d7e9d90ee0b905988c1d79f24b6ab11616c5c | |
| parent | 1decd163d97a3f8b7ab88cbcff501ed1de9615af (diff) | |
| parent | 003cf51e9b6da48063c90cf4c6710fde103c9c4a (diff) | |
| download | platform_build_kati-e184ff147273cfd25d37692dba4be72eca571e9f.tar.gz platform_build_kati-e184ff147273cfd25d37692dba4be72eca571e9f.tar.bz2 platform_build_kati-e184ff147273cfd25d37692dba4be72eca571e9f.zip | |
Merge remote-tracking branch 'aosp/upstream'
* aosp/upstream:
Support validations in ninja
Support expected failures in sh tests
Disable multiline_arg.mk for ninja tests
Fix regen_filefunc_read testcase under Make 4.1
Error out on empty variable names
Reformat with clang-format-9
Use github actions instead of travis
Change-Id: I3aecc20d1c441656920a7cda9863d9fddaa6f145
| -rw-r--r-- | .github/workflows/cpp-ci.yml | 44 | ||||
| -rw-r--r-- | .travis.yml | 36 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | affinity.cc | 4 | ||||
| -rwxr-xr-x | clang-format-check | 3 | ||||
| -rw-r--r-- | dep.cc | 47 | ||||
| -rw-r--r-- | dep.h | 2 | ||||
| -rw-r--r-- | eval.cc | 22 | ||||
| -rw-r--r-- | file_cache.h | 1 | ||||
| -rw-r--r-- | fileutil_bench.cc | 2 | ||||
| -rw-r--r-- | find_test.cc | 1 | ||||
| -rw-r--r-- | flags.cc | 2 | ||||
| -rw-r--r-- | flags.h | 1 | ||||
| -rw-r--r-- | ninja.cc | 10 | ||||
| -rw-r--r-- | ninja_test.cc | 3 | ||||
| -rw-r--r-- | rule.h | 1 | ||||
| -rwxr-xr-x | runtest.rb | 142 | ||||
| -rw-r--r-- | string_piece.cc | 4 | ||||
| -rw-r--r-- | testcase/empty_target_specific_var.mk | 2 | ||||
| -rw-r--r-- | testcase/empty_target_specific_var2.mk | 2 | ||||
| -rw-r--r-- | testcase/multiline_arg.mk | 2 | ||||
| -rwxr-xr-x | testcase/ninja_regen_filefunc_read.sh | 4 | ||||
| -rw-r--r-- | testcase/ninja_validations.sh | 51 |
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 @@ -1,7 +1,7 @@ kati ==== -[](http://travis-ci.org/google/kati) +[](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 @@ -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, @@ -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; @@ -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" @@ -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")) { @@ -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; @@ -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 { @@ -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; @@ -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 + |
