Skip to content

Commit

Permalink
Add func KATI_visibility_prefix
Browse files Browse the repository at this point in the history
syntax: $(KATI_visibility_prefix var, prefix)

1, Add a func KATI_visibility_prefix that takes a variable
name and a list of strings, set this variable's visibility
to these strings. Each string represents the relative path
from the root, and is considered as prefix.

e.g. $(KATI_visibility_prefix VAR, vendor/ device/b baz.mk)
--> VAR is visible to "vendor/foo.mk", "device/bar.mk",
"device/baz.mk", "baz.mk", but not visible to "bar.mk",
"vendor.mk" or "vendor/baz.mk".

If variable visibility is set more than once, and with a
different list of strings, an error will occur.

2. When a variable is being referenced, if this variable has
visibility prefix set, check if the current referencing file
matches the visibility prefix. Throw an error if not.

3. In $(KATI_visibility_prefix FOO, prefix)
If FOO is not defined, create a variable FOO with empty value.
The prefix can also be reference to variable.
If so, this function will expand the reference and set the
visibility_prefix.
  • Loading branch information
mrziwang authored and Colecf committed Apr 30, 2024
1 parent 2c503b6 commit 8b60db2
Show file tree
Hide file tree
Showing 17 changed files with 224 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class SymRef : public Value {
Var* v = ev->LookupVarForEval(name_);
v->Used(ev, name_);
v->Eval(ev, s);
v->CheckCurrentReferencingFile(ev->loc(), name_.c_str());
ev->VarEvalComplete(name_);
}

Expand Down Expand Up @@ -176,6 +177,7 @@ class VarRef : public Value {
Var* v = ev->LookupVarForEval(sym);
v->Used(ev, sym);
v->Eval(ev, s);
v->CheckCurrentReferencingFile(ev->loc(), name.c_str());
ev->VarEvalComplete(sym);
}

Expand Down
2 changes: 1 addition & 1 deletion src/find.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#include <string_view>
#include <vector>

//#undef NOLOG
// #undef NOLOG

#include "fileutil.h"
#include "log.h"
Expand Down
57 changes: 56 additions & 1 deletion src/func.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <algorithm>
#include <iterator>
#include <memory>
#include <sstream>
#include <unordered_map>

#include "eval.h"
Expand Down Expand Up @@ -510,7 +511,7 @@ void EvalFunc(const std::vector<Value*>& args, Evaluator* ev, std::string*) {
}
}

//#define TEST_FIND_EMULATOR
// #define TEST_FIND_EMULATOR

// A hack for Android build. We need to evaluate things like $((3+4))
// when we emit ninja file, because the result of such expressions
Expand Down Expand Up @@ -656,6 +657,59 @@ void ShellFuncNoRerun(const std::vector<Value*>& args,
ShellStatusVar::SetValue(returnCode);
}

void VarVisibilityFunc(const std::vector<Value*>& args,
Evaluator* ev,
std::string* s) {
std::string arg = args[0]->Eval(ev);
std::vector<std::string> prefixes;

std::stringstream ss(args[1]->Eval(ev));
std::string prefix;
while (ss >> prefix) {
if (HasPrefix(prefix, "/")) {
ERROR_LOC(ev->loc(), "Visibility prefix should not start with /");
}
if (HasPrefix(prefix, "../")) {
ERROR_LOC(ev->loc(), "Visibility prefix should not start with ../");
}

std::string normalizedPrefix = prefix;
NormalizePath(&normalizedPrefix);
if (prefix != normalizedPrefix) {
ERROR_LOC(ev->loc(),
"Visibility prefix %s is not normalized. Normalized prefix: %s",
prefix.c_str(), normalizedPrefix.c_str());
}

// one visibility prefix cannot be the prefix of another visibility prefix
for (std::vector<std::string>::iterator it = prefixes.begin();
it != prefixes.end(); ++it) {
if (HasPathPrefix(*it, prefix)) {
ERROR_LOC(ev->loc(),
"Visibility prefix %s is the prefix of another visibility "
"prefix %s",
prefix.c_str(), it->c_str());
} else if (HasPathPrefix(prefix, *it)) {
ERROR_LOC(ev->loc(),
"Visibility prefix %s is the prefix of another visibility "
"prefix %s",
it->c_str(), prefix.c_str());
}
}

prefixes.push_back(prefix);
}

Symbol sym = Intern(arg);
Var* v = ev->PeekVar(sym);
// If variable is not defined, create an empty variable.
if (!v->IsDefined()) {
v = new SimpleVar(VarOrigin::FILE, ev->CurrentFrame(), ev->loc());
sym.SetGlobalVar(v, false, nullptr);
}
v->SetVisibilityPrefix(prefixes, sym.c_str());
}

