fw4spl
ProfileRunner.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2018.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #ifdef _WIN32
8 #include <windows.h>
9 #endif
10 
11 #include <fwRuntime/io/ProfileReader.hpp>
12 #include <fwRuntime/operations.hpp>
13 #include <fwRuntime/profile/Profile.hpp>
14 
15 #include <boost/filesystem.hpp>
16 #include <boost/program_options/options_description.hpp>
17 #include <boost/program_options/parsers.hpp>
18 #include <boost/program_options/positional_options.hpp>
19 #include <boost/program_options/variables_map.hpp>
20 
21 #include <csignal>
22 #include <ostream>
23 #include <stdio.h>
24 #include <string>
25 #include <vector>
26 
27 #ifdef _WIN32
28 # define CONSOLE_LOG false;
29 # define FILE_LOG true;
30 #else
31 # define CONSOLE_LOG true;
32 # define FILE_LOG false;
33 #endif
34 
35 #ifndef DEFAULT_PROFILE
36 #define DEFAULT_PROFILE profile.xml
37 #endif
38 
39 #define GET_DEFAULT_PROFILE(x) #x
40 #define GET_DEFAULT_PROFILE2(x) GET_DEFAULT_PROFILE(x)
41 #define DEFAULT_PROFILE_STRING GET_DEFAULT_PROFILE2(DEFAULT_PROFILE)
42 
43 //------------------------------------------------------------------------------
44 #if defined(_WIN32) && _MSC_VER > 1499 && _MSC_VER < 1600 // Visual C++ 2008 only
45 
46  #pragma message ( "Setting up manifest..." )
47 
48  #if defined(_DEBUG)
49 // add a dependency on the retail crt even in debug
50  #pragma comment(linker,"/manifestdependency:\"type='win32' " \
51  "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".CRT' " \
52  "version='" _CRT_ASSEMBLY_VERSION "' " \
53  "processorArchitecture='*' " \
54  "publicKeyToken='" _VC_ASSEMBLY_PUBLICKEYTOKEN "' " \
55  "language='*'\"")
56  #endif /* _DEBUG */
57 
58  #pragma comment(linker,"/manifestdependency:\"type='win32' " \
59  "name='Microsoft.Windows.Common-Controls' " \
60  "version='6.0.0.0' " \
61  "processorArchitecture='*' " \
62  "publicKeyToken='6595b64144ccf1df' " \
63  "language='*'\"")
64 
65 #endif /* _WIN32 && _MSC_VER > 1499 && _MSC_VER < 1600 */
66 
67 //------------------------------------------------------------------------------
68 namespace po = boost::program_options;
69 namespace fs = boost::filesystem;
70 
71 typedef fs::path PathType;
72 typedef std::vector< PathType > PathListType;
73 typedef std::vector< std::string > StringListType;
74 
75 namespace std
76 {
77 //------------------------------------------------------------------------------
78 
79 template<class A1, class A2>
80 inline ostream& operator<<(ostream& s, vector<A1, A2> const& vec)
81 {
82  copy(vec.begin(), vec.end(), ostream_iterator<A1>(s, " "));
83  return s;
84 }
85 }
86 
87 //-----------------------------------------------------------------------------
88 
89 #ifdef __MACOSX__
90 std::pair<std::string, std::string> parsePns(const std::string& s)
91 {
92  if (s.substr(0, 5) == "-psn_")
93  {
94  return std::make_pair(std::string("psn"), s.substr(5));
95  }
96  return std::make_pair(std::string(), std::string());
97 }
98 #endif
99 
102 PathType absolute( const PathType& path )
103 {
104  return fs::absolute(path).normalize();
105 }
106 
107 //-----------------------------------------------------------------------------
108 
109 volatile sig_atomic_t gSignalStatus = 0;
110 //------------------------------------------------------------------------------
111 
112 void signal_handler(int signal)
113 {
114  gSignalStatus = signal;
115  ::fwRuntime::profile::getCurrentProfile()->cleanup();
116  ::fwRuntime::profile::getCurrentProfile()->stop();
117 }
118 
119 //-----------------------------------------------------------------------------
120 
121 int main(int argc, char* argv[])
122 {
123  PathListType bundlePaths;
124  PathType rwd;
125  PathType profileFile;
126  ::fwRuntime::profile::Profile::ParamsContainer profileArgs;
127 
128  // Launcher options
129  po::options_description options("Launcher options");
130  options.add_options()
131  ("help,h", "Show help message")
132  ("bundle-path,B", po::value(&bundlePaths)->default_value
133  (PathListType(1, (::fwRuntime::Runtime::getDefault()->getWorkingPath() / BUNDLE_RC_PREFIX).string())),
134  "Adds a bundle path")
135  ("rwd", po::value(&rwd)->default_value("./"), "Sets runtime working directory")
136  ;
137 
138  // Log options
139  bool consoleLog = CONSOLE_LOG;
140  bool fileLog = FILE_LOG;
141  std::string logFile;
142  const std::string defaultLogFile = "SLM.log";
143 
144  typedef ::fwCore::log::SpyLogger SpyLogger;
145  int logLevel = SpyLogger::SL_TRACE;
146 
147  po::options_description logOptions("Log options");
148  logOptions.add_options()
149  ("clog", po::value(&consoleLog)->implicit_value(true)->zero_tokens(), "Enable log output to console")
150  ("no-clog", po::value(&consoleLog)->implicit_value(false)->zero_tokens(), "Disable log output to console")
151  ("flog", po::value(&fileLog)->implicit_value(true)->zero_tokens(), "Enable log output to file")
152  ("no-flog", po::value(&fileLog)->implicit_value(false)->zero_tokens(), "Disable log output to file")
153  ("log-output", po::value(&logFile)->default_value(defaultLogFile), "Log output filename")
154 
155  ("log-trace", po::value(&logLevel)->implicit_value(SpyLogger::SL_TRACE)->zero_tokens(), "Set loglevel to trace")
156  ("log-debug", po::value(&logLevel)->implicit_value(SpyLogger::SL_DEBUG)->zero_tokens(), "Set loglevel to debug")
157  ("log-info", po::value(&logLevel)->implicit_value(SpyLogger::SL_INFO )->zero_tokens(), "Set loglevel to info")
158  ("log-warn", po::value(&logLevel)->implicit_value(SpyLogger::SL_WARN )->zero_tokens(), "Set loglevel to warn")
159  ("log-error", po::value(&logLevel)->implicit_value(SpyLogger::SL_ERROR)->zero_tokens(), "Set loglevel to error")
160  ("log-fatal", po::value(&logLevel)->implicit_value(SpyLogger::SL_FATAL)->zero_tokens(), "Set loglevel to fatal")
161  ;
162 
163  // Hidden options
164  po::options_description hidden("Hidden options");
165  hidden.add_options()
166 #ifdef __MACOSX__
167  ("psn", po::value<std::string>(), "Application PSN number")
168  ("NSDocumentRevisionsDebugMode", po::value<std::string>()->zero_tokens(), "DocumentRevisionsDebugMode")
169 #endif
170  ("profile", po::value(&profileFile)->default_value(DEFAULT_PROFILE_STRING), "Profile file")
171  ("profile-args", po::value(&profileArgs)->multitoken(), "Profile args")
172  ;
173 
174  // Set options
175  po::options_description cmdline_options;
176  cmdline_options.add(options).add(logOptions).add(hidden);
177 
178  po::positional_options_description p;
179  p.add("profile", 1).add("profile-args", -1);
180 
181  // Get options
182  po::variables_map vm;
183 
184  try
185  {
186  po::store(po::command_line_parser(argc, argv)
187  .options(cmdline_options)
188 #ifdef __MACOSX__
189  .extra_parser(parsePns)
190 #endif
191  .positional(p)
192  .run(),
193  vm);
194  po::notify(vm);
195  }
196  catch(const po::error& e)
197  {
198  std::cerr << e.what() << std::endl;
199  return 1;
200  }
201 
202  // If help
203  if (vm.count("help"))
204  {
205  std::cout << "usage: " << argv[0] << " [options] [profile(=profile.xml)] [profile-args ...]" << std::endl;
206  std::cout << " use '--' to stop processing args for fwlauncher" << std::endl << std::endl;
207  std::cout << options << std::endl << logOptions << std::endl;
208  return 0;
209  }
210 
211  // Log file
212  SpyLogger& logger = fwCore::log::SpyLogger::getSpyLogger();
213 
214  if(consoleLog)
215  {
216  logger.addStreamAppender(std::clog, static_cast<SpyLogger::LevelType>(logLevel));
217  }
218 
219  if(fileLog)
220  {
221  FILE* pFile = fopen(logFile.c_str(), "w");
222  if (pFile == NULL)
223  {
224  ::boost::system::error_code err;
225  PathType sysTmp = fs::temp_directory_path(err);
226  if(err.value() != 0)
227  {
228  // replace log file appender by stream appender: default dir and temp dir unreachable
229  logger.addStreamAppender(std::clog, static_cast<SpyLogger::LevelType>(logLevel));
230  }
231  else
232  {
233  // creates SLM.log in temp directory: default dir unreachable
234  sysTmp = sysTmp / "SLM.log";
235  logFile = sysTmp.string();
236  logger.addFileAppender(logFile, static_cast<SpyLogger::LevelType>(logLevel));
237  }
238  }
239  else
240  {
241  // creates SLM.log in default logFile directory
242  logger.addFileAppender(logFile, static_cast<SpyLogger::LevelType>(logLevel));
243  }
244  fclose(pFile);
245  }
246 
247 #ifdef __MACOSX__
248  fs::path execPath = argv[0];
249 
250  if ( execPath.string().find(".app/") != std::string::npos || vm.count("psn"))
251  {
252  bool isChdirOkOSX = false;
253 
254  fs::path execPath = argv[0];
255 
256  while ( fs::extension(execPath) != ".app"
257  && execPath != execPath.parent_path()
258  && !fs::is_directory( execPath / fs::path(BUNDLE_RC_PREFIX))
259  )
260  {
261  execPath = execPath.parent_path();
262  }
263 
264  if ( fs::is_directory( execPath / "Contents" / fs::path(BUNDLE_RC_PREFIX) ) )
265  {
266  execPath = execPath / "Contents";
267  }
268  else
269  {
270  OSLM_ERROR_IF("Bundle directory not found.", !fs::is_directory( execPath / fs::path(BUNDLE_RC_PREFIX) ));
271  }
272 
273  isChdirOkOSX = (chdir(execPath.string().c_str()) == 0);
274 
275  SLM_ERROR_IF("Was not able to find a directory to change to.", !isChdirOkOSX);
276  }
277 #endif
278 
279  OSLM_INFO_IF( "Runtime working directory: " << rwd << " => " << ::absolute(rwd), vm.count("rwd") );
280  for(const fs::path& bundlePath : bundlePaths )
281  {
282  OSLM_INFO_IF( "Bundle paths are: " << bundlePath.string() << " => " << ::absolute(bundlePath),
283  vm.count("bundle-path") );
284  }
285  OSLM_INFO_IF( "Profile path: " << profileFile << " => " << ::absolute(profileFile), vm.count("profile"));
286  OSLM_INFO_IF( "Profile-args: " << profileArgs, vm.count("profile-args") );
287 
288  // Check if path exist
289  OSLM_FATAL_IF( "Runtime working directory doesn't exist: " << rwd.string() << " => " << ::absolute(
290  rwd), !::boost::filesystem::exists(rwd.string()) );
291  for(const fs::path& bundlePath : bundlePaths )
292  {
293  OSLM_FATAL_IF( "Bundle paths doesn't exist: " << bundlePath.string() << " => " << ::absolute(
294  bundlePath), !::boost::filesystem::exists(bundlePath.string()) );
295  }
296  OSLM_FATAL_IF( "Profile path doesn't exist: " << profileFile.string() << " => " << ::absolute(
297  profileFile), !::boost::filesystem::exists(profileFile.string()));
298 
299  std::transform( bundlePaths.begin(), bundlePaths.end(), bundlePaths.begin(), ::absolute );
300  profileFile = ::absolute(profileFile);
301  rwd = ::absolute(rwd);
302 
303 #ifdef _WIN32
304  const bool isChdirOk = static_cast<bool>(SetCurrentDirectory(rwd.string().c_str()) != 0);
305 #else
306  const bool isChdirOk = ( chdir(rwd.string().c_str()) == 0 );
307 #endif // _WIN32
308  OSLM_FATAL_IF( "Was not able to change directory to : " << rwd, !isChdirOk);
309 
310  for(const fs::path& bundlePath : bundlePaths )
311  {
312  if ( fs::is_directory(bundlePath))
313  {
314  ::fwRuntime::addBundles( bundlePath );
315  }
316  else
317  {
318  OSLM_ERROR( "Bundle path " << bundlePath << " do not exists or is not a directory.");
319  }
320  }
321 
322  int retValue = 0;
323 
324  if ( fs::is_regular_file(profileFile))
325  {
326  ::fwRuntime::profile::Profile::sptr profile;
327 
328  try
329  {
330  profile = ::fwRuntime::io::ProfileReader::createProfile(profileFile);
331  ::fwRuntime::profile::setCurrentProfile(profile);
332 
333  // Install a signal handler
334  std::signal(SIGINT, signal_handler);
335 
336  profile->setParams(profileArgs);
337 
338  profile->start();
339  profile->run();
340  if(gSignalStatus == 0)
341  {
342  profile->stop();
343  }
344  }
345  catch(std::exception& e)
346  {
347  OSLM_FATAL( e.what() );
348  retValue = 1;
349  }
350  catch(...)
351  {
352  SLM_FATAL( "An unrecoverable error has occurred." );
353  retValue = 1;
354  }
355  }
356  else
357  {
358  OSLM_ERROR( "Profile file " << profileFile << " do not exists or is not a regular file.");
359  retValue = 1;
360  }
361 
362  return retValue;
363 }
364 
365 //-----------------------------------------------------------------------------
366 
367 #ifdef _WIN32
368 int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR args, int)
369 {
370  return main(__argc, __argv);
371 }
372 #endif // _WIN32
static FWRUNTIME_API Runtime * getDefault()
Retrieves the default runtime instance.
Definition: Runtime.cpp:286
STL namespace.
#define OSLM_INFO_IF(message, cond)
Definition: spyLog.hpp:256
#define SLM_FATAL(message)
Definition: spyLog.hpp:283
#define OSLM_ERROR(message)
Definition: spyLog.hpp:274
#define SLM_ERROR_IF(message, cond)
Definition: spyLog.hpp:276
#define OSLM_FATAL(message)
Definition: spyLog.hpp:285
static FWRUNTIME_API std::shared_ptr< ::fwRuntime::profile::Profile > createProfile(const boost::filesystem::path &path)
Creates a profile from an xml file located at the given path.
#define OSLM_FATAL_IF(message, cond)
Definition: spyLog.hpp:289
#define OSLM_ERROR_IF(message, cond)
Definition: spyLog.hpp:278