Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89c9a128 authored by Ryan Mitchell's avatar Ryan Mitchell Committed by Android (Google) Code Review
Browse files

Merge "AAPT2: Refactor flags into commands"

parents fb4d62b6 833a1a6c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
//

toolSources = [
    "cmd/Command.cpp",
    "cmd/Compile.cpp",
    "cmd/Convert.cpp",
    "cmd/Diff.cpp",
@@ -124,7 +125,6 @@ cc_library_host_static {
        "ConfigDescription.cpp",
        "Debug.cpp",
        "DominatorTree.cpp",
        "Flags.cpp",
        "java/AnnotationProcessor.cpp",
        "java/ClassDefinition.cpp",
        "java/JavaClassGenerator.cpp",
+97 −83
Original line number Diff line number Diff line
@@ -29,6 +29,13 @@
#include "androidfw/StringPiece.h"

#include "Diagnostics.h"
#include "cmd/Command.h"
#include "cmd/Compile.h"
#include "cmd/Convert.h"
#include "cmd/Diff.h"
#include "cmd/Dump.h"
#include "cmd/Link.h"
#include "cmd/Optimize.h"
#include "util/Files.h"
#include "util/Util.h"

@@ -43,52 +50,66 @@ static const char* sMajorVersion = "2";
// Update minor version whenever a feature or flag is added.
static const char* sMinorVersion = "19";

static void PrintVersion() {
/** Prints the version information of AAPT2. */
class VersionCommand : public Command {
 public:
  explicit VersionCommand() : Command("version") {
    SetDescription("Prints the version of aapt.");
  }

  int Action(const std::vector<std::string>& /* args */) override {
    std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion,
                              sMinorVersion)
              << std::endl;
    return 0;
  }
};

static void PrintUsage() {
  std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl;
}

extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics);
extern int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics);
extern int Dump(const std::vector<StringPiece>& args);
extern int Diff(const std::vector<StringPiece>& args);
extern int Optimize(const std::vector<StringPiece>& args);
extern int Convert(const std::vector<StringPiece>& args);