void CallFunc(const std::vector<Value*>& args, Evaluator* ev, std::string* s) {
static const Symbol tmpvar_names[] = {
Intern("0"), Intern("1"), Intern("2"), Intern("3"), Intern("4"),
Expand Down Expand Up @@ -1149,6 +1203,7 @@ static const std::unordered_map<std::string_view, FuncInfo> g_func_info_map = {
ENTRY("KATI_shell_no_rerun", &ShellFuncNoRerun, 1, 1, false, false),
ENTRY("KATI_foreach_sep", &ForeachWithSepFunc, 4, 4, false, false),
ENTRY("KATI_file_no_rerun", &FileFuncNoRerun, 2, 1, false, false),
ENTRY("KATI_visibility_prefix", &VarVisibilityFunc, 2, 1, false, false),
};

} // namespace
Expand Down
5 changes: 5 additions & 0 deletions src/strutil.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ bool HasPrefix(std::string_view str, std::string_view prefix) {
return size_diff >= 0 && str.substr(0, prefix.size()) == prefix;
}

bool HasPathPrefix(std::string_view str, std::string_view prefix) {
return HasPrefix(str, prefix) &&
(str.size() == prefix.size() || str.at(prefix.size()) == '/');
}

bool HasSuffix(std::string_view str, std::string_view suffix) {
ssize_t size_diff = str.size() - suffix.size();
return size_diff >= 0 && str.substr(size_diff) == suffix;
Expand Down
5 changes: 5 additions & 0 deletions src/strutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ inline std::string JoinStrings(std::vector<String> v, const char* sep) {

bool HasPrefix(std::string_view str, std::string_view prefix);

// Checks path-like prefixes. str and prefix and both considered as strings
// that represent "path".
// e.g. str "foo/bar" has path prefix "foo" but doesn't have path prefix "fo".
bool HasPathPrefix(std::string_view str, std::string_view prefix);

bool HasSuffix(std::string_view str, std::string_view suffix);

bool HasWord(std::string_view str, std::string_view w);
Expand Down
2 changes: 1 addition & 1 deletion src/symtab.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

// +build ignore

//#define ENABLE_TID_CHECK
// #define ENABLE_TID_CHECK

#include "symtab.h"

Expand Down
35 changes: 35 additions & 0 deletions src/var.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,41 @@ void Var::Used(Evaluator* ev, const Symbol& sym) const {
}
}

void Var::SetVisibilityPrefix(const std::vector<std::string>& prefixes,
const char* name) {
const std::vector<std::string>& current_prefixes = VisibilityPrefix();
if (current_prefixes.size() == 0) {
visibility_prefix_ = prefixes;
} else if (current_prefixes != prefixes) {
ERROR("Visibility prefix conflict on variable: %s", name);
}
}

void Var::CheckCurrentReferencingFile(Loc loc, const char* name) {
const std::vector<std::string>& prefixes = VisibilityPrefix();
if (prefixes.size() == 0) {
return;
}
bool valid = false;
for (const std::string& prefix : prefixes) {
if (HasPathPrefix(loc.filename, prefix)) {
valid = true;
break;
}
}
if (!valid) {
std::string prefixesString;
for (const std::string& p : prefixes) {
prefixesString += (p + "\n");
}
prefixesString.pop_back();
ERROR(
"%s is not a valid file to reference variable %s. Line #%d.\nValid "
"file prefixes:\n%s",
loc.filename, name, loc.lineno, prefixesString.c_str());
}
}

const char* Var::diagnostic_message_text() const {
auto it = diagnostic_messages_.find(this);
return it == diagnostic_messages_.end() ? "" : it->second.c_str();
Expand Down
9 changes: 9 additions & 0 deletions src/var.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ class Var : public Evaluable {
bool SelfReferential() const { return self_referential_; }
void SetSelfReferential() { self_referential_ = true; }

const std::vector<std::string>& VisibilityPrefix() const {
return visibility_prefix_;
}
void SetVisibilityPrefix(const std::vector<std::string>& prefixes,
const char* name);
void CheckCurrentReferencingFile(Loc loc, const char* name);

const std::string& DeprecatedMessage() const;

// This variable was used (either written or read from)
Expand Down Expand Up @@ -101,6 +108,8 @@ class Var : public Evaluable {
const char* diagnostic_message_text() const;

static std::unordered_map<const Var*, std::string> diagnostic_messages_;

std::vector<std::string> visibility_prefix_;
};

class SimpleVar : public Var {
Expand Down
16 changes: 16 additions & 0 deletions testcase/var_visibility_prefix_conflict.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FOO := foo
BAR := bar
PREFIX := pone/ptwo

$(KATI_visibility_prefix FOO, pone/ptwo baz)
$(KATI_visibility_prefix FOO, $(PREFIX) baz)

$(KATI_visibility_prefix BAR, pone/ptwo baz)
$(KATI_visibility_prefix BAR, baz $(PREFIX))

ifndef KATI
$(info Visibility prefix conflict on variable: BAR)
endif

test:
@:
6 changes: 6 additions & 0 deletions testcase/var_visibility_prefix_implicit_define.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
$(KATI_visibility_prefix FOO, Makefile)

BAR := $(FOO)

test:
echo '$(BAR)'
16 changes: 16 additions & 0 deletions testcase/var_visibility_prefix_invalid_file_four.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
$(KATI_visibility_prefix BAR, bar)
FOO = $(BAR) # this should be okay, since it's not evaluated
BAZ := $(FOO)

define ERROR_MSG
Makefile is not a valid file to reference variable BAR. Line #3.
Valid file prefixes:
bar
endef

ifndef KATI
$(info $(ERROR_MSG))
endif

test:
@:
23 changes: 23 additions & 0 deletions testcase/var_visibility_prefix_invalid_file_one.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FOO := foo
BAR := bar
$(KATI_visibility_prefix FOO, Makefile)
$(KATI_visibility_prefix BAR, )
$(KATI_visibility_prefix BAZ, baz)

VAR0 := $(FOO)
VAR1 := $(BAR)
VAR2 := $$(BAZ)
VAR3 := $($(BAZ))

define ERROR_MSG
Makefile is not a valid file to reference variable BAZ. Line #10.
Valid file prefixes:
baz
endef

ifndef KATI
$(info $(ERROR_MSG))
endif

test:
@:
16 changes: 16 additions & 0 deletions testcase/var_visibility_prefix_invalid_file_two.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
$(KATI_visibility_prefix BAR, bar)
FOO := BAR
BAZ := $($(FOO))

define ERROR_MSG
Makefile is not a valid file to reference variable BAR. Line #3.
Valid file prefixes:
bar
endef

ifndef KATI
$(info $(ERROR_MSG))
endif

test:
@:
8 changes: 8 additions & 0 deletions testcase/var_visibility_prefix_invalid_prefix_four.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
$(KATI_visibility_prefix FOO, foo/)

ifndef KATI
$(info Makefile:1: Visibility prefix foo/ is not normalized. Normalized prefix: foo)
endif

test:
@:
8 changes: 8 additions & 0 deletions testcase/var_visibility_prefix_invalid_prefix_one.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
$(KATI_visibility_prefix FOO, /foo)

ifndef KATI
$(info Makefile:1: Visibility prefix should not start with /)
endif

test:
@:
9 changes: 9 additions & 0 deletions testcase/var_visibility_prefix_invalid_prefix_three.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
$(KATI_visibility_prefix FOO, foo fo) # no error, different path prefixes
$(KATI_visibility_prefix Bar, bar/baz bar)

ifndef KATI
$(info Makefile:2: Visibility prefix bar is the prefix of another visibility prefix bar/baz)
endif

test:
@:
8 changes: 8 additions & 0 deletions testcase/var_visibility_prefix_invalid_prefix_two.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
$(KATI_visibility_prefix FOO, foo ../foo)

ifndef KATI
$(info Makefile:1: Visibility prefix should not start with ../)
endif

test:
@:

0 comments on commit 8b60db2

Please sign in to comment.