1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // Declares a generic command-line application framework.
16 : //
17 : // An application can be declared as follows in a library:
18 : //
19 : // class MyApp : public application::AppImplBase {
20 : // public:
21 : // bool ParseCommandLine(const base::CommandLine* command_line);
22 : // int Run();
23 : // protected:
24 : // bool InternalFunc();
25 : // };
26 : //
27 : // The application class can then be unit-tested as appropriate. See the
28 : // declaration of application::AppImplBase for the entire interface expected
29 : // by the application framework. Note that derivation from AppImplBase
30 : // is optional, as the integration with the application framework is
31 : // by template expansion, not virtual function invocation; AppImplBase
32 : // is purely a convenience base class to allow you to elide defining
33 : // parts of the interface you don't need to specialize.
34 : //
35 : // The main() function for the executable can be reduced to:
36 : //
37 : // int main(int argc, const char* const* argv) {
38 : // base::AtExitManager at_exit_manager;
39 : // base::CommandLine::Init(argc, argv);
40 : // return application::Application<MyApp>().Run();
41 : // }
42 : //
43 : // To test how your application implementation interacts with the
44 : // application framework. You can run the application directly from
45 : // a unittest as follows:
46 : //
47 : // TEST(FixtureName, TestName) {
48 : // using application::Application;
49 : //
50 : // base::ScopedFILE in(base::OpenFile("NUL", "r"));
51 : // base::ScopedFILE out(base::OpenFile("NUL", "w"));
52 : // base::ScopedFILE err(base::OpenFile("NUL", "w"));
53 : // ASSERT_TRUE(in.get() != NULL);
54 : // ASSERT_TRUE(out.get() != NULL);
55 : // ASSERT_TRUE(err.get() != NULL);
56 : //
57 : // base::CommandLine cmd_line(base::FilePath(L"program"));
58 : // Application<MyTestApp, LOG_INIT_NO> test_app(&cmd_line,
59 : // in.get(),
60 : // out.get(),
61 : // err.get());
62 : //
63 : // ASSERT_TRUE(test_app.implementation().SomeFunc());
64 : // ASSERT_EQ(0, test_app.Run());
65 : // }
66 : //
67 :
68 : #ifndef SYZYGY_APPLICATION_APPLICATION_H_
69 : #define SYZYGY_APPLICATION_APPLICATION_H_
70 :
71 : #include <objbase.h>
72 : #include <vector>
73 :
74 : #include "base/at_exit.h"
75 : #include "base/command_line.h"
76 : #include "base/logging.h"
77 : #include "base/strings/string_number_conversions.h"
78 : #include "base/strings/string_util.h"
79 : #include "syzygy/common/com_utils.h"
80 :
81 : namespace application {
82 :
83 : // A convenience base class that describes the interface an application
84 : // implementation is expected to expose. This class provides empty default
85 : // method implementations.
86 : //
87 : // @note Each method is responsible for logging its own errors as it deems
88 : // appropriate. No log messages are otherwise generated if one of the
89 : // AppImplBase methods returns an error.
90 : class AppImplBase {
91 : public:
92 : // Initializes an application implementation with the standard IO streams.
93 : // Use the stream IO accessors to customize the IO streams.
94 : // @param name the name of the application.
95 : explicit AppImplBase(const base::StringPiece& name);
96 :
97 : // Parse the given command line in preparation for execution.
98 : bool ParseCommandLine(const base::CommandLine* command_line);
99 :
100 : // A hook called just before Run().
101 : bool SetUp();
102 :
103 : // The main logic for the application implementation.
104 : // @returns the exit status for the application.
105 : int Run();
106 :
107 : // A hook called just after Run().
108 : void TearDown();
109 :
110 : // Get the application name.
111 E : const std::string& name() const { return name_; }
112 :
113 : // @name IO Stream Accessors
114 : // @{
115 E : FILE* in() const { return in_; }
116 E : FILE* out() const { return out_; }
117 E : FILE* err() const { return err_; }
118 :
119 E : void set_in(FILE* f) {
120 E : DCHECK(f != NULL);
121 E : in_ = f;
122 E : }
123 :
124 E : void set_out(FILE* f) {
125 E : DCHECK(f != NULL);
126 E : out_ = f;
127 E : }
128 :
129 E : void set_err(FILE* f) {
130 E : DCHECK(f != NULL);
131 E : err_ = f;
132 E : }
133 : // @}
134 :
135 : // A helper function to return an absolute path (if possible) for the given
136 : // path. If the conversion to an absolute path fails, the original path is
137 : // returned.
138 : static base::FilePath AbsolutePath(const base::FilePath& path);
139 :
140 : // A helper function which appends the set of absolute file paths matching
141 : // the @p pattern (for example ..\foo\*.bin) to the end of @p matches.
142 : // @returns true if at least one matching file was found.
143 : static bool AppendMatchingPaths(const base::FilePath& pattern,
144 : std::vector<base::FilePath>* matches);
145 :
146 : // A helper function to get a command line parameter that has both a current
147 : // and a deprecated name.
148 : template <typename ValueType>
149 : static bool GetDeprecatedSwitch(
150 : const base::CommandLine* cmd_line,
151 : const std::string& current_switch_name,
152 : const std::string& deprecated_switch_name,
153 : ValueType (base::CommandLine::*getter)(const base::StringPiece&) const,
154 E : ValueType* value) {
155 E : DCHECK(cmd_line != NULL);
156 E : DCHECK(getter != NULL);
157 E : DCHECK(value != NULL);
158 E : if (cmd_line->HasSwitch(deprecated_switch_name)) {
159 E : if (cmd_line->HasSwitch(current_switch_name)) {
160 E : LOG(ERROR) << "Cannot specify both --" << current_switch_name
161 : << " and --" << deprecated_switch_name << ".";
162 E : return false;
163 : }
164 E : LOG(WARNING)
165 : << "Using deprecated switch: --" << deprecated_switch_name << ".";
166 E : *value = (cmd_line->*getter)(deprecated_switch_name);
167 E : } else {
168 E : *value = (cmd_line->*getter)(current_switch_name);
169 : }
170 E : return true;
171 E : }
172 :
173 : protected:
174 : // The name of this application.
175 : std::string name_;
176 :
177 : // @name Standard file streams.
178 : // @{
179 : FILE* in_;
180 : FILE* out_;
181 : FILE* err_;
182 : // @}
183 : };
184 :
185 : // Flags controlling the initialization of the logging subsystem.
186 : enum AppLoggingFlag { INIT_LOGGING_NO, INIT_LOGGING_YES };
187 :
188 : // The Application template class.
189 : //
190 : // @tparam Implementation The class which implements the application logic.
191 : // @tparam kInitLogging Tracks whether or not the application should
192 : // (re-)initialize the logging subsystem on startup. Under testing,
193 : // for example, one might want to skip initializing the logging
194 : // subsystem.
195 : template <typename Impl, AppLoggingFlag kInitLogging = INIT_LOGGING_YES>
196 : class Application {
197 : public:
198 : // The application implementation class.
199 : typedef typename Impl Implementation;
200 :
201 : // Initializes the application with the current processes command line and
202 : // the standard IO streams.
203 : //
204 : // @pre base::CommandLine::Init() has been called prior to the creation of the
205 : // application object.
206 : Application();
207 :
208 : // Accessor for the underlying implementation.
209 E : Implementation& implementation() { return implementation_; }
210 :
211 : // @name Accessors for the command line.
212 : // @{
213 E : const base::CommandLine* command_line() const { return command_line_; }
214 :
215 E : void set_command_line(const base::CommandLine* command_line) {
216 E : DCHECK(command_line != NULL);
217 E : command_line_ = command_line;
218 E : }
219 : // @}
220 :
221 : // Get the application name.
222 E : const std::string& name() const { return implementation_.name(); }
223 :
224 : // The main skeleton for actually running an application.
225 : // @returns the exit status for the application.
226 : int Run();
227 :
228 : // @name IO Stream Accessors
229 : // @{
230 E : FILE* in() const { return implementation_.in(); }
231 E : FILE* out() const { return implementation_.out(); }
232 E : FILE* err() const { return implementation_.err(); }
233 :
234 E : void set_in(FILE* f) { implementation_.set_in(f); }
235 E : void set_out(FILE* f) { implementation_.set_out(f); }
236 E : void set_err(FILE* f) { implementation_.set_err(f); }
237 : // @}
238 :
239 : protected:
240 : // Initializes the logging subsystem for this application. This includes
241 : // checking the command line for the --verbose[=level] flag and handling
242 : // it appropriately.
243 : bool InitializeLogging();
244 :
245 : // The command line for this application. The referred instance must outlive
246 : // the application instance.
247 : const base::CommandLine* command_line_;
248 :
249 : // The implementation instance for this application. Execution will be
250 : // delegated to this object.
251 : Implementation implementation_;
252 :
253 : private:
254 : DISALLOW_COPY_AND_ASSIGN(Application);
255 : };
256 :
257 : // A helper class for timing an activity within a scope.
258 : class ScopedTimeLogger {
259 : public:
260 E : explicit ScopedTimeLogger(const char* label)
261 : : label_(label), start_(base::Time::Now()) {
262 E : DCHECK(label != NULL);
263 E : LOG(INFO) << label_ << ".";
264 E : }
265 :
266 E : ~ScopedTimeLogger() {
267 E : base::TimeDelta duration = base::Time::Now() - start_;
268 E : LOG(INFO) << label_ << " took " << duration.InSecondsF() << " seconds.";
269 E : }
270 :
271 : private:
272 : // A labeling phrase for the activity being timed.
273 : const char* const label_;
274 :
275 : // The time at which the activity began.
276 : const base::Time start_;
277 : };
278 :
279 : } // namespace application
280 :
281 : #include "syzygy/application/application_impl.h"
282 :
283 : #endif // SYZYGY_APPLICATION_APPLICATION_H_
|