Loading...
Searching...
No Matches
juce_ConsoleApplication.cpp
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
static File resolveFilename (const String& name)
{
return File::getCurrentWorkingDirectory().getChildFile (name.unquoted());
}
static File checkFileExists (const File& f)
{
if (! f.exists())
ConsoleApplication::fail ("Could not find file: " + f.getFullPathName());
return f;
}
static File checkFolderExists (const File& f)
{
if (! f.isDirectory())
ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
return f;
}
static File resolveFilenameForOption (const ArgumentList& args, StringRef option, const String& filename)
{
if (filename.isEmpty())
{
args.failIfOptionIsMissing (option);
ConsoleApplication::fail ("Expected a filename after the " + option + " option");
}
return resolveFilename (filename);
}
File ArgumentList::Argument::resolveAsFile() const
{
return resolveFilename (text);
}
File ArgumentList::Argument::resolveAsExistingFile() const
{
return checkFileExists (resolveAsFile());
}
File ArgumentList::Argument::resolveAsExistingFolder() const
{
auto f = resolveAsFile();
if (! f.isDirectory())
ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
return f;
}
static bool isShortOptionFormat (StringRef s) { return s[0] == '-' && s[1] != '-'; }
static bool isLongOptionFormat (StringRef s) { return s[0] == '-' && s[1] == '-' && s[2] != '-'; }
static bool isOptionFormat (StringRef s) { return s[0] == '-'; }
bool ArgumentList::Argument::isLongOption() const { return isLongOptionFormat (text); }
bool ArgumentList::Argument::isShortOption() const { return isShortOptionFormat (text); }
bool ArgumentList::Argument::isOption() const { return isOptionFormat (text); }
bool ArgumentList::Argument::isLongOption (const String& option) const
{
if (! isLongOptionFormat (option))
{
jassert (! isShortOptionFormat (option)); // this will always fail to match
return isLongOption ("--" + option);
}
return text.upToFirstOccurrenceOf ("=", false, false) == option;
}
String ArgumentList::Argument::getLongOptionValue() const
{
if (isLongOption())
{
auto equalsIndex = text.indexOfChar ('=');
if (equalsIndex > 0)
return text.substring (equalsIndex + 1);
}
return {};
}
bool ArgumentList::Argument::isShortOption (char option) const
{
jassert (option != '-'); // this is probably not what you intended to pass in
return isShortOption() && text.containsChar (String (option)[0]);
}
bool ArgumentList::Argument::operator== (StringRef wildcard) const
{
for (auto& o : StringArray::fromTokens (wildcard, "|", {}))
{
if (text == o)
return true;
if (isShortOptionFormat (o) && o.length() == 2 && isShortOption ((char) o[1]))
return true;
if (isLongOptionFormat (o) && isLongOption (o))
return true;
}
return false;
}
bool ArgumentList::Argument::operator!= (StringRef s) const { return ! operator== (s); }
//==============================================================================
ArgumentList::ArgumentList (String exeName, StringArray args)
: executableName (std::move (exeName))
{
args.trim();
args.removeEmptyStrings();
for (auto& a : args)
arguments.add ({ a.unquoted() });
}
ArgumentList::ArgumentList (int argc, char* argv[])
: ArgumentList (argv[0], StringArray (argv + 1, argc - 1))
{
}
ArgumentList::ArgumentList (const String& exeName, const String& args)
: ArgumentList (exeName, StringArray::fromTokens (args, true))
{
}
int ArgumentList::size() const { return arguments.size(); }
ArgumentList::Argument ArgumentList::operator[] (int index) const { return arguments[index]; }
void ArgumentList::checkMinNumArguments (int expectedMinNumberOfArgs) const
{
if (size() < expectedMinNumberOfArgs)
ConsoleApplication::fail ("Not enough arguments!");
}
int ArgumentList::indexOfOption (StringRef option) const
{
jassert (option == String (option).trim()); // passing non-trimmed strings will always fail to find a match!
for (int i = 0; i < arguments.size(); ++i)
if (arguments.getReference (i) == option)
return i;
return -1;
}
bool ArgumentList::containsOption (StringRef option) const
{
return indexOfOption (option) >= 0;
}
bool ArgumentList::removeOptionIfFound (StringRef option)
{
auto i = indexOfOption (option);
if (i >= 0)
arguments.remove (i);
return i >= 0;
}
void ArgumentList::failIfOptionIsMissing (StringRef option) const
{
if (indexOfOption (option) < 0)
ConsoleApplication::fail ("Expected the option " + option);
}
String ArgumentList::getValueForOption (StringRef option) const
{
jassert (isOptionFormat (option)); // the thing you're searching for must be an option
for (int i = 0; i < arguments.size(); ++i)
{
auto& arg = arguments.getReference (i);
if (arg == option)
{
if (arg.isShortOption())
{
if (i < arguments.size() - 1 && ! arguments.getReference (i + 1).isOption())
return arguments.getReference (i + 1).text;
return {};
}
if (arg.isLongOption())
return arg.getLongOptionValue();
}
}
return {};
}
String ArgumentList::removeValueForOption (StringRef option)
{
jassert (isOptionFormat (option)); // the thing you're searching for must be an option
for (int i = 0; i < arguments.size(); ++i)
{
auto& arg = arguments.getReference (i);
if (arg == option)
{
if (arg.isShortOption())
{
if (i < arguments.size() - 1 && ! arguments.getReference (i + 1).isOption())
{
auto result = arguments.getReference (i + 1).text;
arguments.removeRange (i, 2);
return result;
}
arguments.remove (i);
return {};
}
if (arg.isLongOption())
{
auto result = arg.getLongOptionValue();
arguments.remove (i);
return result;
}
}
}
return {};
}
File ArgumentList::getFileForOption (StringRef option) const
{
return resolveFilenameForOption (*this, option, getValueForOption (option));
}
File ArgumentList::getFileForOptionAndRemove (StringRef option)
{
return resolveFilenameForOption (*this, option, removeValueForOption (option));
}
File ArgumentList::getExistingFileForOption (StringRef option) const
{
return checkFileExists (getFileForOption (option));
}
File ArgumentList::getExistingFileForOptionAndRemove (StringRef option)
{
return checkFileExists (getFileForOptionAndRemove (option));
}
File ArgumentList::getExistingFolderForOption (StringRef option) const
{
return checkFolderExists (getFileForOption (option));
}
File ArgumentList::getExistingFolderForOptionAndRemove (StringRef option)
{
return checkFolderExists (getFileForOptionAndRemove (option));
}
//==============================================================================
struct ConsoleAppFailureCode
{
String errorMessage;
int returnCode;
};
void ConsoleApplication::fail (String errorMessage, int returnCode)
{
throw ConsoleAppFailureCode { std::move (errorMessage), returnCode };
}
int ConsoleApplication::invokeCatchingFailures (std::function<int()>&& f)
{
int returnCode = 0;
try
{
returnCode = f();
}
catch (const ConsoleAppFailureCode& error)
{
std::cerr << error.errorMessage << std::endl;
returnCode = error.returnCode;
}
return returnCode;
}
const ConsoleApplication::Command* ConsoleApplication::findCommand (const ArgumentList& args, bool optionMustBeFirstArg) const
{
for (auto& c : commands)
{
auto index = args.indexOfOption (c.commandOption);
if (optionMustBeFirstArg ? (index == 0) : (index >= 0))
return &c;
}
if (commandIfNoOthersRecognised >= 0)
return &commands[(size_t) commandIfNoOthersRecognised];
return {};
}
int ConsoleApplication::findAndRunCommand (const ArgumentList& args, bool optionMustBeFirstArg) const
{
return invokeCatchingFailures ([&args, optionMustBeFirstArg, this]
{
if (auto c = findCommand (args, optionMustBeFirstArg))
c->command (args);
else
fail ("Unrecognised arguments");
return 0;
});
}
int ConsoleApplication::findAndRunCommand (int argc, char* argv[]) const
{
return findAndRunCommand (ArgumentList (argc, argv));
}
void ConsoleApplication::addCommand (Command c)
{
commands.emplace_back (std::move (c));
}
void ConsoleApplication::addDefaultCommand (Command c)
{
commandIfNoOthersRecognised = (int) commands.size();
addCommand (std::move (c));
}
void ConsoleApplication::addHelpCommand (String arg, String helpMessage, bool makeDefaultCommand)
{
Command c { arg, arg, "Prints the list of commands", {},
[this, helpMessage] (const ArgumentList& args)
{
std::cout << helpMessage << std::endl;
printCommandList (args);
}};
if (makeDefaultCommand)
addDefaultCommand (std::move (c));
else
addCommand (std::move (c));
}
void ConsoleApplication::addVersionCommand (String arg, String versionText)
{
addCommand ({ arg, arg, "Prints the current version number", {},
[versionText] (const ArgumentList&)
{
std::cout << versionText << std::endl;
}});
}
const std::vector<ConsoleApplication::Command>& ConsoleApplication::getCommands() const
{
return commands;
}
static String getExeNameAndArgs (const ArgumentList& args, const ConsoleApplication::Command& command)
{
auto exeName = args.executableName.fromLastOccurrenceOf ("/", false, false)
.fromLastOccurrenceOf ("\\", false, false);
return " " + exeName + " " + command.argumentDescription;
}
static void printCommandDescription (const ArgumentList& args, const ConsoleApplication::Command& command,
int descriptionIndent)
{
auto nameAndArgs = getExeNameAndArgs (args, command);
if (nameAndArgs.length() > descriptionIndent)
std::cout << nameAndArgs << std::endl << String().paddedRight (' ', descriptionIndent);
else
std::cout << nameAndArgs.paddedRight (' ', descriptionIndent);
std::cout << command.shortDescription << std::endl;
}
void ConsoleApplication::printCommandList (const ArgumentList& args) const
{
int descriptionIndent = 0;
for (auto& c : commands)
descriptionIndent = std::max (descriptionIndent, getExeNameAndArgs (args, c).length());
descriptionIndent = std::min (descriptionIndent + 2, 40);
for (auto& c : commands)
printCommandDescription (args, c, descriptionIndent);
std::cout << std::endl;
}
void ConsoleApplication::printCommandDetails (const ArgumentList& args, const Command& command) const
{
auto len = getExeNameAndArgs (args, command).length();
printCommandDescription (args, command, std::min (len + 3, 40));
if (command.longDescription.isNotEmpty())
std::cout << std::endl << command.longDescription << std::endl;
}
} // namespace juce
juce::File
Definition juce_File.h:45
juce::File::getChildFile
File getChildFile(StringRef relativeOrAbsolutePath) const
Definition juce_File.cpp:420
juce::File::getCurrentWorkingDirectory
static File getCurrentWorkingDirectory()
juce::StringArray
Definition juce_StringArray.h:35
juce::StringArray::removeEmptyStrings
void removeEmptyStrings(bool removeWhitespaceStrings=true)
Definition juce_StringArray.cpp:250
juce::StringArray::fromTokens
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Definition juce_StringArray.cpp:387
juce::StringArray::trim
void trim()
Definition juce_StringArray.cpp:266
juce::StringRef
Definition juce_StringRef.h:62
juce::String
Definition juce_String.h:53
juce::String::fromLastOccurrenceOf
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1575
juce::String::isNotEmpty
bool isNotEmpty() const noexcept
Definition juce_String.h:316
juce::ArgumentList::Argument
Definition juce_ConsoleApplication.h:59
juce::ArgumentList::Argument::operator!=
bool operator!=(StringRef stringToCompare) const
Definition juce_ConsoleApplication.cpp:134
juce::ArgumentList::Argument::resolveAsExistingFile
File resolveAsExistingFile() const
Definition juce_ConsoleApplication.cpp:63
juce::ArgumentList::Argument::text
String text
Definition juce_ConsoleApplication.h:61
juce::ArgumentList::Argument::isLongOption
bool isLongOption() const
Definition juce_ConsoleApplication.cpp:82
juce::ArgumentList::Argument::isLongOption
bool isLongOption(const String &optionRoot) const
Definition juce_ConsoleApplication.cpp:86
juce::ArgumentList::Argument::resolveAsExistingFolder
File resolveAsExistingFolder() const
Definition juce_ConsoleApplication.cpp:68
juce::ArgumentList::Argument::isShortOption
bool isShortOption() const
Definition juce_ConsoleApplication.cpp:83
juce::ArgumentList::Argument::resolveAsFile
File resolveAsFile() const
Definition juce_ConsoleApplication.cpp:58
juce::ArgumentList::Argument::operator==
bool operator==(StringRef stringToCompare) const
Definition juce_ConsoleApplication.cpp:117
juce::ArgumentList::Argument::getLongOptionValue
String getLongOptionValue() const
Definition juce_ConsoleApplication.cpp:97
juce::ArgumentList::Argument::isOption
bool isOption() const
Definition juce_ConsoleApplication.cpp:84
juce::ArgumentList
Definition juce_ConsoleApplication.h:39
juce::ArgumentList::getExistingFileForOptionAndRemove
File getExistingFileForOptionAndRemove(StringRef option)
Definition juce_ConsoleApplication.cpp:274
juce::ArgumentList::removeValueForOption
String removeValueForOption(StringRef option)
Definition juce_ConsoleApplication.cpp:224
juce::ArgumentList::getExistingFileForOption
File getExistingFileForOption(StringRef option) const
Definition juce_ConsoleApplication.cpp:269
juce::ArgumentList::removeOptionIfFound
bool removeOptionIfFound(StringRef option)
Definition juce_ConsoleApplication.cpp:182
juce::ArgumentList::getExistingFolderForOption
File getExistingFolderForOption(StringRef option) const
Definition juce_ConsoleApplication.cpp:279
juce::ArgumentList::indexOfOption
int indexOfOption(StringRef option) const
Definition juce_ConsoleApplication.cpp:166
juce::ArgumentList::executableName
String executableName
Definition juce_ConsoleApplication.h:200
juce::ArgumentList::getFileForOption
File getFileForOption(StringRef option) const
Definition juce_ConsoleApplication.cpp:259
juce::ArgumentList::failIfOptionIsMissing
void failIfOptionIsMissing(StringRef option) const
Definition juce_ConsoleApplication.cpp:192
juce::ArgumentList::arguments
Array< Argument > arguments
Definition juce_ConsoleApplication.h:203
juce::ArgumentList::containsOption
bool containsOption(StringRef option) const
Definition juce_ConsoleApplication.cpp:177
juce::ArgumentList::checkMinNumArguments
void checkMinNumArguments(int expectedMinNumberOfArgs) const
Definition juce_ConsoleApplication.cpp:160
juce::ArgumentList::getValueForOption
String getValueForOption(StringRef option) const
Definition juce_ConsoleApplication.cpp:198
juce::ArgumentList::operator[]
Argument operator[](int index) const
Definition juce_ConsoleApplication.cpp:158
juce::ArgumentList::size
int size() const
Definition juce_ConsoleApplication.cpp:157
juce::ArgumentList::ArgumentList
ArgumentList(String executable, StringArray arguments)
Definition juce_ConsoleApplication.cpp:137
juce::ArgumentList::getFileForOptionAndRemove
File getFileForOptionAndRemove(StringRef option)
Definition juce_ConsoleApplication.cpp:264
juce::ArgumentList::getExistingFolderForOptionAndRemove
File getExistingFolderForOptionAndRemove(StringRef option)
Definition juce_ConsoleApplication.cpp:284
juce::ConsoleApplication::printCommandDetails
void printCommandDetails(const ArgumentList &, const Command &) const
Definition juce_ConsoleApplication.cpp:428
juce::ConsoleApplication::findAndRunCommand
int findAndRunCommand(const ArgumentList &, bool optionMustBeFirstArg=false) const
Definition juce_ConsoleApplication.cpp:334
juce::ConsoleApplication::addDefaultCommand
void addDefaultCommand(Command)
Definition juce_ConsoleApplication.cpp:357
juce::ConsoleApplication::Command::argumentDescription
String argumentDescription
Definition juce_ConsoleApplication.h:256
juce::ConsoleApplication::printCommandList
void printCommandList(const ArgumentList &) const
Definition juce_ConsoleApplication.cpp:413
juce::ConsoleApplication::findCommand
const Command * findCommand(const ArgumentList &, bool optionMustBeFirstArg) const
Definition juce_ConsoleApplication.cpp:318
juce::ConsoleApplication::addCommand
void addCommand(Command)
Definition juce_ConsoleApplication.cpp:352
juce::ConsoleApplication::invokeCatchingFailures
static int invokeCatchingFailures(std::function< int()> &&functionToCall)
Definition juce_ConsoleApplication.cpp:301
juce::ConsoleApplication::addHelpCommand
void addHelpCommand(String helpArgument, String helpMessage, bool makeDefaultCommand)
Definition juce_ConsoleApplication.cpp:363
juce::ConsoleApplication::addVersionCommand
void addVersionCommand(String versionArgument, String versionText)
Definition juce_ConsoleApplication.cpp:378
juce::ConsoleApplication::fail
static void fail(String errorMessage, int returnCode=1)
Definition juce_ConsoleApplication.cpp:296
juce::ConsoleApplication::getCommands
const std::vector< Command > & getCommands() const
Definition juce_ConsoleApplication.cpp:387
juce::ConsoleApplication::Command::longDescription
String longDescription
Definition juce_ConsoleApplication.h:264
juce::ConsoleApplication::Command
Definition juce_ConsoleApplication.h:247