static int ExecuteCommand(const StringPiece& command, const std::vector<StringPiece>& args,
                          IDiagnostics* diagnostics) {
  if (command == "compile" || command == "c") {
    return Compile(args, diagnostics);
  } else if (command == "link" || command == "l") {
    return Link(args, diagnostics);
  } else if (command == "dump" || command == "d") {
    return Dump(args);
  } else if (command == "diff") {
    return Diff(args);
  } else if (command == "optimize") {
    return Optimize(args);
  } else if (command == "convert") {
    return Convert(args);
  } else if (command == "version") {
    PrintVersion();
    return 0;
/** The main entry point of AAPT. */
class MainCommand : public Command {
 public:
  explicit MainCommand(IDiagnostics* diagnostics) : Command("aapt2"), diagnostics_(diagnostics) {
    AddOptionalSubcommand(util::make_unique<CompileCommand>(diagnostics));
    AddOptionalSubcommand(util::make_unique<LinkCommand>(diagnostics));
    AddOptionalSubcommand(util::make_unique<DumpCommand>());
    AddOptionalSubcommand(util::make_unique<DiffCommand>());
    AddOptionalSubcommand(util::make_unique<OptimizeCommand>());
    AddOptionalSubcommand(util::make_unique<ConvertCommand>());
    AddOptionalSubcommand(util::make_unique<VersionCommand>());
  }

  int Action(const std::vector<std::string>& args) override {
    if (args.size() == 0) {
      diagnostics_->Error(DiagMessage() << "no subcommand specified");
    } else {
      diagnostics_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'");
    }
  diagnostics->Error(DiagMessage() << "unknown command '" << command << "'");

    Usage(&std::cerr);
    return -1;
  }

static void RunDaemon(IDiagnostics* diagnostics) {
 private:
  IDiagnostics* diagnostics_;
};

/*
 * Run in daemon mode. The first line of input is the command. This can be 'quit' which ends
 * the daemon mode. Each subsequent line is a single parameter to the command. The end of a
 * invocation is signaled by providing an empty line. At any point, an EOF signal or the
 * command 'quit' will end the daemon mode.
 */
class DaemonCommand : public Command {
 public:
  explicit DaemonCommand(IDiagnostics* diagnostics) : Command("daemon", "m"),
                                                      diagnostics_(diagnostics) {
    SetDescription("Runs aapt in daemon mode. Each subsequent line is a single parameter to the\n"
        "command. The end of an invocation is signaled by providing an empty line.");
  }

  int Action(const std::vector<std::string>& /* args */) override {
    std::cout << "Ready" << std::endl;

  // Run in daemon mode. The first line of input is the command. This can be 'quit' which ends
  // the daemon mode. Each subsequent line is a single parameter to the command. The end of a
  // invocation is signaled by providing an empty line. At any point, an EOF signal or the
  // command 'quit' will end the daemon mode.
    while (true) {
      std::vector<std::string> raw_args;
      for (std::string line; std::getline(std::cin, line) && !line.empty();) {
@@ -104,53 +125,46 @@ static void RunDaemon(IDiagnostics* diagnostics) {
        continue;
      }

      // End the dameon
      if (raw_args[0] == "quit") {
        break;
      }

      std::vector<StringPiece> args;
    args.insert(args.end(), ++raw_args.begin(), raw_args.end());
    int ret = ExecuteCommand(raw_args[0], args, diagnostics);
    if (ret != 0) {
      args.insert(args.end(), raw_args.begin(), raw_args.end());
      if (MainCommand(diagnostics_).Execute(args, &std::cerr) != 0) {
        std::cerr << "Error" << std::endl;
      }
      std::cerr << "Done" << std::endl;
    }
    std::cout << "Exiting daemon" << std::endl;

    return 0;
  }

 private:
  IDiagnostics* diagnostics_;
};

}  // namespace aapt

int MainImpl(int argc, char** argv) {
  if (argc < 2) {
    std::cerr << "no command specified\n";
    aapt::PrintUsage();
  if (argc < 1) {
    return -1;
  }

  argv += 1;
  argc -= 1;

  aapt::StdErrDiagnostics diagnostics;

  // Collect the arguments starting after the program name and command name.
  std::vector<StringPiece> args;
  for (int i = 1; i < argc; i++) {
    args.push_back(argv[i]);
  }

  const StringPiece command(argv[0]);
  if (command != "daemon" && command != "m") {
    // Single execution.
    const int result = aapt::ExecuteCommand(command, args, &diagnostics);
    if (result < 0) {
      aapt::PrintUsage();
    }
    return result;
  }
  // Add the daemon subcommand here so it cannot be called while executing the daemon
  aapt::StdErrDiagnostics diagnostics;
  auto main_command = new aapt::MainCommand(&diagnostics);
  main_command->AddOptionalSubcommand(aapt::util::make_unique<aapt::DaemonCommand>(&diagnostics));

  aapt::RunDaemon(&diagnostics);
  return 0;
  return main_command->Execute(args, &std::cerr);
}

int main(int argc, char** argv) {
+78 −45
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 * Copyright (C) 2018 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.
@@ -14,7 +14,7 @@
 * limitations under the License.
 */

#include "Flags.h"
#include "Command.h"

#include <iomanip>
#include <iostream>
@@ -29,7 +29,7 @@ using android::StringPiece;

namespace aapt {

Flags& Flags::RequiredFlag(const StringPiece& name,
void Command::AddRequiredFlag(const StringPiece& name,
    const StringPiece& description, std::string* value) {
  auto func = [value](const StringPiece& arg) -> bool {
    *value = arg.to_string();
@@ -37,10 +37,9 @@ Flags& Flags::RequiredFlag(const StringPiece& name,
  };

  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
  return *this;
}

Flags& Flags::RequiredFlagList(const StringPiece& name,
void Command::AddRequiredFlagList(const StringPiece& name,
    const StringPiece& description,
    std::vector<std::string>* value) {
  auto func = [value](const StringPiece& arg) -> bool {
@@ -49,10 +48,9 @@ Flags& Flags::RequiredFlagList(const StringPiece& name,
  };

  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
  return *this;
}

Flags& Flags::OptionalFlag(const StringPiece& name,
void Command::AddOptionalFlag(const StringPiece& name,
    const StringPiece& description,
    Maybe<std::string>* value) {
  auto func = [value](const StringPiece& arg) -> bool {
@@ -61,10 +59,9 @@ Flags& Flags::OptionalFlag(const StringPiece& name,
  };

  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
  return *this;
}

Flags& Flags::OptionalFlagList(const StringPiece& name,
void Command::AddOptionalFlagList(const StringPiece& name,
    const StringPiece& description,
    std::vector<std::string>* value) {
  auto func = [value](const StringPiece& arg) -> bool {
@@ -73,10 +70,9 @@ Flags& Flags::OptionalFlagList(const StringPiece& name,
  };

  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
  return *this;
}

Flags& Flags::OptionalFlagList(const StringPiece& name,
void Command::AddOptionalFlagList(const StringPiece& name,
    const StringPiece& description,
    std::unordered_set<std::string>* value) {
  auto func = [value](const StringPiece& arg) -> bool {
@@ -85,10 +81,9 @@ Flags& Flags::OptionalFlagList(const StringPiece& name,
  };

  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
  return *this;
}

Flags& Flags::OptionalSwitch(const StringPiece& name,
void Command::AddOptionalSwitch(const StringPiece& name,
    const StringPiece& description, bool* value) {
  auto func = [value](const StringPiece& arg) -> bool {
    *value = true;
@@ -96,20 +91,51 @@ Flags& Flags::OptionalSwitch(const StringPiece& name,
  };

  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false});
  return *this;
}

void Flags::Usage(const StringPiece& command, std::ostream* out) {
void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand) {
  subcommand->fullname_ = name_ + " " + subcommand->name_;
  subcommands_.push_back(std::move(subcommand));
}

void Command::SetDescription(const android::StringPiece& description) {
  description_ = description.to_string();
}

void Command::Usage(std::ostream* out) {
  constexpr size_t kWidth = 50;

  *out << command << " [options]";
  *out << fullname_;

  if (!subcommands_.empty()) {
    *out << " [subcommand]";
  }

  *out << " [options]";
  for (const Flag& flag : flags_) {
    if (flag.required) {
      *out << " " << flag.name << " arg";
    }
  }

  *out << " files...\n\nOptions:\n";
  *out << " files...\n";

  if (!subcommands_.empty()) {
    *out << "\nSubcommands:\n";
    for (auto& subcommand : subcommands_) {
      std::string argline = subcommand->name_;

      // Split the description by newlines and write out the argument (which is
      // empty after the first line) followed by the description line. This will make sure
      // that multiline descriptions are still right justified and aligned.
      for (StringPiece line : util::Tokenize(subcommand->description_, '\n')) {
        *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
        argline = " ";
      }
    }
  }

  *out << "\nOptions:\n";

  for (const Flag& flag : flags_) {
    std::string argline = flag.name;
@@ -118,10 +144,8 @@ void Flags::Usage(const StringPiece& command, std::ostream* out) {
    }

    // Split the description by newlines and write out the argument (which is
    // empty after
    // the first line) followed by the description line. This will make sure
    // that multiline
    // descriptions are still right justified and aligned.
    // empty after the first line) followed by the description line. This will make sure
    // that multiline descriptions are still right justified and aligned.
    for (StringPiece line : util::Tokenize(flag.description, '\n')) {
      *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
      argline = " ";
@@ -132,19 +156,29 @@ void Flags::Usage(const StringPiece& command, std::ostream* out) {
  out->flush();
}

bool Flags::Parse(const StringPiece& command,
                  const std::vector<StringPiece>& args,
                  std::ostream* out_error) {
int Command::Execute(const std::vector<android::StringPiece>& args, std::ostream* out_error) {
  std::vector<std::string> file_args;

  for (size_t i = 0; i < args.size(); i++) {
    StringPiece arg = args[i];
    if (*(arg.data()) != '-') {
      args_.push_back(arg.to_string());
      // Continue parsing as the sub command if the first argument matches one of the subcommands
      if (i == 0) {
        for (auto& subcommand : subcommands_) {
          if (arg == subcommand->name_ || arg==subcommand->short_name_) {
            return subcommand->Execute(
                std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error);
          }
        }
      }

      file_args.push_back(arg.to_string());
      continue;
    }

    if (arg == "-h" || arg == "--help") {
      Usage(command, out_error);
      return false;
      Usage(out_error);
      return 1;
    }

    bool match = false;
@@ -154,7 +188,7 @@ bool Flags::Parse(const StringPiece& command,
          i++;
          if (i >= args.size()) {
            *out_error << flag.name << " missing argument.\n\n";
            Usage(command, out_error);
            Usage(out_error);
            return false;
          }
          flag.action(args[i]);
@@ -169,21 +203,20 @@ bool Flags::Parse(const StringPiece& command,

    if (!match) {
      *out_error << "unknown option '" << arg << "'.\n\n";
      Usage(command, out_error);
      return false;
      Usage(out_error);
      return 1;
    }
  }

  for (const Flag& flag : flags_) {
    if (flag.required && !flag.parsed) {
      *out_error << "missing required flag " << flag.name << "\n\n";
      Usage(command, out_error);
      return false;
      Usage(out_error);
      return 1;
    }
  }
  return true;
}

const std::vector<std::string>& Flags::GetArgs() { return args_; }
  return Action(file_args);
}

}  // namespace aapt
+90 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 * Copyright (C) 2018 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.
@@ -14,8 +14,8 @@
 * limitations under the License.
 */

#ifndef AAPT_FLAGS_H
#define AAPT_FLAGS_H
#ifndef AAPT_COMMAND_H
#define AAPT_COMMAND_H

#include <functional>
#include <ostream>
@@ -29,27 +29,42 @@

namespace aapt {

class Flags {
class Command {
 public:
  Flags& RequiredFlag(const android::StringPiece& name, const android::StringPiece& description,
  explicit Command(const android::StringPiece& name) : name_(name.to_string()),
                                                       fullname_(name.to_string()) { }
  explicit Command(const android::StringPiece& name, const android::StringPiece& short_name)
      : name_(name.to_string()), short_name_(short_name.to_string()), fullname_(name.to_string()) {}
  virtual ~Command() = default;

  void AddRequiredFlag(const android::StringPiece& name, const android::StringPiece& description,
      std::string* value);
  Flags& RequiredFlagList(const android::StringPiece& name, const android::StringPiece& description,
                          std::vector<std::string>* value);
  Flags& OptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
  void AddRequiredFlagList(const android::StringPiece& name, const android::StringPiece&
      description, std::vector<std::string>* value);
  void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
      Maybe<std::string>* value);
  Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description,
                          std::vector<std::string>* value);
  Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description,
                          std::unordered_set<std::string>* value);
  Flags& OptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
  void AddOptionalFlagList(const android::StringPiece& name,
      const android::StringPiece& description, std::vector<std::string>* value);
  void AddOptionalFlagList(const android::StringPiece& name,
      const android::StringPiece& description, std::unordered_set<std::string>* value);
  void AddOptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
      bool* value);
  void AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand);

  void SetDescription(const android::StringPiece& name);

  void Usage(const android::StringPiece& command, std::ostream* out);
  /** Prints the help menu of the command. */
  void Usage(std::ostream* out);

  bool Parse(const android::StringPiece& command, const std::vector<android::StringPiece>& args,
             std::ostream* outError);
  /**
   * Parses the command line arguments, sets the flag variable values, and runs the action of
   * the command. If the arguments fail to parse to the command and its subcommands, then the action
   * will not be run and the usage will be printed instead.
   **/
  int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError);

  const std::vector<std::string>& GetArgs();
  /** The action to preform when the command is executed. */
  virtual int Action(const std::vector<std::string>& args) = 0;

 private:
  struct Flag {
@@ -62,10 +77,14 @@ class Flags {
    bool parsed;
  };

  std::string description_;
  std::string name_;
  std::string short_name_;
  std::string fullname_;
  std::vector<Flag> flags_;
  std::vector<std::string> args_;
  std::vector<std::unique_ptr<Command>> subcommands_;
};

}  // namespace aapt

#endif  // AAPT_FLAGS_H
#endif  // AAPT_COMMAND_H
+27 −67
Original line number Diff line number Diff line
@@ -14,8 +14,9 @@
 * limitations under the License.
 */

#include <dirent.h>
#include "Compile.h"

#include <dirent.h>
#include <string>

#include "android-base/errors.h"
@@ -27,7 +28,6 @@

#include "ConfigDescription.h"
#include "Diagnostics.h"
#include "Flags.h"
#include "ResourceParser.h"
#include "ResourceTable.h"
#include "cmd/Util.h"
@@ -121,17 +121,6 @@ static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
                          extension.to_string(), config_str.to_string(), config};
}

struct CompileOptions {
  std::string output_path;
  Maybe<std::string> res_dir;
  Maybe<std::string> generate_text_symbols_path;
  Maybe<Visibility::Level> visibility;
  bool pseudolocalize = false;
  bool no_png_crunch = false;
  bool legacy_mode = false;
  bool verbose = false;
};

static std::string BuildIntermediateContainerFilename(const ResourcePathData& data) {
  std::stringstream name;
  name << data.resource_dir;
@@ -712,50 +701,21 @@ class CompileContext : public IAaptContext {
  bool verbose_ = false;
};

// Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
  CompileContext context(diagnostics);
  CompileOptions options;

  bool verbose = false;
  Maybe<std::string> visibility;
  Flags flags =
      Flags()
          .RequiredFlag("-o", "Output path", &options.output_path)
          .OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir)
          .OptionalFlag("--output-text-symbols",
                        "Generates a text file containing the resource symbols in the\n"
                        "specified file",
                        &options.generate_text_symbols_path)
          .OptionalSwitch("--pseudo-localize",
                          "Generate resources for pseudo-locales "
                          "(en-XA and ar-XB)",
                          &options.pseudolocalize)
          .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch)
          .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
                          &options.legacy_mode)
          .OptionalSwitch("-v", "Enables verbose logging", &verbose)
          .OptionalFlag("--visibility",
                        "Sets the visibility of the compiled resources to the specified\n"
                        "level. Accepted levels: public, private, default",
                        &visibility);
  if (!flags.Parse("aapt2 compile", args, &std::cerr)) {
    return 1;
  }

  context.SetVerbose(verbose);

  if (visibility) {
    if (visibility.value() == "public") {
      options.visibility = Visibility::Level::kPublic;
    } else if (visibility.value() == "private") {
      options.visibility = Visibility::Level::kPrivate;
    } else if (visibility.value() == "default") {
      options.visibility = Visibility::Level::kUndefined;
int CompileCommand::Action(const std::vector<std::string>& args) {
  CompileContext context(diagnostic_);
  context.SetVerbose(options_.verbose);

  if (visibility_) {
    if (visibility_.value() == "public") {
      options_.visibility = Visibility::Level::kPublic;
    } else if (visibility_.value() == "private") {
      options_.visibility = Visibility::Level::kPrivate;
    } else if (visibility_.value() == "default") {
      options_.visibility = Visibility::Level::kUndefined;
    } else {
      context.GetDiagnostics()->Error(
          DiagMessage() << "Unrecognized visibility level passes to --visibility: '"
                        << visibility.value() << "'. Accepted levels: public, private, default");
                        << visibility_.value() << "'. Accepted levels: public, private, default");
      return 1;
    }
  }
@@ -763,25 +723,25 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
  std::unique_ptr<IArchiveWriter> archive_writer;

  std::vector<ResourcePathData> input_data;
  if (options.res_dir) {
    if (!flags.GetArgs().empty()) {
  if (options_.res_dir) {
    if (!args.empty()) {
      // Can't have both files and a resource directory.
      context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
      flags.Usage("aapt2 compile", &std::cerr);
      Usage(&std::cerr);
      return 1;
    }

    if (!LoadInputFilesFromDir(&context, options, &input_data)) {
    if (!LoadInputFilesFromDir(&context, options_, &input_data)) {
      return 1;
    }

    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options.output_path);
    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);

  } else {
    input_data.reserve(flags.GetArgs().size());
    input_data.reserve(args.size());

    // Collect data from the path for each input file.
    for (const std::string& arg : flags.GetArgs()) {
    for (const std::string& arg : args) {
      std::string error_str;
      if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) {
        input_data.push_back(std::move(path_data.value()));
@@ -791,7 +751,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
      }
    }

    archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options.output_path);
    archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options_.output_path);
  }

  if (!archive_writer) {
@@ -800,7 +760,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {

  bool error = false;
  for (ResourcePathData& path_data : input_data) {
    if (options.verbose) {
    if (options_.verbose) {
      context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing");
    }

@@ -821,7 +781,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
      if (*type != ResourceType::kRaw) {
        if (path_data.extension == "xml") {
          compile_func = &CompileXml;
        } else if ((!options.no_png_crunch && path_data.extension == "png")
        } else if ((!options_.no_png_crunch && path_data.extension == "png")
            || path_data.extension == "9.png") {
          compile_func = &CompilePng;
        }
@@ -835,7 +795,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {

    // Treat periods as a reserved character that should not be present in a file name
    // Legacy support for AAPT which did not reserve periods
    if (compile_func != &CompileFile && !options.legacy_mode
    if (compile_func != &CompileFile && !options_.legacy_mode
        && std::count(path_data.name.begin(), path_data.name.end(), '.') != 0) {
      error = true;
      context.GetDiagnostics()->Error(DiagMessage() << "resource file '" << path_data.source.path
@@ -846,7 +806,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {

    // Compile the file.
    const std::string out_path = BuildIntermediateContainerFilename(path_data);
    error |= !compile_func(&context, options, path_data, archive_writer.get(), out_path);
    error |= !compile_func(&context, options_, path_data, archive_writer.get(), out_path);
  }
  return error ? 1 : 0;
}
Loading