1 : // Copyright 2012 Google Inc.
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 common::AppImplBase {
20 : // public:
21 : // bool ParseCommandLine(const 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 common::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 : // CommandLine::Init(argc, argv);
40 : // return common::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 common::Application;
49 : //
50 : // file_util::ScopedFILE in(file_util::OpenFile("NUL", "r"));
51 : // file_util::ScopedFILE out(file_util::OpenFile("NUL", "w"));
52 : // file_util::ScopedFILE err(file_util::OpenFile("NUL", "w"));
53 : // ASSERT_TRUE(in.get() != NULL);
54 : // ASSERT_TRUE(out.get() != NULL);
55 : // ASSERT_TRUE(err.get() != NULL);
56 : //
57 : // CommandLine cmd_line(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_COMMON_APPLICATION_H_
69 : #define SYZYGY_COMMON_APPLICATION_H_
70 :
71 : #include <objbase.h>
72 :
73 : #include "base/at_exit.h"
74 : #include "base/command_line.h"
75 : #include "base/logging.h"
76 : #include "base/string_number_conversions.h"
77 : #include "base/string_util.h"
78 : #include "sawbuck/common/com_utils.h"
79 :
80 : namespace common {
81 :
82 : // A convenience base class that describes the interface an application
83 : // implementation is expected to expose. This class provides empty default
84 : // method implementations.
85 : //
86 : // @note Each method is responsible for logging its own errors as it deems
87 : // appropriate. No log messages are otherwise generated if one of the
88 : // AppImplBase methods returns an error.
89 : class AppImplBase {
90 : public:
91 : // Initializes an application implementation with the standard IO streams.
92 : // Use the stream IO accessors to customize the IO streams.
93 : // @param name the name of the application.
94 : explicit AppImplBase(const base::StringPiece& name);
95 :
96 : // Parse the given command line in preparation for execution.
97 : bool ParseCommandLine(const CommandLine* command_line);
98 :
99 : // A hook called just before Run().
100 : bool SetUp();
101 :
102 : // The main logic for the application implementation.
103 : // @returns the exit status for the application.
104 : int Run();
105 :
106 : // A hook called just after Run().
107 : void TearDown();
108 :
109 : // Get the application name.
110 E : const std::string& name() const { return name_; }
111 :
112 : // @name IO Stream Accessors
113 : // @{
114 E : FILE* in() const { return in_; }
115 E : FILE* out() const { return out_; }
116 E : FILE* err() const { return err_; }
117 :
118 E : void set_in(FILE* f) {
119 E : DCHECK(f != NULL);
120 E : in_ = f;
121 E : }
122 :
123 E : void set_out(FILE* f) {
124 E : DCHECK(f != NULL);
125 E : out_ = f;
126 E : }
127 :
128 E : void set_err(FILE* f) {
129 E : DCHECK(f != NULL);
130 E : err_ = f;
131 E : }
132 : // @}
133 :
134 : // A helper function to return an absolute path (if possible) for the given
135 : // path. If the conversion to an absolute path fails, the original path is
136 : // returned.
137 : static FilePath AbsolutePath(const FilePath& path);
138 :
139 : protected:
140 : // The name of this application.
141 : std::string name_;
142 :
143 : // @name Standard file streams.
144 : // @{
145 : FILE* in_;
146 : FILE* out_;
147 : FILE* err_;
148 : // @}
149 : };
150 :
151 : // Flags controlling the initialization of the logging subsystem.
152 : enum AppLoggingFlag { INIT_LOGGING_NO, INIT_LOGGING_YES };
153 :
154 : // The Application template class.
155 : //
156 : // @tparam Implementation The class which implements the application logic.
157 : // @tparam kInitLogging Tracks whether or not the application should
158 : // (re-)initialize the logging subsystem on startup. Under testing,
159 : // for example, one might want to skip initializing the logging
160 : // subsystem.
161 : template <typename Impl, AppLoggingFlag kInitLogging = INIT_LOGGING_YES>
162 : class Application {
163 : public:
164 : // The application implementation class.
165 : typedef typename Impl Implementation;
166 :
167 : // Initializes the application with the current processes command line and
168 : // the standard IO streams.
169 : //
170 : // @pre CommandLine::Init() has been called prior to the creation of the
171 : // application object.
172 : Application();
173 :
174 : // Accessor for the underlying implementation.
175 E : Implementation& implementation() { return implementation_; }
176 :
177 : // @name Accessors for the command line.
178 : // @{
179 E : const CommandLine* command_line() const { return command_line_; }
180 :
181 E : void set_command_line(const CommandLine* command_line) {
182 E : DCHECK(command_line != NULL);
183 E : command_line_ = command_line;
184 E : }
185 : // @}
186 :
187 : // Get the application name.
188 E : const std::string& name() const { return implementation_.name(); }
189 :
190 : // The main skeleton for actually running an application.
191 : // @returns the exit status for the application.
192 : int Run();
193 :
194 : // @name IO Stream Accessors
195 : // @{
196 E : FILE* in() const { return implementation_.in(); }
197 E : FILE* out() const { return implementation_.out(); }
198 E : FILE* err() const { return implementation_.err(); }
199 :
200 E : void set_in(FILE* f) { implementation_.set_in(f); }
201 E : void set_out(FILE* f) { implementation_.set_out(f); }
202 E : void set_err(FILE* f) { implementation_.set_err(f); }
203 : // @}
204 :
205 : protected:
206 : // Initializes the logging subsystem for this application. This includes
207 : // checking the command line for the --verbose[=level] flag and handling
208 : // it appropriately.
209 : bool InitializeLogging();
210 :
211 : // The command line for this application. The referred instance must outlive
212 : // the application instance.
213 : const CommandLine* command_line_;
214 :
215 : // The implementation instance for this application. Execution will be
216 : // delegated to this object.
217 : Implementation implementation_;
218 :
219 : private:
220 : DISALLOW_COPY_AND_ASSIGN(Application);
221 : };
222 :
223 : // A helper class for timing an activity within a scope.
224 : class ScopedTimeLogger {
225 : public:
226 E : explicit ScopedTimeLogger(const char* label)
227 : : label_(label), start_(base::Time::Now()) {
228 E : DCHECK(label != NULL);
229 E : LOG(INFO) << label_ << ".";
230 E : }
231 :
232 E : ~ScopedTimeLogger() {
233 E : base::TimeDelta duration = base::Time::Now() - start_;
234 E : LOG(INFO) << label_ << " took " << duration.InSecondsF() << " seconds.";
235 E : }
236 :
237 : private:
238 : // A labeling phrase for the activity being timed.
239 : const char* const label_;
240 :
241 : // The time at which the activity began.
242 : const base::Time start_;
243 : };
244 :
245 : } // namespace common
246 :
247 : #include "syzygy/common/application_impl.h"
248 :
249 : #endif // SYZYGY_COMMON_APPLICATION_H_
|