diff options
| author | Shinichiro Hamaji <shinichiro.hamaji@gmail.com> | 2016-07-12 00:12:28 +0000 |
|---|---|---|
| committer | android-build-merger <android-build-merger@google.com> | 2016-07-12 00:12:28 +0000 |
| commit | e1de29e5429454e581fb7ff5ae224812cd465739 (patch) | |
| tree | d371f6416e5e900565699a4d3bc4daea641cc27a | |
| parent | 32b10dc02806b70ffdad7a6e3c5b767c5a6de507 (diff) | |
| parent | fa3234cabaa3a7350cebc04d9b5948d7d1d1dcfa (diff) | |
Merge remote-tracking branch \'aosp/upstream\'
am: fa3234caba
Change-Id: I1eb4edbfe2f5d4fbfe07e1eb15884f0df2e3b0b9
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | .travis.yml | 5 | ||||
| -rw-r--r-- | AUTHORS | 3 | ||||
| -rw-r--r-- | CONTRIBUTORS | 1 | ||||
| -rw-r--r-- | eval.cc | 8 | ||||
| -rw-r--r-- | expr.cc | 2 | ||||
| -rw-r--r-- | flags.cc | 8 | ||||
| -rw-r--r-- | func.cc | 11 | ||||
| -rw-r--r-- | rule.cc | 27 | ||||
| -rw-r--r-- | rule.h | 8 | ||||
| -rw-r--r-- | strutil.cc | 95 | ||||
| -rw-r--r-- | strutil_test.cc | 48 | ||||
| -rw-r--r-- | symtab.cc | 4 | ||||
| -rwxr-xr-x | testcase/cmdline_var.sh | 27 | ||||
| -rwxr-xr-x | testcase/cmdline_var_makeflags.sh | 36 | ||||
| -rwxr-xr-x | testcase/cmdline_var_modify.sh | 27 | ||||
| -rwxr-xr-x | testcase/cmdline_var_override.sh | 27 | ||||
| -rw-r--r-- | testcase/crlf.mk | 1 | ||||
| -rw-r--r-- | testcase/empty_target_specific_var.mk | 7 | ||||
| -rw-r--r-- | testcase/empty_target_specific_var2.mk | 11 | ||||
| -rw-r--r-- | testcase/eval_starts_with_comment.mk | 9 | ||||
| -rw-r--r-- | testcase/ifdef_rec_var.mk | 8 | ||||
| -rw-r--r-- | testcase/join.mk | 3 |
23 files changed, 297 insertions, 81 deletions
@@ -11,8 +11,10 @@ repo/maloader/ testcase_parse_benchmark_test.go bench-old.out bench-new.out +find_test ninja_test string_piece_test +strutil_bench strutil_test go_src_stamp version.cc diff --git a/.travis.yml b/.travis.yml index f045dd3..2fd6fa8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,10 @@ before_script: - sudo apt-get install -y libstdc++-4.8-dev clang-3.5 ninja-build realpath script: - - make -j4 ckati + - make -j4 ckati ckati_tests - ruby runtest.rb -c - ruby runtest.rb -c -n - ruby runtest.rb -c -n -a + - ./ninja_test + - ./string_piece_test + - ./strutil_test @@ -8,5 +8,6 @@ # # Please keep the list sorted. -Kouhei Sutou <kou@cozmixng.org> Google Inc. +Kouhei Sutou <kou@cozmixng.org> +Po Hu <hupo1985@gmail.com> diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 1d81fb2..c153787 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -26,6 +26,7 @@ Colin Cross <ccross@google.com> Dan Willemsen <dwillemsen@google.com> Fumitoshi Ukai <ukai@google.com> Kouhei Sutou <kou@cozmixng.org> +Po Hu <hupo1985@gmail.com> Ryo Hashimoto <hashimoto@google.com> Shinichiro Hamaji <hamaji@google.com> Stefan Becker <stefanb@gpartner-nvidia.com> @@ -122,7 +122,10 @@ void Evaluator::EvalRule(const RuleStmt* stmt) { Rule* rule; RuleVarAssignment rule_var; - ParseRule(loc_, expr, stmt->term, &rule, &rule_var); + function<string()> after_term_fn = [this, stmt](){ + return stmt->after_term ? stmt->after_term->Eval(this) : ""; + }; + ParseRule(loc_, expr, stmt->term, after_term_fn, &rule, &rule_var); if (rule) { if (stmt->term == ';') { @@ -201,8 +204,7 @@ void Evaluator::EvalIf(const IfStmt* stmt) { if (lhs.str().find_first_of(" \t") != string::npos) Error("*** invalid syntax in conditional."); Var* v = LookupVarInCurrentScope(lhs); - const string&& s = v->Eval(this); - is_true = (s.empty() == (stmt->op == CondOp::IFNDEF)); + is_true = (v->String().empty() == (stmt->op == CondOp::IFNDEF)); break; } case CondOp::IFEQ: @@ -537,6 +537,8 @@ Value* ParseExprImpl(const Loc& loc, r->AddValue(new Literal(StringPiece(" "))); // Skip the current escaped newline i += 2; + if (n == '\r' && s.get(i) == '\n') + i++; // Then continue skipping escaped newlines, spaces, and tabs for (; i < s.size(); i++) { if (s[i] == '\\' && (s.get(i+1) == '\r' || s.get(i+1) == '\n')) { @@ -16,6 +16,7 @@ #include "flags.h" +#include <stdlib.h> #include <unistd.h> #include "log.h" @@ -52,6 +53,13 @@ void Flags::Parse(int argc, char** argv) { num_jobs = num_cpus = sysconf(_SC_NPROCESSORS_ONLN); const char* num_jobs_str; + if (const char* makeflags = getenv("MAKEFLAGS")) { + for (StringPiece tok : WordScanner(makeflags)) { + if (!HasPrefix(tok, "-") && tok.find('=') != string::npos) + cl_vars.push_back(tok); + } + } + for (int i = 1; i < argc; i++) { const char* arg = argv[i]; bool should_propagate = true; @@ -299,13 +299,18 @@ void JoinFunc(const vector<Value*>& args, Evaluator* ev, string* s) { WordScanner ws1(list1); WordScanner ws2(list2); WordWriter ww(s); - for (WordScanner::Iterator iter1 = ws1.begin(), iter2 = ws2.begin(); + WordScanner::Iterator iter1, iter2; + for (iter1 = ws1.begin(), iter2 = ws2.begin(); iter1 != ws1.end() && iter2 != ws2.end(); ++iter1, ++iter2) { ww.Write(*iter1); // Use |AppendString| not to append extra ' '. AppendString(*iter2, s); } + for (; iter1 != ws1.end(); ++iter1) + ww.Write(*iter1); + for (; iter2 != ws2.end(); ++iter2) + ww.Write(*iter2); } void WildcardFunc(const vector<Value*>& args, Evaluator* ev, string* s) { @@ -458,10 +463,6 @@ void EvalFunc(const vector<Value*>& args, Evaluator* ev, string*) { //const string text = args[0]->Eval(ev); string* text = new string; args[0]->Eval(ev, text); - if ((*text)[0] == '#') { - delete text; - return; - } if (ev->avoid_io()) { KATI_WARN("%s:%d: *warning*: $(eval) in a recipe is not recommended: %s", LOCF(ev->loc()), text->c_str()); @@ -54,6 +54,7 @@ Rule::Rule() } void ParseRule(Loc& loc, StringPiece line, char term, + function<string()> after_term_fn, Rule** out_rule, RuleVarAssignment* rule_var) { size_t index = line.find(':'); if (index == string::npos) { @@ -84,15 +85,31 @@ void ParseRule(Loc& loc, StringPiece line, char term, StringPiece rest = line.substr(index); size_t term_index = rest.find_first_of("=;"); + string buf; if ((term_index != string::npos && rest[term_index] == '=') || (term_index == string::npos && term == '=')) { if (term_index == string::npos) term_index = rest.size(); - rule_var->outputs.swap(outputs); - ParseAssignStatement(rest, term_index, - &rule_var->lhs, &rule_var->rhs, &rule_var->op); - *out_rule = NULL; - return; + // "test: =foo" is questionable but a valid rule definition (not a + // target specific variable). + // See https://github.com/google/kati/issues/83 + if (term_index == 0) { + KATI_WARN("%s:%d: defining a target which starts with `=', " + "which is not probably what you meant", LOCF(loc)); + buf = line.as_string(); + if (term) + buf += term; + buf += after_term_fn(); + line = buf; + rest = line.substr(index); + term_index = string::npos; + } else { + rule_var->outputs.swap(outputs); + ParseAssignStatement(rest, term_index, + &rule_var->lhs, &rule_var->rhs, &rule_var->op); + *out_rule = NULL; + return; + } } Rule* rule = new Rule(); @@ -15,6 +15,8 @@ #ifndef RULE_H_ #define RULE_H_ +#include <functional> +#include <string> #include <vector> #include "loc.h" @@ -58,8 +60,12 @@ struct RuleVarAssignment { AssignOp op; }; -// If |rule| is not NULL, |rule_var| is filled. +// If |rule| is not NULL, |rule_var| is filled. If the expression +// after the terminator |term| is needed (this happens only when +// |term| is '='), |after_term_fn| will be called to obtain the right +// hand side. void ParseRule(Loc& loc, StringPiece line, char term, + function<string()> after_term_fn, Rule** rule, RuleVarAssignment* rule_var); #endif // RULE_H_ @@ -21,6 +21,7 @@ #include <unistd.h> #include <algorithm> +#include <functional> #include <stack> #include <utility> @@ -38,8 +39,9 @@ static bool isSpace(char c) { static int SkipUntilSSE42(const char* s, int len, const char* ranges, int ranges_size) { __m128i ranges16 = _mm_loadu_si128((const __m128i*)ranges); + len &= ~15; int i = 0; - do { + while (i < len) { __m128i b16 = _mm_loadu_si128((const __m128i*)(s + i)); int r = _mm_cmpestri( ranges16, ranges_size, b16, len - i, @@ -48,11 +50,26 @@ static int SkipUntilSSE42(const char* s, int len, return i + r; } i += 16; - } while (i < len); + } return len; } #endif +template <typename Cond> +static int SkipUntil(const char* s, int len, + const char* ranges, int ranges_size, + Cond cond) { + int i = 0; +#ifdef __SSE4_2__ + i += SkipUntilSSE42(s, len, ranges, ranges_size); +#endif + for (; i < len; i++) { + if (cond(s[i])) + break; + } + return i; +} + WordScanner::Iterator& WordScanner::Iterator::operator++() { int len = static_cast<int>(in->size()); for (s = i + 1; s < len; s++) { @@ -66,17 +83,11 @@ WordScanner::Iterator& WordScanner::Iterator::operator++() { return *this; } -#ifdef __SSE4_2__ static const char ranges[] = "\x09\x0d "; - i = s; - i += SkipUntilSSE42(in->data() + s, len - s, ranges, 4); -#else - for (i = s; i < len; i++) { - if (isSpace((*in)[i])) - break; - } -#endif - + // It's intentional we are not using isSpace here. It seems with + // lambda the compiler generates better code. + i = s + SkipUntil(in->data() + s, len - s, ranges, 4, + [](char c) { return (9 <= c && c <= 13) || c == 32; }); return *this; } @@ -423,10 +434,10 @@ size_t FindThreeOutsideParen(StringPiece s, char c1, char c2, char c3) { } size_t FindEndOfLine(StringPiece s, size_t e, size_t* lf_cnt) { -#ifdef __SSE4_2__ static const char ranges[] = "\0\0\n\n\\\\"; while (e < s.size()) { - e += SkipUntilSSE42(s.data() + e, s.size() - e, ranges, 6); + e += SkipUntil(s.data() + e, s.size() - e, ranges, 6, + [](char c) { return c == 0 || c == '\n' || c == '\\'; }); if (e >= s.size()) { CHECK(s.size() == e); break; @@ -452,24 +463,6 @@ size_t FindEndOfLine(StringPiece s, size_t e, size_t* lf_cnt) { } } return e; -#else - bool prev_backslash = false; - for (; e < s.size(); e++) { - char c = s[e]; - if (c == '\\') { - prev_backslash = !prev_backslash; - } else if (c == '\n') { - ++*lf_cnt; - if (!prev_backslash) { - return e; - } - prev_backslash = false; - } else if (c != '\r') { - prev_backslash = false; - } - } - return e; -#endif } StringPiece TrimLeadingCurdir(StringPiece s) { @@ -528,11 +521,14 @@ string EchoEscape(const string str) { return buf; } +static bool NeedsShellEscape(char c) { + return c == 0 || c == '"' || c == '$' || c == '\\' || c == '`'; +} + void EscapeShell(string* s) { -#ifdef __SSE4_2__ static const char ranges[] = "\0\0\"\"$$\\\\``"; size_t prev = 0; - size_t i = SkipUntilSSE42(s->c_str(), s->size(), ranges, 10); + size_t i = SkipUntil(s->c_str(), s->size(), ranges, 10, NeedsShellEscape); if (i == s->size()) return; @@ -550,37 +546,8 @@ void EscapeShell(string* s) { r += c; i++; prev = i; - i += SkipUntilSSE42(s->c_str() + i, s->size() - i, ranges, 10); + i += SkipUntil(s->c_str() + i, s->size() - i, ranges, 10, NeedsShellEscape); } StringPiece(*s).substr(prev).AppendToString(&r); s->swap(r); -#else - if (s->find_first_of("$`\\\"") == string::npos) - return; - string r; - bool last_dollar = false; - for (char c : *s) { - switch (c) { - case '$': - if (last_dollar) { - r += c; - last_dollar = false; - } else { - r += '\\'; - r += c; - last_dollar = true; - } - break; - case '`': - case '"': - case '\\': - r += '\\'; - // fall through. - default: - r += c; - last_dollar = false; - } - } - s->swap(r); -#endif } diff --git a/strutil_test.cc b/strutil_test.cc index a89786f..7a5a47d 100644 --- a/strutil_test.cc +++ b/strutil_test.cc @@ -17,6 +17,8 @@ #include "strutil.h" #include <assert.h> +#include <sys/mman.h> +#include <unistd.h> #include <string> #include <vector> @@ -138,6 +140,50 @@ void TestFindEndOfLine() { ASSERT_EQ(FindEndOfLine(StringPiece(buf, 2), 0, &lf_cnt), 2); } +// Take a string, and copy it into an allocated buffer where +// the byte immediately after the null termination character +// is read protected. Useful for testing, but doesn't support +// freeing the allocated pages. +const char* CreateProtectedString(const char* str) { + int pagesize = sysconf(_SC_PAGE_SIZE); + void *buffer; + char *buffer_str; + + // Allocate two pages of memory + if (posix_memalign(&buffer, pagesize, pagesize * 2) != 0) { + perror("posix_memalign failed"); + assert(false); + } + + // Make the second page unreadable + buffer_str = (char*)buffer + pagesize; + if (mprotect(buffer_str, pagesize, PROT_NONE) != 0) { + perror("mprotect failed"); + assert(false); + } + + // Then move the test string into the very end of the first page + buffer_str -= strlen(str) + 1; + strcpy(buffer_str, str); + + return buffer_str; +} + +void TestWordScannerInvalidAccess() { + vector<StringPiece> ss; + for (StringPiece tok : WordScanner(CreateProtectedString("0123 456789"))) { + ss.push_back(tok); + } + assert(ss.size() == 2LU); + ASSERT_EQ(ss[0], "0123"); + ASSERT_EQ(ss[1], "456789"); +} + +void TestFindEndOfLineInvalidAccess() { + size_t lf_cnt = 0; + ASSERT_EQ(FindEndOfLine(CreateProtectedString("a\\"), 0, &lf_cnt), 2); +} + } // namespace int main() { @@ -150,5 +196,7 @@ int main() { TestNormalizePath(); TestEscapeShell(); TestFindEndOfLine(); + TestWordScannerInvalidAccess(); + TestFindEndOfLineInvalidAccess(); assert(!g_failed); } @@ -69,6 +69,10 @@ void Symbol::SetGlobalVar(Var* v, bool is_override) const { orig->Origin() == VarOrigin::ENVIRONMENT_OVERRIDE)) { return; } + if (orig->Origin() == VarOrigin::COMMAND_LINE && + v->Origin() == VarOrigin::FILE) { + return; + } if (orig->Origin() == VarOrigin::AUTOMATIC) { ERROR("overriding automatic variable is not implemented yet"); } diff --git a/testcase/cmdline_var.sh b/testcase/cmdline_var.sh new file mode 100755 index 0000000..b892736 --- /dev/null +++ b/testcase/cmdline_var.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Copyright 2016 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 +CLVAR := FAIL +all: + @echo \$(CLVAR) +EOF + +${mk} CLVAR:=PASS 2> /dev/null diff --git a/testcase/cmdline_var_makeflags.sh b/testcase/cmdline_var_makeflags.sh new file mode 100755 index 0000000..6e38a2d --- /dev/null +++ b/testcase/cmdline_var_makeflags.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# +# Copyright 2016 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 +CLVAR := FAIL +MFVAR := FAIL +FILEVAR := PASS +all: + @echo \$(ENVVAR) \$(origin ENVVAR) + @echo \$(MFVAR) \$(origin MFVAR) + @echo \$(CLVAR) \$(origin CLVAR) + @echo \$(FILEVAR) \$(origin FILEVAR) +EOF + +export ENVVAR=PASS +export FILEVAR=FAIL +export MAKEFLAGS="MFVAR=PASS CLVAR=FAIL" + +${mk} CLVAR=PASS 2> /dev/null diff --git a/testcase/cmdline_var_modify.sh b/testcase/cmdline_var_modify.sh new file mode 100755 index 0000000..9616a2b --- /dev/null +++ b/testcase/cmdline_var_modify.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Copyright 2016 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 +CLVAR := FAIL +all: + @echo \$(CLVAR) +EOF + +${mk} CLVAR:=P CLVAR+=A CLVAR+=SS CLVAR?=FAIL 2> /dev/null diff --git a/testcase/cmdline_var_override.sh b/testcase/cmdline_var_override.sh new file mode 100755 index 0000000..0a5b3c1 --- /dev/null +++ b/testcase/cmdline_var_override.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Copyright 2016 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 +override CLVAR := PASS +all: + @echo \$(CLVAR) +EOF + +${mk} CLVAR:=FAIL 2> /dev/null diff --git a/testcase/crlf.mk b/testcase/crlf.mk index bcfc169..2dcfcd7 100644 --- a/testcase/crlf.mk +++ b/testcase/crlf.mk @@ -1,4 +1,5 @@ PASS := \
+ PASS \
PASS
test:
diff --git a/testcase/empty_target_specific_var.mk b/testcase/empty_target_specific_var.mk new file mode 100644 index 0000000..fd9e6c1 --- /dev/null +++ b/testcase/empty_target_specific_var.mk @@ -0,0 +1,7 @@ +# TODO(go): https://github.com/google/kati/issues/83 + +test: =foo + +var==foo +$(var): + echo PASS diff --git a/testcase/empty_target_specific_var2.mk b/testcase/empty_target_specific_var2.mk new file mode 100644 index 0000000..6defb52 --- /dev/null +++ b/testcase/empty_target_specific_var2.mk @@ -0,0 +1,11 @@ +# TODO(go): https://github.com/google/kati/issues/83 + +define var +VAR:=1 +endef + +$(call var) + +eq_one:==1 +$(eq_one): + echo PASS diff --git a/testcase/eval_starts_with_comment.mk b/testcase/eval_starts_with_comment.mk new file mode 100644 index 0000000..c3adca4 --- /dev/null +++ b/testcase/eval_starts_with_comment.mk @@ -0,0 +1,9 @@ +.PHONY: test + +define _rule +# comment +test: + echo PASS +endef + +$(eval $(_rule)) diff --git a/testcase/ifdef_rec_var.mk b/testcase/ifdef_rec_var.mk new file mode 100644 index 0000000..0874d2e --- /dev/null +++ b/testcase/ifdef_rec_var.mk @@ -0,0 +1,8 @@ +empty=$(info FAIL) +rec=$(empty) + +ifdef rec +$(info PASS) +else +$(info FAIL) +endif diff --git a/testcase/join.mk b/testcase/join.mk index cafe395..2772740 100644 --- a/testcase/join.mk +++ b/testcase/join.mk @@ -3,4 +3,5 @@ foo:=$(join a b,.c .o) # produces `a.c b.o'. test: echo $(foo) - + echo $(join a b c, 0 1) + echo $(join a b, 0 1 2) |
