Add command line parser for simulator

Add a command line parser. Also add the support to parse the oem
property file and skip certain functions.

Bug: 131911365
Test: run simulator for wear builds
Change-Id: Ide306b53d3f42b29c02279969aeb18bec4045d6f
diff --git a/updater/include/updater/build_info.h b/updater/include/updater/build_info.h
index a1355e8..2229957 100644
--- a/updater/include/updater/build_info.h
+++ b/updater/include/updater/build_info.h
@@ -51,9 +51,18 @@
   // Parses the given target-file, initializes the build properties and extracts the images.
   bool ParseTargetFile(const std::string_view target_file_path, bool extracted_input);
 
+  std::string GetOemSettings() const {
+    return oem_settings_;
+  }
+  void SetOemSettings(const std::string_view oem_settings) {
+    oem_settings_ = oem_settings;
+  }
+
  private:
   // A map to store the system properties during simulation.
   std::map<std::string, std::string, std::less<>> build_props_;
+  // A file that contains the oem properties.
+  std::string oem_settings_;
   // A map from the blockdev_name to the FakeBlockDevice object, which contains the path to the
   // temporary file.
   std::map<std::string, FakeBlockDevice, std::less<>> blockdev_map_;
diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp
index c8718c7..d2074d6 100644
--- a/updater/simulator_runtime.cpp
+++ b/updater/simulator_runtime.cpp
@@ -87,7 +87,11 @@
 }
 
 bool SimulatorRuntime::ReadFileToString(const std::string_view filename,
-                                        std::string* /* content */) const {
+                                        std::string* content) const {
+  if (android::base::EndsWith(filename, "oem.prop")) {
+    return android::base::ReadFileToString(source_->GetOemSettings(), content);
+  }
+
   LOG(INFO) << "SKip reading filename " << filename;
   return true;
 }
diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp
index 019c404..278a440 100644
--- a/updater/update_simulator_main.cpp
+++ b/updater/update_simulator_main.cpp
@@ -14,11 +14,18 @@
  * limitations under the License.
  */
 
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+
 #include <string>
+#include <string_view>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 
+#include "edify/expr.h"
 #include "otautil/error_code.h"
 #include "otautil/paths.h"
 #include "updater/blockimg.h"
@@ -28,21 +35,67 @@
 #include "updater/simulator_runtime.h"
 #include "updater/updater.h"
 
+using namespace std::string_literals;
+
+void Usage(std::string_view name) {
+  LOG(INFO) << "Usage: " << name << "[--oem_settings <oem_property_file>]"
+            << "[--skip_functions <skip_function_file>]"
+            << " --source <source_target_file>"
+            << " --ota_package <ota_package>";
+}
+
+Value* SimulatorPlaceHolderFn(const char* name, State* /* state */,
+                              const std::vector<std::unique_ptr<Expr>>& /* argv */) {
+  LOG(INFO) << "Skip function " << name << " in host simulation";
+  return StringValue("t");
+}
+
 int main(int argc, char** argv) {
   // Write the logs to stdout.
   android::base::InitLogging(argv, &android::base::StderrLogger);
 
-  if (argc != 3 && argc != 4) {
-    LOG(ERROR) << "unexpected number of arguments: " << argc << std::endl
-               << "Usage: " << argv[0] << " <source_target-file> <ota_package>";
-    return EXIT_FAILURE;
+  std::string oem_settings;
+  std::string skip_function_file;
+  std::string source_target_file;
+  std::string package_name;
+
+  constexpr struct option OPTIONS[] = {
+    { "oem_settings", required_argument, nullptr, 0 },
+    { "ota_package", required_argument, nullptr, 0 },
+    { "skip_functions", required_argument, nullptr, 0 },
+    { "source", required_argument, nullptr, 0 },
+    { nullptr, 0, nullptr, 0 },
+  };
+
+  int arg;
+  int option_index;
+  while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
+    if (arg != 0) {
+      LOG(ERROR) << "Invalid command argument";
+      Usage(argv[0]);
+      return EXIT_FAILURE;
+    }
+    auto option_name = OPTIONS[option_index].name;
+    // The same oem property file used during OTA generation. It's needed for file_getprop() to
+    // return the correct value for the source build.
+    if (option_name == "oem_settings"s) {
+      oem_settings = optarg;
+    } else if (option_name == "skip_functions"s) {
+      skip_function_file = optarg;
+    } else if (option_name == "source"s) {
+      source_target_file = optarg;
+    } else if (option_name == "ota_package"s) {
+      package_name = optarg;
+    } else {
+      Usage(argv[0]);
+      return EXIT_FAILURE;
+    }
   }
 
-  // TODO(xunchang) implement a commandline parser, e.g. it can take an oem property so that the
-  // file_getprop() will return correct value.
-
-  std::string source_target_file = argv[1];
-  std::string package_name = argv[2];
+  if (source_target_file.empty() || package_name.empty()) {
+    Usage(argv[0]);
+    return EXIT_FAILURE;
+  }
 
   // Configure edify's functions.
   RegisterBuiltins();
@@ -50,6 +103,22 @@
   RegisterBlockImageFunctions();
   RegisterDynamicPartitionsFunctions();
 
+  if (!skip_function_file.empty()) {
+    std::string content;
+    if (!android::base::ReadFileToString(skip_function_file, &content)) {
+      PLOG(ERROR) << "Failed to read " << skip_function_file;
+      return EXIT_FAILURE;
+    }
+
+    auto lines = android::base::Split(content, "\n");
+    for (const auto& line : lines) {
+      if (line.empty() || android::base::StartsWith(line, "#")) {
+        continue;
+      }
+      RegisterFunction(line, SimulatorPlaceHolderFn);
+    }
+  }
+
   TemporaryFile temp_saved_source;
   TemporaryFile temp_last_command;
   TemporaryDir temp_stash_base;
@@ -67,6 +136,11 @@
     return EXIT_FAILURE;
   }
 
+  if (!oem_settings.empty()) {
+    CHECK_EQ(0, access(oem_settings.c_str(), R_OK));
+    source_build_info.SetOemSettings(oem_settings);
+  }
+
   Updater updater(std::make_unique<SimulatorRuntime>(&source_build_info));
   if (!updater.Init(cmd_pipe.release(), package_name, false)) {
     return EXIT_FAILURE;