mirror of
https://github.com/aseprite/aseprite.git
synced 2024-10-05 06:20:10 +00:00
Change the way ProgramOptions store specified args
Now we can parse command line arguments sequentially in the same order they were specified.
This commit is contained in:
parent
ba73bcafed
commit
94a642cd36
@ -111,9 +111,7 @@ public:
|
||||
|
||||
App* App::m_instance = NULL;
|
||||
|
||||
// Initializes the application loading the modules, setting the
|
||||
// graphics mode, loading the configuration and resources, etc.
|
||||
App::App(int argc, const char* argv[])
|
||||
App::App()
|
||||
: m_modules(NULL)
|
||||
, m_legacy(NULL)
|
||||
, m_isGui(false)
|
||||
@ -122,14 +120,18 @@ App::App(int argc, const char* argv[])
|
||||
{
|
||||
ASSERT(m_instance == NULL);
|
||||
m_instance = this;
|
||||
}
|
||||
|
||||
void App::initialize(int argc, const char* argv[])
|
||||
{
|
||||
AppOptions options(argc, argv);
|
||||
|
||||
// Initializes the application loading the modules, setting the
|
||||
// graphics mode, loading the configuration and resources, etc.
|
||||
m_modules = new Modules(!options.startUI(), options.verbose());
|
||||
m_isGui = options.startUI();
|
||||
m_isShell = options.startShell();
|
||||
m_legacy = new LegacyModules(isGui() ? REQUIRE_INTERFACE: 0);
|
||||
m_files = options.files();
|
||||
|
||||
if (options.hasExporterParams()) {
|
||||
m_exporter.reset(new DocumentExporter);
|
||||
@ -177,13 +179,9 @@ App::App(int argc, const char* argv[])
|
||||
|
||||
// Set system palette to the default one.
|
||||
set_current_palette(NULL, true);
|
||||
}
|
||||
|
||||
int App::run()
|
||||
{
|
||||
UIContext* context = UIContext::instance();
|
||||
|
||||
// Initialize GUI interface
|
||||
UIContext* context = UIContext::instance();
|
||||
if (isGui()) {
|
||||
PRINTF("GUI mode\n");
|
||||
|
||||
@ -215,9 +213,15 @@ int App::run()
|
||||
// Procress options
|
||||
PRINTF("Processing options...\n");
|
||||
|
||||
// Open file specified in the command line
|
||||
{
|
||||
Console console;
|
||||
for (const std::string& filename : m_files) {
|
||||
for (const auto& value : options.values()) {
|
||||
if (value.option() != NULL) // File names aren't associated to any option
|
||||
continue;
|
||||
|
||||
const std::string& filename = value.value();
|
||||
|
||||
// Load the sprite
|
||||
Document* document = load_document(context, filename.c_str());
|
||||
if (!document) {
|
||||
@ -245,7 +249,10 @@ int App::run()
|
||||
m_exporter->exportSheet();
|
||||
m_exporter.reset(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void App::run()
|
||||
{
|
||||
// Run the GUI
|
||||
if (isGui()) {
|
||||
#ifdef ENABLE_UPDATER
|
||||
@ -301,8 +308,6 @@ int App::run()
|
||||
std::cerr << "Your version of " PACKAGE " wasn't compiled with shell support.\n";
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Finishes the Aseprite application.
|
||||
|
@ -51,7 +51,7 @@ namespace app {
|
||||
|
||||
class App {
|
||||
public:
|
||||
App(int argc, const char* argv[]);
|
||||
App();
|
||||
~App();
|
||||
|
||||
static App* instance() { return m_instance; }
|
||||
@ -63,8 +63,10 @@ namespace app {
|
||||
bool isPortable();
|
||||
|
||||
// Runs the Aseprite application. In GUI mode it's the top-level
|
||||
// window, in console/scripting it just runs the specified scripts.
|
||||
int run();
|
||||
// window, in console/scripting it just runs the specified
|
||||
// scripts.
|
||||
void initialize(int argc, const char* argv[]);
|
||||
void run();
|
||||
|
||||
tools::ToolBox* getToolBox() const;
|
||||
RecentFiles* getRecentFiles() const;
|
||||
|
@ -57,27 +57,27 @@ AppOptions::AppOptions(int argc, const char* argv[])
|
||||
try {
|
||||
m_po.parse(argc, argv);
|
||||
|
||||
m_verbose = verbose.enabled();
|
||||
m_paletteFileName = palette.value();
|
||||
m_startShell = shell.enabled();
|
||||
// m_dataFormat = dataFormat.value();
|
||||
m_data = data.value();
|
||||
// m_textureFormat = textureFormat.value();
|
||||
m_sheet = sheet.value();
|
||||
m_verbose = m_po.enabled(verbose);
|
||||
m_paletteFileName = m_po.value_of(palette);
|
||||
m_startShell = m_po.enabled(shell);
|
||||
// m_dataFormat = m_po.value_of(dataFormat);
|
||||
m_data = m_po.value_of(data);
|
||||
// m_textureFormat = m_po.value_of(textureFormat);
|
||||
m_sheet = m_po.value_of(sheet);
|
||||
// if (scale.enabled())
|
||||
// m_scale = std::strtod(scale.value().c_str(), NULL);
|
||||
// m_scaleMode = scaleMode.value();
|
||||
// m_scale = std::strtod(m_po.value_of(scale).c_str(), NULL);
|
||||
// m_scaleMode = m_po.value_of(scaleMode);
|
||||
|
||||
if (help.enabled()) {
|
||||
if (m_po.enabled(help)) {
|
||||
showHelp();
|
||||
m_startUI = false;
|
||||
}
|
||||
else if (version.enabled()) {
|
||||
else if (m_po.enabled(version)) {
|
||||
showVersion();
|
||||
m_startUI = false;
|
||||
}
|
||||
|
||||
if (shell.enabled() || batch.enabled()) {
|
||||
if (m_po.enabled(shell) || m_po.enabled(batch)) {
|
||||
m_startUI = false;
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
|
||||
const std::string& paletteFileName() const { return m_paletteFileName; }
|
||||
|
||||
const base::ProgramOptions::ValueList& files() const {
|
||||
const base::ProgramOptions::ValueList& values() const {
|
||||
return m_po.values();
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ void ProgramOptions::parse(int argc, const char* argv[])
|
||||
}
|
||||
|
||||
Option* option = *it;
|
||||
option->setEnabled(true);
|
||||
std::string optionValue;
|
||||
|
||||
if (option->doesRequireValue()) {
|
||||
if (usedBy != 0) {
|
||||
@ -104,9 +104,11 @@ void ProgramOptions::parse(int argc, const char* argv[])
|
||||
}
|
||||
|
||||
// Set the value specified for this argument
|
||||
option->setValue(argv[++i]);
|
||||
optionValue = argv[++i];
|
||||
usedBy = option->mnemonic();
|
||||
}
|
||||
|
||||
m_values.push_back(Value(option, optionValue));
|
||||
}
|
||||
}
|
||||
// Use name
|
||||
@ -133,7 +135,6 @@ void ProgramOptions::parse(int argc, const char* argv[])
|
||||
}
|
||||
|
||||
Option* option = *it;
|
||||
option->setEnabled(true);
|
||||
|
||||
if (option->doesRequireValue()) {
|
||||
// If the option was specified without '=', we can get the
|
||||
@ -147,15 +148,14 @@ void ProgramOptions::parse(int argc, const char* argv[])
|
||||
}
|
||||
optionValue = argv[++i];
|
||||
}
|
||||
|
||||
// Set the value specified for this argument
|
||||
option->setValue(optionValue);
|
||||
}
|
||||
|
||||
m_values.push_back(Value(option, optionValue));
|
||||
}
|
||||
}
|
||||
// Add values
|
||||
// Add values without a related option.
|
||||
else {
|
||||
m_values.push_back(arg);
|
||||
m_values.push_back(Value(NULL, arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,14 +163,24 @@ void ProgramOptions::parse(int argc, const char* argv[])
|
||||
void ProgramOptions::reset()
|
||||
{
|
||||
m_values.clear();
|
||||
for_each(m_options.begin(), m_options.end(), &ProgramOptions::resetOption);
|
||||
}
|
||||
|
||||
// static
|
||||
void ProgramOptions::resetOption(Option* option)
|
||||
bool ProgramOptions::enabled(const Option& option) const
|
||||
{
|
||||
option->setEnabled(false);
|
||||
option->setValue("");
|
||||
for (const auto& value : m_values) {
|
||||
if (value.option() == &option)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ProgramOptions::value_of(const Option& option) const
|
||||
{
|
||||
for (const auto& value : m_values) {
|
||||
if (value.option() == &option)
|
||||
return value.value();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
@ -39,16 +39,13 @@ namespace base {
|
||||
public:
|
||||
Option(const std::string& name)
|
||||
: m_name(name)
|
||||
, m_mnemonic(0)
|
||||
, m_enabled(false) {
|
||||
, m_mnemonic(0) {
|
||||
}
|
||||
// Getters
|
||||
const std::string& name() const { return m_name; }
|
||||
const std::string& description() const { return m_description; }
|
||||
const std::string& value() const { return m_value; }
|
||||
const std::string& getValueName() const { return m_valueName; }
|
||||
char mnemonic() const { return m_mnemonic; }
|
||||
bool enabled() const { return m_enabled; }
|
||||
bool doesRequireValue() const { return !m_valueName.empty(); }
|
||||
// Setters
|
||||
Option& description(const std::string& desc) { m_description = desc; return *this; }
|
||||
@ -58,21 +55,29 @@ namespace base {
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
void setValue(const std::string& value) { m_value = value; }
|
||||
void setEnabled(bool enabled) { m_enabled = enabled; }
|
||||
|
||||
std::string m_name; // Name of the option (e.g. "help" for "--help")
|
||||
std::string m_description; // Description of the option (this can be used when the help is printed).
|
||||
std::string m_value; // The value specified by the user in the command line.
|
||||
std::string m_valueName; // Empty if this option doesn't require a value, or the name of the expected value.
|
||||
char m_mnemonic; // One character that can be used in the command line to use this option.
|
||||
bool m_enabled; // True if the user specified this argument.
|
||||
|
||||
friend class ProgramOptions;
|
||||
};
|
||||
|
||||
class Value {
|
||||
public:
|
||||
Value(Option* option, const std::string& value)
|
||||
: m_option(option)
|
||||
, m_value(value) {
|
||||
}
|
||||
const Option* option() const { return m_option; }
|
||||
const std::string& value() const { return m_value; }
|
||||
private:
|
||||
Option* m_option;
|
||||
std::string m_value;
|
||||
};
|
||||
|
||||
typedef std::vector<Option*> OptionList;
|
||||
typedef std::vector<std::string> ValueList;
|
||||
typedef std::vector<Value> ValueList;
|
||||
|
||||
ProgramOptions();
|
||||
|
||||
@ -88,23 +93,19 @@ namespace base {
|
||||
// Detects which options where specified in the command line.
|
||||
void parse(int argc, const char* argv[]);
|
||||
|
||||
// Reset all options values/flags.
|
||||
// Reset all option values/flags.
|
||||
void reset();
|
||||
|
||||
// Returns the list of available options. To know the list of
|
||||
// specified options you can iterate this list asking for
|
||||
// Option::enabled() flag to know if the option was specified by
|
||||
// the user in the command line.
|
||||
// Returns the list of available options for the user.
|
||||
const OptionList& options() const { return m_options; }
|
||||
|
||||
// Returns the list of values that are not associated to any
|
||||
// options. E.g. a list of files specified in the command line to
|
||||
// be opened.
|
||||
// List of specified options/values in the command line.
|
||||
const ValueList& values() const { return m_values; }
|
||||
|
||||
private:
|
||||
static void resetOption(Option* option);
|
||||
bool enabled(const Option& option) const;
|
||||
std::string value_of(const Option& option) const;
|
||||
|
||||
private:
|
||||
OptionList m_options;
|
||||
ValueList m_values;
|
||||
};
|
||||
|
@ -21,13 +21,13 @@ TEST(ProgramOptions, OptionMembers)
|
||||
EXPECT_EQ("help", help.name());
|
||||
EXPECT_EQ("Show the help", help.description());
|
||||
EXPECT_EQ('h', help.mnemonic());
|
||||
EXPECT_FALSE(help.enabled());
|
||||
EXPECT_FALSE(po.enabled(help));
|
||||
EXPECT_FALSE(help.doesRequireValue());
|
||||
|
||||
EXPECT_EQ("output", output.name());
|
||||
EXPECT_EQ("", output.description());
|
||||
EXPECT_EQ('O', output.mnemonic());
|
||||
EXPECT_FALSE(output.enabled());
|
||||
EXPECT_FALSE(po.enabled(output));
|
||||
EXPECT_TRUE(output.doesRequireValue());
|
||||
}
|
||||
|
||||
@ -36,20 +36,20 @@ TEST(ProgramOptions, Reset)
|
||||
ProgramOptions po;
|
||||
ProgramOptions::Option& help = po.add("help");
|
||||
ProgramOptions::Option& file = po.add("file").requiresValue("FILE");
|
||||
EXPECT_FALSE(help.enabled());
|
||||
EXPECT_FALSE(file.enabled());
|
||||
EXPECT_EQ("", file.value());
|
||||
EXPECT_FALSE(po.enabled(help));
|
||||
EXPECT_FALSE(po.enabled(file));
|
||||
EXPECT_EQ("", po.value_of(file));
|
||||
|
||||
const char* argv[] = { "program.exe", "--help", "--file=readme.txt" };
|
||||
po.parse(3, argv);
|
||||
EXPECT_TRUE(help.enabled());
|
||||
EXPECT_TRUE(file.enabled());
|
||||
EXPECT_EQ("readme.txt", file.value());
|
||||
EXPECT_TRUE(po.enabled(help));
|
||||
EXPECT_TRUE(po.enabled(file));
|
||||
EXPECT_EQ("readme.txt", po.value_of(file));
|
||||
|
||||
po.reset();
|
||||
EXPECT_FALSE(help.enabled());
|
||||
EXPECT_FALSE(file.enabled());
|
||||
EXPECT_EQ("", file.value());
|
||||
EXPECT_FALSE(po.enabled(help));
|
||||
EXPECT_FALSE(po.enabled(file));
|
||||
EXPECT_EQ("", po.value_of(file));
|
||||
}
|
||||
|
||||
TEST(ProgramOptions, Parse)
|
||||
@ -61,50 +61,59 @@ TEST(ProgramOptions, Parse)
|
||||
|
||||
const char* argv1[] = { "program.exe", "-?" };
|
||||
po.parse(2, argv1);
|
||||
EXPECT_TRUE(help.enabled());
|
||||
EXPECT_TRUE(po.enabled(help));
|
||||
|
||||
const char* argv2[] = { "program.exe", "--help" };
|
||||
po.reset();
|
||||
po.parse(2, argv2);
|
||||
EXPECT_TRUE(help.enabled());
|
||||
EXPECT_TRUE(po.enabled(help));
|
||||
|
||||
const char* argv3[] = { "program.exe", "--input", "hello.cpp", "--output", "hello.exe" };
|
||||
po.reset();
|
||||
po.parse(5, argv3);
|
||||
EXPECT_FALSE(help.enabled());
|
||||
EXPECT_TRUE(input.enabled());
|
||||
EXPECT_TRUE(output.enabled());
|
||||
EXPECT_EQ("hello.cpp", input.value());
|
||||
EXPECT_EQ("hello.exe", output.value());
|
||||
EXPECT_FALSE(po.enabled(help));
|
||||
EXPECT_TRUE(po.enabled(input));
|
||||
EXPECT_TRUE(po.enabled(output));
|
||||
EXPECT_EQ("hello.cpp", po.value_of(input));
|
||||
EXPECT_EQ("hello.exe", po.value_of(output));
|
||||
|
||||
const char* argv4[] = { "program.exe", "--input=hi.c", "--output=out.exe" };
|
||||
po.reset();
|
||||
po.parse(3, argv4);
|
||||
EXPECT_FALSE(help.enabled());
|
||||
EXPECT_TRUE(input.enabled());
|
||||
EXPECT_TRUE(output.enabled());
|
||||
EXPECT_EQ("hi.c", input.value());
|
||||
EXPECT_EQ("out.exe", output.value());
|
||||
EXPECT_FALSE(po.enabled(help));
|
||||
EXPECT_TRUE(po.enabled(input));
|
||||
EXPECT_TRUE(po.enabled(output));
|
||||
EXPECT_EQ("hi.c", po.value_of(input));
|
||||
EXPECT_EQ("out.exe", po.value_of(output));
|
||||
|
||||
const char* argv5[] = { "program.exe", "-?i", "input.md", "-o", "output.html", "extra-file.txt" };
|
||||
po.reset();
|
||||
po.parse(6, argv5);
|
||||
EXPECT_TRUE(help.enabled());
|
||||
EXPECT_TRUE(input.enabled());
|
||||
EXPECT_TRUE(output.enabled());
|
||||
EXPECT_EQ("input.md", input.value());
|
||||
EXPECT_EQ("output.html", output.value());
|
||||
ASSERT_EQ(1, po.values().size());
|
||||
EXPECT_EQ("extra-file.txt", po.values()[0]);
|
||||
EXPECT_TRUE(po.enabled(help));
|
||||
EXPECT_TRUE(po.enabled(input));
|
||||
EXPECT_TRUE(po.enabled(output));
|
||||
EXPECT_EQ("input.md", po.value_of(input));
|
||||
EXPECT_EQ("output.html", po.value_of(output));
|
||||
ASSERT_EQ(4, po.values().size());
|
||||
EXPECT_EQ(&help, po.values()[0].option());
|
||||
EXPECT_EQ(&input, po.values()[1].option());
|
||||
EXPECT_EQ(&output, po.values()[2].option());
|
||||
EXPECT_EQ(NULL, po.values()[3].option());
|
||||
EXPECT_EQ("", po.values()[0].value());
|
||||
EXPECT_EQ("input.md", po.values()[1].value());
|
||||
EXPECT_EQ("output.html", po.values()[2].value());
|
||||
EXPECT_EQ("extra-file.txt", po.values()[3].value());
|
||||
|
||||
const char* argv6[] = { "program.exe", "value1", "value2", "-o", "output", "value3", "--input=input", "value4" };
|
||||
po.reset();
|
||||
po.parse(8, argv6);
|
||||
ASSERT_EQ(4, po.values().size());
|
||||
EXPECT_EQ("value1", po.values()[0]);
|
||||
EXPECT_EQ("value2", po.values()[1]);
|
||||
EXPECT_EQ("value3", po.values()[2]);
|
||||
EXPECT_EQ("value4", po.values()[3]);
|
||||
ASSERT_EQ(6, po.values().size());
|
||||
EXPECT_EQ("value1", po.values()[0].value());
|
||||
EXPECT_EQ("value2", po.values()[1].value());
|
||||
EXPECT_EQ("output", po.values()[2].value());
|
||||
EXPECT_EQ("value3", po.values()[3].value());
|
||||
EXPECT_EQ("input", po.values()[4].value());
|
||||
EXPECT_EQ("value4", po.values()[5].value());
|
||||
}
|
||||
|
||||
TEST(ProgramOptions, ParseErrors)
|
||||
@ -125,19 +134,19 @@ TEST(ProgramOptions, ParseErrors)
|
||||
|
||||
const char* argv4[] = { "program.exe", "-?a" };
|
||||
po.reset();
|
||||
EXPECT_FALSE(help.enabled());
|
||||
EXPECT_FALSE(po.enabled(help));
|
||||
EXPECT_THROW(po.parse(2, argv4), InvalidProgramOption);
|
||||
EXPECT_TRUE(help.enabled()); // -? is parsed anyway, -a is the invalid option
|
||||
EXPECT_TRUE(po.enabled(help)); // -? is parsed anyway, -a is the invalid option
|
||||
|
||||
const char* argv5[] = { "program.exe", "-io", "input-and-output.txt" };
|
||||
po.reset();
|
||||
EXPECT_THROW(po.parse(2, argv5), ProgramOptionNeedsValue);
|
||||
po.reset();
|
||||
EXPECT_THROW(po.parse(3, argv5), InvalidProgramOptionsCombination);
|
||||
EXPECT_TRUE(input.enabled());
|
||||
EXPECT_TRUE(output.enabled());
|
||||
EXPECT_EQ("input-and-output.txt", input.value());
|
||||
EXPECT_EQ("", output.value());
|
||||
EXPECT_TRUE(po.enabled(input));
|
||||
EXPECT_FALSE(po.enabled(output));
|
||||
EXPECT_EQ("input-and-output.txt", po.value_of(input));
|
||||
EXPECT_EQ("", po.value_of(output));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
@ -18,23 +18,24 @@ typedef base::ProgramOptions PO;
|
||||
static void run(int argc, const char* argv[])
|
||||
{
|
||||
PO po;
|
||||
PO::Option& inputFn = po.add("input").requiresValue("<filename>");
|
||||
PO::Option& inputOpt = po.add("input").requiresValue("<filename>");
|
||||
PO::Option& widgetId = po.add("widgetid").requiresValue("<filename>");
|
||||
po.parse(argc, argv);
|
||||
|
||||
// Try to load the XML file
|
||||
TiXmlDocument* doc = NULL;
|
||||
|
||||
if (inputFn.enabled()) {
|
||||
base::FileHandle inputFile(base::open_file(inputFn.value(), "rb"));
|
||||
std::string inputFilename = po.value_of(inputOpt);
|
||||
if (!inputFilename.empty()) {
|
||||
base::FileHandle inputFile(base::open_file(inputFilename, "rb"));
|
||||
doc = new TiXmlDocument();
|
||||
doc->SetValue(inputFn.value().c_str());
|
||||
doc->SetValue(inputFilename.c_str());
|
||||
if (!doc->LoadFile(inputFile))
|
||||
throw std::runtime_error("invalid input file");
|
||||
}
|
||||
|
||||
if (doc && widgetId.enabled())
|
||||
gen_ui_class(doc, inputFn.value(), widgetId.value());
|
||||
if (doc && po.enabled(widgetId))
|
||||
gen_ui_class(doc, inputFilename, po.value_of(widgetId));
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
|
@ -70,7 +70,7 @@ int app_main(int argc, char* argv[])
|
||||
she::ScopedHandle<she::System> system(she::create_system());
|
||||
MemLeak memleak;
|
||||
ui::GuiSystem guiSystem;
|
||||
app::App app(argc, const_cast<const char**>(argv));
|
||||
app::App app;
|
||||
|
||||
// Change the name of the memory dump file
|
||||
{
|
||||
@ -79,7 +79,9 @@ int app_main(int argc, char* argv[])
|
||||
memoryDump.setFileName(filename);
|
||||
}
|
||||
|
||||
return app.run();
|
||||
app.initialize(argc, const_cast<const char**>(argv));
|
||||
app.run();
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::cerr << e.what() << '\n';
|
||||
|
Loading…
Reference in New Issue
Block a user