4 /// @file run-single-task.C
5 /// @author Matthias.Richter@ift.uib.no
7 /// @brief Run a single task
9 /// Helper macro to run a single task either locally or on Grid
11 /// aliroot -b -q -l run-single-task.C'("mode", "run", "tasks", "name", useMC, events, "path", "pattern", "friendPattern", "outputDir", "user")'
13 /// mode: local, full, test
14 /// run: list of run numbers. Or if using AODs with predefined list/AODs in same folder, specifiy as "AOD"
15 /// tasks: list of class names/source or header files of task
17 /// optional arguments
18 /// name: analysis name (default 'myanalysis')
19 /// useMC: MC analysis enabled if true (default false')
20 /// events: number of events to be processed (default -1 -> all)
21 /// path: data search path for grid analysis (default from configuration file)
22 /// pattern: data search pattern (default from configuration file)
23 /// friend pattern: friend file search pattern (default from configuration file)
24 /// output dir: output directory in Grid home (default gridwork/yyyy-mm-dd_hh-mm)
25 /// user: default NULL, using user of active token
28 /// aliroot -b -q -l run-single-task.C'("full", "146860", "AliAnalysisTaskSample", "myanalysis_LHC11a")'
30 /// aliroot -b -q -l run-single-task.C'("local", "$ALICE_ROOT/test/ppbench/AliESDs.root", "AliAnalysisTaskSample")'
32 /// aliroot -b -q -l run-single-task.C'("local", "AOD", "AddTaskSample.C")'
34 /// aliroot -b -q -l run-single-task.C'("full", "146860", "AliAnalysisTaskSample", "correlation3p_LHC11a", 0, -1, "/alice/data/2011/LHC11a", "*/pass2_without_SDD/AOD*/*/AliAOD.root")'
37 /// depending on the format of the search pattern either the ESD or AOD input handler is used.
40 /// If the task and classes used by the task are not in an AliRoot library available, e.g.
41 /// for the purpose of development, all header and source files need to be in the local
42 /// directory. The macro searches automatically for dependencies, compiles those and
43 /// copies files to the Grid working directory. In order to make the files accessible in
44 /// the local directory, the files can be just linked.
46 /// for f in <search path>; do ln -s $f; done
50 /// requires only the path to the input file and the task class name. If the specified file is
51 /// a text file (.txt) each line can contain an input ESD file path, all files are chained.
52 /// Analysis on local AOD files needs to be setup prior to this macro. gDirectory must contain
53 /// a TChain object of name 'aodTree'. This is for example created by macros like
54 /// $ALICE_ROOT/PWGHF/vertexingHF/MakeAODInputChain.C
55 /// Set $ALICE_ROOT/PWGHF/correlationHF/macros/setupDxHFE.C for an example.
58 /// All modes provided by the AliAnalysisAlien plugin can be used, e.g. full, test, offline
59 /// A couple of settings need to be defined in a configuration file 'grid-config.C' which can be
60 /// either in the local directory or home directory. The file can look like
62 /// const char* alienAPIVersion="V1.1x";
63 /// const char* alienROOTVersion="v5-34-01";
64 /// const char* alienAliROOTVersion="v5-03-61-AN";
65 /// const char* defaultGridDataDir="/alice/data/2011/LHC11a";
66 /// const char* defaultDataPattern="*/pass2_without_SDD/*/AliESDs.root";
67 /// const char* defaultFriendDataPattern="";
68 /// {} // note this empty body
70 /// Data path and pattern can also be specified as command line arguments.
71 /// The working directory in the grid home directory of the user is set to
72 /// gridwork/<date>_<time> (gridwork/yyyy-mm-dd_hh-mm), can be overridden by command line
77 ///////////////////////////////////////////////////////////////////////////////////////////////////
82 const char* defaultAnalysisName="myanalysis";
83 const char* includePath="-I. -I$ROOTSYS/include -I$ALICE_ROOT/include";
84 const char* libraryDependencies=
89 "libANALYSISalice.so "
92 TString GetIncludeHeaders(const char* filename, TString& headers, TString& libs, bool loadClass=true);
93 void ErrorConfigurationFile(const char* fileName);
95 void run_single_task(const char* mode,
97 const char* tasknames,
98 const char* analysisName=defaultAnalysisName,
101 const char* gridDataDir=NULL,
102 const char* dataPattern=NULL,
103 const char* friendDataPattern=NULL,
105 const char* user=NULL
108 ///////////////////////////////////////////////////////////////////////////////////////////////////
109 ///////////////////////////////////////////////////////////////////////////////////////////////////
110 ///////////////////////////////////////////////////////////////////////////////////////////////////
114 if (analysisName==defaultAnalysisName && gDirectory!=NULL) {
115 // NOTE: the direct pointer comparison is on purpose
116 // string comparison not necessary in this special case
118 // fetch the analysis name from the setup file
119 const char* confObjectName="analysis_name";
120 TObject* confObject=gDirectory->FindObject(confObjectName);
122 analysisName=confObject->GetTitle();
126 bool bRunLocal=strcmp(mode, "local")==0;
127 const char* gridConfigFile="grid-config.C";
128 TString strGridConfigFile=gridConfigFile;
129 if (gSystem->AccessPathName(strGridConfigFile)!=0) {
130 strGridConfigFile.Prepend("/");
131 strGridConfigFile.Prepend(gSystem->Getenv("HOME"));
132 if (gSystem->AccessPathName(strGridConfigFile)!=0) {
134 ErrorConfigurationFile(gridConfigFile);
137 strGridConfigFile="";
141 if (strGridConfigFile.IsNull()==0 && !bRunLocal) {
142 cout << "loading grid configuration from file '" << strGridConfigFile << "':" << endl;
143 gROOT->LoadMacro(strGridConfigFile);
144 cout << " alienAPIVersion =" << alienAPIVersion << endl;
145 cout << " alienROOTVersion =" << alienROOTVersion << endl;
146 cout << " alienAliROOTVersion =" << alienAliROOTVersion << endl;
147 cout << " defaultGridDataDir =" << defaultGridDataDir << endl;
148 cout << " defaultDataPattern =" << defaultDataPattern << endl;
149 cout << " defaultFriendDataPattern =" << defaultFriendDataPattern << endl;
151 if (gridDataDir==NULL) gridDataDir=defaultGridDataDir;
152 if (dataPattern==NULL) dataPattern=defaultDataPattern;
153 if (friendDataPattern==NULL) friendDataPattern=defaultFriendDataPattern;
154 } else if (bRunLocal) {
155 if (dataPattern==NULL) {
157 if (strin.EndsWith("AOD"))
159 else if (strin.EndsWith("ESD"))
166 TGrid::Connect("alien://");
168 ///////////////////////////////////////////////////////////////////////////////////////////////////
169 ///////////////////////////////////////////////////////////////////////////////////////////////////
170 ///////////////////////////////////////////////////////////////////////////////////////////////////
172 // make the analysis manager
174 AliAnalysisManager *pManager = new AliAnalysisManager("AnalysisManager");
176 cerr << "failed to created AnalysisManager" << endl;
179 AliInputEventHandler *pInputHandler = NULL;
180 TString strDataPattern(dataPattern);
181 if (strDataPattern.Contains("AOD")) pInputHandler=new AliAODInputHandler;
182 else if (strDataPattern.Contains("ESD")) pInputHandler=new AliESDInputHandler;
184 cerr << "can not determine input type from data pattern '" << dataPattern << "'" << endl;
187 if (!pInputHandler) {
188 cerr << "failed to created input handler" << endl;
191 //pInputHandler->SetReadFriends(kFALSE);
192 pManager->SetInputEventHandler(pInputHandler);
193 pManager->SetNSysInfo(1000);
195 TString ofile=Form("%s.root", analysisName);
197 ///////////////////////////////////////////////////////////////////////////////////////////////////
198 ///////////////////////////////////////////////////////////////////////////////////////////////////
199 ///////////////////////////////////////////////////////////////////////////////////////////////////
201 // load task classes and find and load all dependencies
203 gSystem->AddIncludePath(includePath);
204 TString libraries=libraryDependencies;
205 if (gDirectory!=NULL) {
206 // fetch the analysis libraries from the setup file
207 const char* confObjectName="analysis_libraries";
208 TObject* confObject=gDirectory->FindObject(confObjectName);
210 TString analysisLibraries(confObject->GetTitle());
211 TObjArray* pTokens=analysisLibraries.Tokenize(" ");
213 for (int i=0; i<pTokens->GetEntriesFast(); i++) {
214 if (libraries.Contains(pTokens->At(i)->GetName())==0) {
216 libraries+=pTokens->At(i)->GetName();
223 TObjArray* pTokens=libraries.Tokenize(" ");
225 for (int i=0; i<pTokens->GetEntriesFast(); i++) {
226 if (gSystem->Load(pTokens->At(i)->GetName())==0) {
227 cout << "loading " << pTokens->At(i)->GetName() << endl;
233 TString taskNames=tasknames;
234 TString taskClasses="";
235 TString taskSources="";
236 TString taskHeaders="";
237 TString addTaskMacros="";
238 TString parPackages="";
239 TObjArray* pTaskNames=taskNames.Tokenize(" ");
241 for (int iTaskName=0; iTaskName<pTaskNames->GetEntriesFast(); iTaskName++) {
242 TString taskSource=pTaskNames->At(iTaskName)->GetName();
243 TString taskHeader=pTaskNames->At(iTaskName)->GetName();
244 bool bIsAddTask=false;
245 if (taskSource.EndsWith(".C")) {
246 // suppose that's an 'AddTask' macro
249 } else if (taskSource.EndsWith(".par")) {
251 if (gSystem->AccessPathName(taskSource)!=0) {
252 ::Error("run_single_task", Form("par file '%s' not found in current directory, you might want to set a symbolic link", taskSource.Data()));
256 parPackages+=taskSource;
258 } else if (taskSource.EndsWith(".h")) {
259 taskSource.ReplaceAll(".h", "");
261 taskClasses+=taskSource;
263 } else if (taskSource.EndsWith(".cxx")) {
264 taskHeader.ReplaceAll(".cxx", "");
266 taskClasses+=taskHeader;
270 taskClasses+=taskSource;
274 TString dependencyHeader;
275 TString dependencySource;
276 if (gSystem->AccessPathName(taskHeader)==0) {
277 GetIncludeHeaders(taskHeader, dependencyHeader, libraries);
278 taskHeaders+=" "; taskHeaders+=taskHeader;
280 if (gSystem->AccessPathName(taskSource)==0) {
281 GetIncludeHeaders(taskSource, dependencyHeader, libraries);
282 if (!bIsAddTask) {taskSources+=" "; taskSources+=taskSource;}
283 else {addTaskMacros+=" "; addTaskMacros+=taskSource;}
285 TObjArray* pTokens=dependencyHeader.Tokenize(" ");
287 for (int i=0; i<pTokens->GetEntriesFast(); i++) {
288 TString sourceFile=pTokens->At(i)->GetName();
289 sourceFile.ReplaceAll(".h", ".cxx");
290 if (gSystem->AccessPathName(sourceFile)!=0) continue;
291 if (!dependencySource.IsNull()) dependencySource+=" ";
292 dependencySource+=sourceFile;
293 if (!libraries.IsNull()) libraries+=" ";
294 libraries+=sourceFile;
298 dependencySource.ReplaceAll(taskSource, "");
299 dependencyHeader.ReplaceAll(taskHeader, "");
303 cout << "Tasks: " << taskClasses << endl;
304 cout << "Task files: " << taskSources << addTaskMacros << taskHeaders << endl;
305 cout << "Dependency classes: " << dependencySource << endl;
306 cout << "Dependency headers: " << dependencyHeader << endl;
307 cout << "Dependency libraries: " << libraries << endl;
308 cout << "Packages: " << parPackages << endl;
310 ///////////////////////////////////////////////////////////////////////////////////////////////////
311 ///////////////////////////////////////////////////////////////////////////////////////////////////
312 ///////////////////////////////////////////////////////////////////////////////////////////////////
314 // init for local or GRID analysis
316 AliAnalysisAlien *alienHandler = NULL; // for grid analysis
317 TChain *chain=NULL; // for local analysis
318 TString strInput=input;
320 ///////////////////////////////////////////////////////////////////////////////////////////////////
324 if(strInput.EndsWith("AliESDs.root")){
325 // suppose it's a single ESD file
326 chain = new TChain("esdTree");
327 chain->Add(strInput);
328 } else if(strInput.EndsWith(".txt")) {
329 // Constructs chain from filenames in *.txt
330 // in the form $DIR/AliESDs.root
331 gROOT->LoadMacro("$ALICE_ROOT/PWG0/CreateESDChain.C");
332 // chain can contain up to 200 files, value can be modified to
333 // include a subset of what the *txt file contains
334 chain = CreateESDChainf(strInput.Data(),200);
336 // check if the files are on grid
337 TIter next(chain->GetListOfFiles());
338 TChainElement *chEl = 0;
339 while(( chEl = (TChainElement*)next() )){
340 TString tmp = chEl->GetTitle();
341 if(tmp.BeginsWith("alien://")) {
342 TGrid::Connect("alien://");
346 } else if(strInput.EndsWith("AOD")){
347 // fetch aod tree from the setup macro
348 if (gDirectory!=NULL) {
349 const char* aodTreeName="aodTree";
350 TObject* chainObject=gDirectory->FindObject(aodTreeName);
352 chain=dynamic_cast<TChain*>(chainObject);
356 ::Error("run_single_task", Form("failed to fetch aod tree object from setup; the chain with name '%s' has to be created before calling this macro", aodTreeName));
360 ::Error("run_single_task", Form("invalid input"));
364 ///////////////////////////////////////////////////////////////////////////////////////////////////
369 TString strInput(input);
370 if (!strInput.IsDigit()) {
371 // support for external macros specifying the the runs to be
373 // the input is expected to be an external plugin with name 'input'
374 // and all run numbers being set
375 TObject* pObj=gDirectory->FindObject(input);
376 if (pObj) alienHandler=dynamic_cast<AliAnalysisAlien*>(pObj);
378 ::Error("run_single_task", Form("can not find plugin of name '%s', please setup alien handler with name and run numbers before calling this macro", input));
383 alienHandler=new AliAnalysisAlien();
386 ::Error("run_single_task", Form("failed to create alien handler"));
390 // do not check for copying to grid (CLOSE_SE)
391 alienHandler->SetCheckCopy(kFALSE);
393 // Set the run mode (can be "full", "test", "offline", "submit" or "terminate")
394 alienHandler->SetRunMode(mode);
396 // check the versions available on alien with the command 'packages'
397 alienHandler->SetAPIVersion(alienAPIVersion);
398 alienHandler->SetROOTVersion(alienROOTVersion);
399 alienHandler->SetAliROOTVersion(alienAliROOTVersion);
401 //Allow non-default outputs
402 //This is required to set non-default output files with the SetOutputFiles
404 alienHandler->SetDefaultOutputs(kFALSE);
405 if (user && user[0]!=0) alienHandler->SetUser(user);
407 // data alien directory
408 alienHandler->SetGridDataDir(gridDataDir);
410 // Set data search pattern
411 alienHandler->SetDataPattern(dataPattern);
412 alienHandler->SetFriendChainName(friendDataPattern);
414 TObjArray* packageTokens=parPackages.Tokenize(" " );
416 for (int iPackageToken=0; iPackageToken<packageTokens->GetEntriesFast(); iPackageToken++) {
417 alienHandler->EnablePackage(packageTokens->At(iPackageToken)->GetName());
419 delete packageTokens;
423 // only set if input is a run number
424 if (!useMC && !strInput.BeginsWith("000"))
425 alienHandler->SetRunPrefix("000"); // real data
427 alienHandler->AddRunNumber(input);
430 // define working and output directories
433 odir=(Form("gridwork/%04d-%02d-%02d_%02d-%02d", dt.GetYear(), dt.GetMonth(), dt.GetDay(), dt.GetHour(), dt.GetMinute()));
434 cout << odir << endl;
435 alienHandler->SetGridWorkingDir(odir); // relative to $HOME
436 alienHandler->SetGridOutputDir("output"); // relative to working dir
437 //alienHandler->SetOverwriteMode(); // overwrites the contents of the working and output directory
439 // workaround for a Root feature: GetIncludePath() appends always
440 // the current Root include path including escaped quotes. Those
441 // quotes make it difficult to pass the output directly. Search for the
442 // last appended include path and truncate
443 TString strIncludePath(gSystem->GetIncludePath());
444 Int_t pos=strIncludePath.Index(includePath);
448 cut=pos+strlen(includePath);
449 } while ((pos=strIncludePath.Index(includePath, cut))>cut);
450 strIncludePath.Resize(cut);
452 alienHandler->AddIncludePath(strIncludePath);
454 // Note: there is no extra source or header file to be transferred if 'AddTask' macros are used
455 alienHandler->SetAnalysisSource(Form("%s %s %s %s", dependencySource.Data(), dependencyHeader.Data(), taskSources.Data(), taskHeaders.Data()));
456 alienHandler->SetAdditionalLibs(Form("%s %s %s", libraries.Data(), taskHeaders.Data(), dependencyHeader.Data()));
458 alienHandler->SetOutputFiles(ofile);
460 // Optionally define the files to be archived.
461 alienHandler->SetOutputArchive("log_archive.zip:stdout,stderr");
463 // Optionally set a name for the generated analysis macro (default MyAnalysis.C)
464 TString macroName; macroName.Form("run_%s.C",analysisName); macroName.ReplaceAll("-","_");
465 alienHandler->SetAnalysisMacro(macroName);
467 //alienHandler->SetExecutable("comparison.sh");
468 alienHandler->SetExecutable(Form("run_%s.sh",analysisName));
470 alienHandler->SetSplitMaxInputFileNumber(100);
472 // Optionally set number of failed jobs that will trigger killing waiting sub-jobs.
473 alienHandler->SetMaxInitFailed(10);
475 // Optionally resubmit threshold.
476 alienHandler->SetMasterResubmitThreshold(90); // in %
478 alienHandler->SetTTL(86400);// in sec
480 // Optionally set input format (default xml-single)
481 alienHandler->SetInputFormat("xml-single");
483 // Optionally modify the name of the generated JDL (default analysis.jdl)
484 alienHandler->SetJDLName(Form("run_%s.jdl",analysisName));
486 // Optionally modify job price (default 1)
487 alienHandler->SetPrice(1);
489 // Optionally modify split mode (default 'se')
490 alienHandler->SetSplitMode("se");
492 // comment out the next line when using the "terminate" option, unless
493 // you want separate merged files for each run
494 if (strcmp(mode, "terminate")==0) {
495 alienHandler->SetMergeViaJDL(kFALSE);
498 alienHandler->SetOneStageMerging(kFALSE);
499 alienHandler->SetMaxMergeStages(2);
502 // Connect plugin to the analysis manager
504 pManager->SetGridHandler(alienHandler);
507 ///////////////////////////////////////////////////////////////////////////////////////////////////
508 ///////////////////////////////////////////////////////////////////////////////////////////////////
509 ///////////////////////////////////////////////////////////////////////////////////////////////////
511 // create task from the name, create output container, connect slots
513 TObjArray* taskClassTokens=taskClasses.Tokenize(" ");
514 if (taskClassTokens) {
515 for (int iTaskClassToken=0; iTaskClassToken<taskClassTokens->GetEntriesFast(); iTaskClassToken++) {
516 AliAnalysisTaskSE *pTask=NULL;
517 TString taskName=taskClassTokens->At(iTaskClassToken)->GetName();
518 taskName.ReplaceAll(".cxx", "");
519 TClass* pCl=TClass::GetClass(taskName);
521 cerr << "can not load class " << taskName << endl;
524 TObject* p=pCl->New();
526 cerr << "failed to instantiate class " << taskName << endl;
529 pTask=reinterpret_cast<AliAnalysisTaskSE*>(p);
530 pManager->AddTask(pTask);
531 AliAnalysisDataContainer *pContainer=pManager->CreateContainer(analysisName ,TObject::Class(), AliAnalysisManager::kOutputContainer, ofile);
532 pManager->ConnectInput(pTask,0,pManager->GetCommonInputContainer());
533 pManager->ConnectOutput(pTask,1,pContainer);
535 delete taskClassTokens;
537 TObjArray* taskMacroTokens=addTaskMacros.Tokenize(" ");
538 if (taskMacroTokens) {
539 for (int iTaskMacroToken=0; iTaskMacroToken<taskMacroTokens->GetEntriesFast(); iTaskMacroToken++) {
541 TString configuration;
542 configuration.Form("name=%s file=%s %s", analysisName, ofile.Data(), useMC?"mc":"");
543 if (gDirectory) gDirectory->Add(new TNamed("run_single_task_configuration", configuration.Data()));
544 gROOT->Macro(taskMacroTokens->At(iTaskMacroToken)->GetName());
546 delete taskMacroTokens;
549 ///////////////////////////////////////////////////////////////////////////////////////////////////
550 ///////////////////////////////////////////////////////////////////////////////////////////////////
551 ///////////////////////////////////////////////////////////////////////////////////////////////////
556 if (!pManager->InitAnalysis()) {
557 cerr << "failed to initialize analysis" << endl;
560 if (nevents<0) nevents=1000000000;
561 pManager->PrintStatus();
563 pManager->StartAnalysis("local", chain, nevents);
565 pManager->StartAnalysis("grid", nevents);
569 TString GetIncludeHeaders(const char* filename, TString& headers, TString& libs, bool loadClass)
571 // scan the file and add all include headers found by path
572 // to the parameter headers
573 ifstream input(filename);
575 cerr << "failed to open file " << filename << endl;
579 while (!line.ReadLine(input).eof()) {
580 if (!line.Contains("#include") || !line.Contains(".h")) continue;
581 line=line(0, line.Index(".h"));line+=".h";
582 line.Replace(0, line.Index("#include"), "");
583 line.ReplaceAll("#include", "");
584 line.ReplaceAll(" ", "");
585 line.ReplaceAll("\"", "");
586 if (!line.BeginsWith("Ali") && !line.BeginsWith("T")) continue;
587 if (gSystem->AccessPathName(line)!=0) {
588 // not an include file in the current directory, check if class
589 // is available or find library
590 line.ReplaceAll(".h","");
591 //cout << "checking class " << line << endl;
592 if (TClass::GetClass(line)==NULL) {
594 TString resfilename(gSystem->TempDirectory()); resfilename+="/findlib.txt";
595 command.Form("for lib in $ALICE_ROOT/lib/*/lib*.so; do (nm $lib | grep %s | grep ' T ' | grep Class_Name > /dev/null) && echo $lib > %s; done", line.Data(), resfilename.Data());
596 gSystem->Exec(command);
597 ifstream resfile(resfilename.Data());
598 if (resfile.good()) {
600 if (!result.ReadLine(resfile).eof()) {
602 while ((haveSlash=result.First('/'))>=0) result.Replace(0, haveSlash+1, "");
603 if (!libs.Contains(result)) {
604 cout << "loading dependency library '" << result << "' for class '" << line << "'" << endl;
605 gSystem->Load(result);
606 if (!libs.IsNull()) libs+=" ";
610 command="rm "; command+=resfilename;
611 gSystem->Exec(command);
615 if (headers.Contains(line)) {
616 if (!headers.BeginsWith(line)) {
617 headers.ReplaceAll(line, "");
618 if (!headers.IsNull()) headers.Insert(0, " ");
619 if (macroVerbosity>0) cout << "moving " << line << endl;
620 headers.Insert(0, line);
624 if (!headers.IsNull()) headers.Insert(0, " ");
625 if (macroVerbosity>0) cout << "inserting " << line << endl;
626 headers.Insert(0, line);
627 TString source=line; source.ReplaceAll(".h", ".cxx");
628 if (gSystem->AccessPathName(source)==0) {
629 GetIncludeHeaders(source, headers, libs);
631 GetIncludeHeaders(line, headers, libs);
632 if (loadClass && gSystem->AccessPathName(source)==0) {
633 line.ReplaceAll(".h", "");
634 if (TClass::GetClass(line)==NULL) {
636 gROOT->LoadMacro(source);
645 void ErrorConfigurationFile(const char* fileName) {
647 cout << "/// -------------------------------------------------------------------" << endl;
648 cout << "/// Warning: can not find configuration file '" << fileName << "'" << endl;
649 cout << "/// please create a configuration file in either local or HOME directory, or in" << endl;
650 cout << "/// specified location. Below is an example, fill in your preferred defaults." << endl;
651 cout << "/// -------------------------------------------------------------------" << endl;
653 cout << "const char* alienAPIVersion=\"V1.1x\";" << endl;
654 cout << "const char* alienROOTVersion=\"v5-33-02a\";" << endl;
655 cout << "const char* alienAliROOTVersion=\"v5-01-Rev-29\";" << endl;
656 cout << "const char* defaultGridDataDir=\"/alice/data/2011/LHC11f\";" << endl;
657 cout << "const char* defaultDataPattern=\"*ESDs.root\";" << endl;
658 cout << "const char* defaultFriendDataPattern=\"\";" << endl;
659 cout << "{} // note this empty body";