Snap for 5374457 from 729bc5978e09f803c12e26e377f18bd022166cc7 to qt-release
Change-Id: I6ca7d6797d846942022f56c59e0c439047898932
diff --git a/include/linkerconfig/configwriter.h b/include/linkerconfig/configwriter.h
new file mode 100644
index 0000000..9bc0f5f
--- /dev/null
+++ b/include/linkerconfig/configwriter.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+#pragma once
+
+#include <sstream>
+#include <string>
+
+namespace android {
+namespace linkerconfig {
+namespace modules {
+
+class ConfigWriter {
+ public:
+ void SetPrefix(const std::string& prefix);
+ void ResetPrefix();
+ void WriteLine(const std::string& line);
+ void WriteLine(const char* format, ...);
+ std::string ToString();
+
+ private:
+ std::stringstream content_;
+ std::string prefix_;
+
+ std::string ResolveVariables(const std::string& str);
+};
+
+} // namespace modules
+} // namespace linkerconfig
+} // namespace android
\ No newline at end of file
diff --git a/include/linkerconfig/link.h b/include/linkerconfig/link.h
index fada5b3..ed8d8a8 100644
--- a/include/linkerconfig/link.h
+++ b/include/linkerconfig/link.h
@@ -19,7 +19,8 @@
#include <utility>
#include <vector>
-#include <android-base/logging.h>
+#include "linkerconfig/configwriter.h"
+#include "linkerconfig/log.h"
#define LOG_TAG "linkerconfig"
@@ -36,7 +37,7 @@
}
template <typename T, typename... Args>
void AddSharedLib(T&& lib_name, Args&&... lib_names);
- std::string GenerateConfig();
+ void WriteConfig(ConfigWriter& writer);
private:
const std::string origin_namespace_;
diff --git a/include/linkerconfig/log.h b/include/linkerconfig/log.h
new file mode 100644
index 0000000..ccc8bbd
--- /dev/null
+++ b/include/linkerconfig/log.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+#pragma once
+
+#include <android-base/logging.h>
+
+#define LOG_TAG "linkerconfig"
diff --git a/include/linkerconfig/namespace.h b/include/linkerconfig/namespace.h
index 0ad4b9a..3e8c1bf 100644
--- a/include/linkerconfig/namespace.h
+++ b/include/linkerconfig/namespace.h
@@ -19,7 +19,8 @@
#include <string>
#include <vector>
-#include "link.h"
+#include "linkerconfig/configwriter.h"
+#include "linkerconfig/link.h"
namespace android {
namespace linkerconfig {
@@ -71,7 +72,7 @@
bool with_data_asan = true);
std::shared_ptr<Link> CreateLink(const std::string& target_namespace,
bool allow_all_shared_libs = false);
- std::string GenerateConfig();
+ void WriteConfig(ConfigWriter& writer);
private:
const bool is_isolated_;
@@ -82,8 +83,8 @@
std::vector<std::string> asan_search_paths_;
std::vector<std::string> asan_permitted_paths_;
std::map<std::string, std::shared_ptr<Link>> links_;
- std::string GetPathString(const std::string& path_type,
- const std::vector<std::string>& path_list);
+ void WritePathString(ConfigWriter& writer, const std::string& path_type,
+ const std::vector<std::string>& path_list);
};
} // namespace modules
} // namespace linkerconfig
diff --git a/include/linkerconfig/section.h b/include/linkerconfig/section.h
index d5e37c6..b5ac994 100644
--- a/include/linkerconfig/section.h
+++ b/include/linkerconfig/section.h
@@ -21,7 +21,8 @@
#include <utility>
#include <vector>
-#include "namespace.h"
+#include "linkerconfig/configwriter.h"
+#include "linkerconfig/namespace.h"
namespace android {
namespace linkerconfig {
@@ -35,8 +36,8 @@
std::shared_ptr<Namespace> CreateNamespace(const std::string& namespace_name,
bool is_isolated = false,
bool is_visible = false);
- std::string GenerateConfig();
- std::string GenerateBinaryPaths();
+ void WriteConfig(ConfigWriter& writer);
+ void WriteBinaryPaths(ConfigWriter& writer);
std::string GetName();
private:
diff --git a/include/linkerconfig/variables.h b/include/linkerconfig/variables.h
new file mode 100644
index 0000000..7c2c87f
--- /dev/null
+++ b/include/linkerconfig/variables.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+#pragma once
+
+#include <map>
+#include <optional>
+#include <string>
+
+namespace android {
+namespace linkerconfig {
+namespace modules {
+class Variables {
+ public:
+ static std::optional<std::string> GetValue(const std::string& key);
+ static void AddValue(const std::string& key, const std::string& value);
+
+ private:
+ static std::map<std::string, std::string> variables_;
+};
+} // namespace modules
+} // namespace linkerconfig
+} // namespace android
\ No newline at end of file
diff --git a/modules/configwriter.cc b/modules/configwriter.cc
new file mode 100644
index 0000000..aed8abf
--- /dev/null
+++ b/modules/configwriter.cc
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "linkerconfig/configwriter.h"
+
+#include <cstdarg>
+#include <cstdio>
+#include <iostream>
+#include <memory>
+#include <regex>
+
+#include "linkerconfig/log.h"
+#include "linkerconfig/variables.h"
+
+constexpr const char* kVariableRegex =
+ "@\\{([^@\\{\\}:]+)(:([^@\\{\\}:]*))?\\}";
+
+namespace android {
+namespace linkerconfig {
+namespace modules {
+
+void ConfigWriter::SetPrefix(const std::string& prefix) {
+ prefix_ = prefix;
+}
+
+void ConfigWriter::ResetPrefix() {
+ prefix_ = "";
+}
+
+void ConfigWriter::WriteLine(const char* format, ...) {
+ va_list args_for_length, args;
+
+ va_start(args, format);
+ va_copy(args_for_length, args);
+
+ int length = vsnprintf(nullptr, 0, format, args_for_length);
+ va_end(args_for_length);
+
+ if (length < 0) {
+ LOG(ERROR) << "Failed to get length of the string with format " << format;
+ va_end(args);
+ return;
+ }
+
+ std::unique_ptr<char[]> formatted_string(new char[length + 1]);
+
+ int res = vsnprintf(formatted_string.get(), length + 1, format, args);
+ va_end(args);
+
+ if (res < 0) {
+ LOG(ERROR) << "Failed to write a string with format " << format;
+ return;
+ }
+
+ WriteLine(std::string(formatted_string.get()));
+}
+
+void ConfigWriter::WriteLine(const std::string& line) {
+ auto resolved_line = ResolveVariables(prefix_ + line);
+ content_ << resolved_line << std::endl;
+}
+
+std::string ConfigWriter::ToString() {
+ return content_.str();
+}
+
+std::string ConfigWriter::ResolveVariables(const std::string& str) {
+ std::string result = str;
+ std::regex variable_regex(kVariableRegex);
+ std::smatch sm;
+
+ while (std::regex_search(result, sm, variable_regex)) {
+ std::stringstream ss;
+ ss << sm.prefix();
+ auto resolved_value = Variables::GetValue(sm[1]);
+ if (resolved_value.has_value()) {
+ ss << resolved_value.value();
+ } else {
+ LOG(WARNING) << "Unable to find value for " << sm[1];
+ bool contains_default = sm[2].length() > 0;
+ if (contains_default) {
+ ss << sm[3];
+ } else {
+ LOG(FATAL) << "There is no default value defined for " << sm[1];
+ }
+ }
+ ss << ResolveVariables(sm.suffix());
+ result = ss.str();
+ }
+
+ return result;
+}
+
+} // namespace modules
+} // namespace linkerconfig
+} // namespace android
\ No newline at end of file
diff --git a/modules/link.cc b/modules/link.cc
index 133cd57..64dddda 100644
--- a/modules/link.cc
+++ b/modules/link.cc
@@ -19,23 +19,21 @@
namespace android {
namespace linkerconfig {
namespace modules {
-std::string Link::GenerateConfig() {
- std::string prefix =
- "namespace." + origin_namespace_ + ".link." + target_namespace_;
- std::string config = "";
+void Link::WriteConfig(ConfigWriter& writer) {
+ writer.SetPrefix("namespace." + origin_namespace_ + ".link." +
+ target_namespace_);
if (allow_all_shared_libs_) {
- config = prefix + ".allow_all_shared_libs = true\n";
+ writer.WriteLine(".allow_all_shared_libs = true");
} else {
bool is_first = true;
for (auto& lib_name : shared_libs_) {
- config += prefix + ".shared_libs " + (is_first ? "= " : "+= ") +
- lib_name + "\n";
+ writer.WriteLine(".shared_libs %s %s",
+ is_first ? "=" : "+=", lib_name.c_str());
is_first = false;
}
}
-
- return config;
+ writer.ResetPrefix();
}
} // namespace modules
} // namespace linkerconfig
diff --git a/modules/namespace.cc b/modules/namespace.cc
index c2c3ea2..486691c 100644
--- a/modules/namespace.cc
+++ b/modules/namespace.cc
@@ -16,7 +16,7 @@
#include "linkerconfig/namespace.h"
-#include <android-base/logging.h>
+#include "linkerconfig/log.h"
#define LOG_TAG "linkerconfig"
@@ -26,17 +26,15 @@
constexpr const char* kDataAsanPath = "/data/asan";
-std::string Namespace::GetPathString(const std::string& path_type,
- const std::vector<std::string>& path_list) {
- std::string prefix = "namespace." + name_ + "." + path_type + ".paths ";
- std::string path_string = "";
+void Namespace::WritePathString(ConfigWriter& writer,
+ const std::string& path_type,
+ const std::vector<std::string>& path_list) {
+ std::string prefix = path_type + ".paths ";
bool is_first = true;
for (auto& path : path_list) {
- path_string += prefix + (is_first ? "= " : "+= ") + path + "\n";
+ writer.WriteLine(prefix + (is_first ? "= " : "+= ") + path);
is_first = false;
}
-
- return path_string;
}
std::shared_ptr<Link> Namespace::CreateLink(const std::string& target_namespace,
@@ -53,41 +51,40 @@
return new_link;
}
-std::string Namespace::GenerateConfig() {
- std::string config = "";
- std::string prefix = "namespace." + name_ + ".";
+void Namespace::WriteConfig(ConfigWriter& writer) {
+ writer.SetPrefix("namespace." + name_ + ".");
- config += prefix + "isolated = " + (is_isolated_ ? "true" : "false") + "\n";
+ writer.WriteLine("isolated = %s", is_isolated_ ? "true" : "false");
if (is_visible_) {
- config += prefix + "visible = true\n";
+ writer.WriteLine("visible = true");
}
- config += GetPathString("search", search_paths_);
- config += GetPathString("permitted", permitted_paths_);
- config += GetPathString("asan.search", asan_search_paths_);
- config += GetPathString("asan.permitted", asan_permitted_paths_);
+ WritePathString(writer, "search", search_paths_);
+ WritePathString(writer, "permitted", permitted_paths_);
+ WritePathString(writer, "asan.search", asan_search_paths_);
+ WritePathString(writer, "asan.permitted", asan_permitted_paths_);
if (!links_.empty()) {
- config += prefix + "links = ";
+ std::string link_list = "";
bool is_first = true;
for (auto& link : links_) {
if (!is_first) {
- config += ",";
+ link_list += ",";
}
- config += link.first;
+ link_list += link.first;
is_first = false;
}
- config += "\n";
+ writer.WriteLine("links = " + link_list);
for (auto& link : links_) {
- config += link.second->GenerateConfig();
+ link.second->WriteConfig(writer);
}
}
- return config;
+ writer.ResetPrefix();
}
void Namespace::AddSearchPath(const std::string& path, bool in_asan,
diff --git a/modules/section.cc b/modules/section.cc
index 5501ce3..98c3a94 100644
--- a/modules/section.cc
+++ b/modules/section.cc
@@ -16,7 +16,7 @@
#include "linkerconfig/section.h"
-#include <android-base/logging.h>
+#include "linkerconfig/log.h"
#define LOG_TAG "linkerconfig"
@@ -37,8 +37,8 @@
return new_namespace;
}
-std::string Section::GenerateConfig() {
- std::string config = "[" + name_ + "]\n";
+void Section::WriteConfig(ConfigWriter& writer) {
+ writer.WriteLine("[%s]", name_.c_str());
std::string additional_namespaces = "";
@@ -55,25 +55,22 @@
}
if (!is_first) {
- config += "additional.namespaces = " + additional_namespaces + "\n";
+ writer.WriteLine("additional.namespaces = " + additional_namespaces);
}
for (auto& ns : namespaces_) {
- config += ns.second->GenerateConfig();
+ ns.second->WriteConfig(writer);
}
-
- return config;
}
-std::string Section::GenerateBinaryPaths() {
- std::string binary_path = "";
- std::string prefix = "dir." + name_ + " = ";
+void Section::WriteBinaryPaths(ConfigWriter& writer) {
+ writer.SetPrefix("dir." + name_ + " = ");
for (auto& path : binary_paths_) {
- binary_path += prefix + path + "\n";
+ writer.WriteLine(path);
}
- return binary_path;
+ writer.ResetPrefix();
}
std::string Section::GetName() {
diff --git a/modules/tests/configwriter_test.cc b/modules/tests/configwriter_test.cc
new file mode 100644
index 0000000..4c5c9fa
--- /dev/null
+++ b/modules/tests/configwriter_test.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "linkerconfig/configwriter.h"
+#include "linkerconfig/variables.h"
+
+constexpr const char* kSampleContent = "Lorem ipsum dolor sit amet";
+
+constexpr const char* kExpectedResultWithPrefix =
+ R"(Lorem ipsum dolor sit amet
+SAMPLE.TEST.Text : Lorem ipsum dolor sit amet
+end of context
+)";
+
+constexpr const char* kExpectedResultWithVariables =
+ R"(/Value/Test
+/Invalid_Value/Path
+//Path2
+)";
+
+TEST(linkerconfig_configwriter, write_line) {
+ android::linkerconfig::modules::ConfigWriter writer;
+
+ writer.WriteLine(kSampleContent);
+
+ ASSERT_EQ(writer.ToString(), "Lorem ipsum dolor sit amet\n");
+}
+
+TEST(linkerconfig_configwriter, write_with_format) {
+ android::linkerconfig::modules::ConfigWriter writer;
+ writer.WriteLine("Sample text(%d) : %s", 10, kSampleContent);
+ ASSERT_EQ(writer.ToString(),
+ "Sample text(10) : Lorem ipsum dolor sit amet\n");
+}
+
+TEST(linkerconfig_configwriter, write_with_prefix) {
+ android::linkerconfig::modules::ConfigWriter writer;
+ writer.WriteLine(kSampleContent);
+ writer.SetPrefix("SAMPLE.TEST.");
+ writer.WriteLine("Text : %s", kSampleContent);
+ writer.ResetPrefix();
+ writer.WriteLine("end of context");
+
+ ASSERT_EQ(writer.ToString(), kExpectedResultWithPrefix);
+}
+
+TEST(linkerconfig_configwriter, replace_variable) {
+ android::linkerconfig::modules::ConfigWriter writer;
+
+ android::linkerconfig::modules::Variables::AddValue("Test_Prop_Q", "Value");
+ android::linkerconfig::modules::Variables::AddValue("VNDK_VER", "Q");
+
+ writer.WriteLine("/@{Test_Prop_@{VNDK_VER}}/Test");
+ writer.WriteLine("/@{Invalid_Key:Invalid_Value}/Path");
+ writer.WriteLine("/@{Invalid_Key:}/Path2");
+
+ ASSERT_EQ(writer.ToString(), kExpectedResultWithVariables);
+}
\ No newline at end of file
diff --git a/modules/tests/link_test.cc b/modules/tests/link_test.cc
index d84e3b4..a467ba4 100644
--- a/modules/tests/link_test.cc
+++ b/modules/tests/link_test.cc
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
+#include "linkerconfig/configwriter.h"
#include "linkerconfig/link.h"
constexpr const char* kSharedLibsExpectedResult =
@@ -25,18 +26,27 @@
)";
TEST(linkerconfig_link, link_with_all_shared_libs) {
+ android::linkerconfig::modules::ConfigWriter writer;
+
auto link = std::make_shared<android::linkerconfig::modules::Link>(
"originalNamespace", "targetNamespace", true);
- auto config_text = link->GenerateConfig();
+
+ link->WriteConfig(writer);
+ auto config_text = writer.ToString();
+
ASSERT_EQ(config_text,
"namespace.originalNamespace.link.targetNamespace.allow_all_shared_"
"libs = true\n");
}
TEST(linkerconfig_link, link_with_shared_libs) {
+ android::linkerconfig::modules::ConfigWriter writer;
auto link = std::make_shared<android::linkerconfig::modules::Link>(
"originalNamespace", "targetNamespace");
link->AddSharedLib("lib1.so", "lib2.so", "lib3.so");
- auto config_text = link->GenerateConfig();
+
+ link->WriteConfig(writer);
+ auto config_text = writer.ToString();
+
ASSERT_EQ(config_text, kSharedLibsExpectedResult);
}
\ No newline at end of file
diff --git a/modules/tests/namespace_test.cc b/modules/tests/namespace_test.cc
index e6a70eb..777d8d7 100644
--- a/modules/tests/namespace_test.cc
+++ b/modules/tests/namespace_test.cc
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
+#include "linkerconfig/configwriter.h"
#include "modules_testbase.h"
constexpr const char* kExpectedSimpleNamespaceConfig =
@@ -57,21 +58,27 @@
)";
TEST(linkerconfig_namespace, simple_namespace) {
+ android::linkerconfig::modules::ConfigWriter writer;
auto ns = std::make_shared<android::linkerconfig::modules::Namespace>(
"test_namespace");
+
DecorateNamespaceWithPaths(ns);
- auto config = ns->GenerateConfig();
+ ns->WriteConfig(writer);
+ auto config = writer.ToString();
ASSERT_EQ(config, kExpectedSimpleNamespaceConfig);
}
TEST(linkerconfig_namespace, namespace_with_links) {
+ android::linkerconfig::modules::ConfigWriter writer;
auto ns = std::make_shared<android::linkerconfig::modules::Namespace>(
"test_namespace", /*is_isolated*/ true,
/*is_visible*/ true);
+
DecorateNamespaceWithPaths(ns);
DecorateNamespaceWithLinks(ns, "target_namespace1", "target_namespace2");
- auto config = ns->GenerateConfig();
+ ns->WriteConfig(writer);
+ auto config = writer.ToString();
ASSERT_EQ(config, kExpectedNamespaceWithLinkConfig);
}
\ No newline at end of file
diff --git a/modules/tests/section_test.cc b/modules/tests/section_test.cc
index 697e595..bba62a4 100644
--- a/modules/tests/section_test.cc
+++ b/modules/tests/section_test.cc
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
+#include "linkerconfig/configwriter.h"
#include "linkerconfig/section.h"
#include "modules_testbase.h"
@@ -96,7 +97,9 @@
dir.test_section = binary_path2
dir.test_section = binary_path3
)";
+
TEST(linkerconfig_section, section_with_namespaces) {
+ android::linkerconfig::modules::ConfigWriter writer;
android::linkerconfig::modules::Section section("test_section");
auto default_namespace = section.CreateNamespace(
@@ -111,23 +114,28 @@
auto namespace2 = section.CreateNamespace("namespace2");
DecorateNamespaceWithPaths(namespace2);
- auto config = section.GenerateConfig();
+ section.WriteConfig(writer);
+ auto config = writer.ToString();
ASSERT_EQ(config, kSectionWithNamespacesExpectedResult);
}
TEST(linkerconfig_section, section_with_one_namespace) {
+ android::linkerconfig::modules::ConfigWriter writer;
android::linkerconfig::modules::Section section("test_section");
auto ns = section.CreateNamespace("default");
DecorateNamespaceWithPaths(ns);
- auto config = section.GenerateConfig();
+ section.WriteConfig(writer);
+ auto config = writer.ToString();
ASSERT_EQ(config, kSectionWithOneNamespaceExpectedResult);
}
TEST(linkerconfig_section, binary_paths) {
+ android::linkerconfig::modules::ConfigWriter writer;
android::linkerconfig::modules::Section section("test_section");
section.AddBinaryPath("binary_path1", "binary_path2", "binary_path3");
- auto binary_paths = section.GenerateBinaryPaths();
+ section.WriteBinaryPaths(writer);
+ auto binary_paths = writer.ToString();
ASSERT_EQ(binary_paths, kSectionBinaryPathExpectedResult);
}
\ No newline at end of file
diff --git a/modules/tests/variables_test.cc b/modules/tests/variables_test.cc
new file mode 100644
index 0000000..e4ada44
--- /dev/null
+++ b/modules/tests/variables_test.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <android-base/properties.h>
+#include <gtest/gtest.h>
+#include <chrono>
+
+#include "linkerconfig/variables.h"
+
+using android::linkerconfig::modules::Variables;
+
+TEST(linkerconfig_variables, load_from_map) {
+ Variables::AddValue("TEST_KEY", "TEST_VALUE");
+ auto value = Variables::GetValue("TEST_KEY");
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value.value(), "TEST_VALUE");
+}
+
+TEST(linkerconfig_variables, load_from_property) {
+#if defined(__BIONIC__)
+ android::base::SetProperty("debug.linkerconfig.test_prop_key",
+ "TEST_PROP_VALUE");
+ ASSERT_TRUE(android::base::WaitForProperty("debug.linkerconfig.test_prop_key",
+ "TEST_PROP_VALUE",
+ std::chrono::seconds(1)));
+ auto value = Variables::GetValue("debug.linkerconfig.test_prop_key");
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value.value(), "TEST_PROP_VALUE");
+#endif
+}
+
+TEST(linkerconfig_variables, fallback_value) {
+ auto value = Variables::GetValue("INVALID_KEY");
+ ASSERT_FALSE(value.has_value());
+}
\ No newline at end of file
diff --git a/modules/variables.cc b/modules/variables.cc
new file mode 100644
index 0000000..fcc41ee
--- /dev/null
+++ b/modules/variables.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "linkerconfig/variables.h"
+
+#include <android-base/properties.h>
+
+#include "linkerconfig/log.h"
+
+namespace android {
+namespace linkerconfig {
+namespace modules {
+
+std::map<std::string, std::string> Variables::variables_;
+
+std::optional<std::string> Variables::GetValue(const std::string& variable) {
+ // If variable is in predefined key-value pair, use this value
+ if (variables_.find(variable) != variables_.end() &&
+ !variables_[variable].empty()) {
+ return {variables_[variable]};
+ }
+
+ // If variable is defined as property, use this value
+ std::string prop_value = android::base::GetProperty(variable, "");
+ if (!prop_value.empty()) {
+ return {prop_value};
+ }
+
+ // If cannot find variable, return default value
+ return std::nullopt;
+}
+
+void Variables::AddValue(const std::string& key, const std::string& value) {
+ variables_[key] = value;
+}
+} // namespace modules
+} // namespace linkerconfig
+} // namespace android
\ No newline at end of file