blob: 6c216a047d19ef4ff9c8d34ce78db5cfd7670614 [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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.
//
// Declares a generic command-line application framework.
//
// An application can be declared as follows in a library:
//
// class MyApp : public application::AppImplBase {
// public:
// bool ParseCommandLine(const base::CommandLine* command_line);
// int Run();
// protected:
// bool InternalFunc();
// };
//
// The application class can then be unit-tested as appropriate. See the
// declaration of application::AppImplBase for the entire interface expected
// by the application framework. Note that derivation from AppImplBase
// is optional, as the integration with the application framework is
// by template expansion, not virtual function invocation; AppImplBase
// is purely a convenience base class to allow you to elide defining
// parts of the interface you don't need to specialize.
//
// The main() function for the executable can be reduced to:
//
// int main(int argc, const char* const* argv) {
// base::AtExitManager at_exit_manager;
// base::CommandLine::Init(argc, argv);
// return application::Application<MyApp>().Run();
// }
//
// To test how your application implementation interacts with the
// application framework. You can run the application directly from
// a unittest as follows:
//
// TEST(FixtureName, TestName) {
// using application::Application;
//
// base::ScopedFILE in(base::OpenFile("NUL", "r"));
// base::ScopedFILE out(base::OpenFile("NUL", "w"));
// base::ScopedFILE err(base::OpenFile("NUL", "w"));
// ASSERT_TRUE(in.get() != NULL);
// ASSERT_TRUE(out.get() != NULL);
// ASSERT_TRUE(err.get() != NULL);
//
// base::CommandLine cmd_line(base::FilePath(L"program"));
// Application<MyTestApp, LOG_INIT_NO> test_app(&cmd_line,
// in.get(),
// out.get(),
// err.get());
//
// ASSERT_TRUE(test_app.implementation().SomeFunc());
// ASSERT_EQ(0, test_app.Run());
// }
//
#ifndef SYZYGY_APPLICATION_APPLICATION_H_
#define SYZYGY_APPLICATION_APPLICATION_H_
#include <objbase.h>
#include <vector>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "syzygy/common/com_utils.h"
namespace application {
// A convenience base class that describes the interface an application
// implementation is expected to expose. This class provides empty default
// method implementations.
//
// @note Each method is responsible for logging its own errors as it deems
// appropriate. No log messages are otherwise generated if one of the
// AppImplBase methods returns an error.
class AppImplBase {
public:
// Initializes an application implementation with the standard IO streams.
// Use the stream IO accessors to customize the IO streams.
// @param name the name of the application.
explicit AppImplBase(const base::StringPiece& name);
// Parse the given command line in preparation for execution.
bool ParseCommandLine(const base::CommandLine* command_line);
// A hook called just before Run().
bool SetUp();
// The main logic for the application implementation.
// @returns the exit status for the application.
int Run();
// A hook called just after Run().
void TearDown();
// Get the application name.
const std::string& name() const { return name_; }
// @name IO Stream Accessors
// @{
FILE* in() const { return in_; }
FILE* out() const { return out_; }
FILE* err() const { return err_; }
void set_in(FILE* f) {
DCHECK(f != NULL);
in_ = f;
}
void set_out(FILE* f) {
DCHECK(f != NULL);
out_ = f;
}
void set_err(FILE* f) {
DCHECK(f != NULL);
err_ = f;
}
// @}
// A helper function to return an absolute path (if possible) for the given
// path. If the conversion to an absolute path fails, the original path is
// returned.
static base::FilePath AbsolutePath(const base::FilePath& path);
// A helper function which appends the set of absolute file paths matching
// the @p pattern (for example ..\foo\*.bin) to the end of @p matches.
// @returns true if at least one matching file was found.
static bool AppendMatchingPaths(const base::FilePath& pattern,
std::vector<base::FilePath>* matches);
// A helper function to get a command line parameter that has both a current
// and a deprecated name.
template <typename ValueType>
static bool GetDeprecatedSwitch(
const base::CommandLine* cmd_line,
const std::string& current_switch_name,
const std::string& deprecated_switch_name,
ValueType (base::CommandLine::*getter)(const base::StringPiece&) const,
ValueType* value) {
DCHECK(cmd_line != NULL);
DCHECK(getter != NULL);
DCHECK(value != NULL);
if (cmd_line->HasSwitch(deprecated_switch_name)) {
if (cmd_line->HasSwitch(current_switch_name)) {
LOG(ERROR) << "Cannot specify both --" << current_switch_name
<< " and --" << deprecated_switch_name << ".";
return false;
}
LOG(WARNING)
<< "Using deprecated switch: --" << deprecated_switch_name << ".";
*value = (cmd_line->*getter)(deprecated_switch_name);
} else {
*value = (cmd_line->*getter)(current_switch_name);
}
return true;
}
protected:
// The name of this application.
std::string name_;
// @name Standard file streams.
// @{
FILE* in_;
FILE* out_;
FILE* err_;
// @}
};
// Flags controlling the initialization of the logging subsystem.
enum AppLoggingFlag { INIT_LOGGING_NO, INIT_LOGGING_YES };
// The Application template class.
//
// @tparam Implementation The class which implements the application logic.
// @tparam kInitLogging Tracks whether or not the application should
// (re-)initialize the logging subsystem on startup. Under testing,
// for example, one might want to skip initializing the logging
// subsystem.
template <typename Impl, AppLoggingFlag kInitLogging = INIT_LOGGING_YES>
class Application {
public:
// The application implementation class.
typedef typename Impl Implementation;
// Initializes the application with the current processes command line and
// the standard IO streams.
//
// @pre base::CommandLine::Init() has been called prior to the creation of the
// application object.
Application();
// Accessor for the underlying implementation.
Implementation& implementation() { return implementation_; }
// @name Accessors for the command line.
// @{
const base::CommandLine* command_line() const { return command_line_; }
void set_command_line(const base::CommandLine* command_line) {
DCHECK(command_line != NULL);
command_line_ = command_line;
}
// @}
// Get the application name.
const std::string& name() const { return implementation_.name(); }
// The main skeleton for actually running an application.
// @returns the exit status for the application.
int Run();
// @name IO Stream Accessors
// @{
FILE* in() const { return implementation_.in(); }
FILE* out() const { return implementation_.out(); }
FILE* err() const { return implementation_.err(); }
void set_in(FILE* f) { implementation_.set_in(f); }
void set_out(FILE* f) { implementation_.set_out(f); }
void set_err(FILE* f) { implementation_.set_err(f); }
// @}
protected:
// Initializes the logging subsystem for this application. This includes
// checking the command line for the --verbose[=level] flag and handling
// it appropriately.
bool InitializeLogging();
// The command line for this application. The referred instance must outlive
// the application instance.
const base::CommandLine* command_line_;
// The implementation instance for this application. Execution will be
// delegated to this object.
Implementation implementation_;
private:
DISALLOW_COPY_AND_ASSIGN(Application);
};
// A helper class for timing an activity within a scope.
class ScopedTimeLogger {
public:
explicit ScopedTimeLogger(const char* label)
: label_(label), start_(base::Time::Now()) {
DCHECK(label != NULL);
LOG(INFO) << label_ << ".";
}
~ScopedTimeLogger() {
base::TimeDelta duration = base::Time::Now() - start_;
LOG(INFO) << label_ << " took " << duration.InSecondsF() << " seconds.";
}
private:
// A labeling phrase for the activity being timed.
const char* const label_;
// The time at which the activity began.
const base::Time start_;
};
} // namespace application
#include "syzygy/application/application_impl.h"
#endif // SYZYGY_APPLICATION_APPLICATION_H_