1 /* $Id: generate.cpp 591546 2019-08-16 16:59:06Z vasilche $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Eugene Vasilchenko
27 *
28 * File Description:
29 * Main generator: collects all types, classes and files.
30 */
31
32 #include <ncbi_pch.hpp>
33 #include <corelib/ncbifile.hpp>
34 #include <algorithm>
35 #include <typeinfo>
36 #include "moduleset.hpp"
37 #include "module.hpp"
38 #include "type.hpp"
39 #include "statictype.hpp"
40 #include "reftype.hpp"
41 #include "unitype.hpp"
42 #include "enumtype.hpp"
43 #include "blocktype.hpp"
44 #include "choicetype.hpp"
45 #include "filecode.hpp"
46 #include "generate.hpp"
47 #include "exceptions.hpp"
48 #include "fileutil.hpp"
49 #include "rpcgen.hpp"
50 #include "code.hpp"
51 #include "classstr.hpp"
52 #include <serial/error_codes.hpp>
53
54
55 #define NCBI_USE_ERRCODE_X Serial_MainGen
56
57 BEGIN_NCBI_SCOPE
58
CCodeGenerator(void)59 CCodeGenerator::CCodeGenerator(void)
60 : m_ExcludeRecursion(false), m_FileNamePrefixSource(eFileName_FromNone)
61 {
62 m_MainFiles.SetModuleContainer(this);
63 m_ImportFiles.SetModuleContainer(this);
64 m_UseQuotedForm = false;
65 m_CreateCvsignore = false;
66 }
67
~CCodeGenerator(void)68 CCodeGenerator::~CCodeGenerator(void)
69 {
70 }
71
GetConfig(void) const72 const CMemoryRegistry& CCodeGenerator::GetConfig(void) const
73 {
74 return m_Config;
75 }
76
GetFileNamePrefix(void) const77 string CCodeGenerator::GetFileNamePrefix(void) const
78 {
79 return m_FileNamePrefix;
80 }
81
UseQuotedForm(bool use)82 void CCodeGenerator::UseQuotedForm(bool use)
83 {
84 m_UseQuotedForm = use;
85 }
86
CreateCvsignore(bool create)87 void CCodeGenerator::CreateCvsignore(bool create)
88 {
89 m_CreateCvsignore = create;
90 }
91
SetFileNamePrefix(const string & prefix)92 void CCodeGenerator::SetFileNamePrefix(const string& prefix)
93 {
94 m_FileNamePrefix = prefix;
95 }
96
GetFileNamePrefixSource(void) const97 EFileNamePrefixSource CCodeGenerator::GetFileNamePrefixSource(void) const
98 {
99 return m_FileNamePrefixSource;
100 }
101
SetFileNamePrefixSource(EFileNamePrefixSource source)102 void CCodeGenerator::SetFileNamePrefixSource(EFileNamePrefixSource source)
103 {
104 m_FileNamePrefixSource =
105 EFileNamePrefixSource(m_FileNamePrefixSource | source);
106 }
107
InternalResolve(const string & module,const string & name) const108 CDataType* CCodeGenerator::InternalResolve(const string& module,
109 const string& name) const
110 {
111 return ExternalResolve(module, name);
112 }
113
GetNamespace(void) const114 const CNamespace& CCodeGenerator::GetNamespace(void) const
115 {
116 return m_DefaultNamespace;
117 }
118
SetDefaultNamespace(const string & ns)119 void CCodeGenerator::SetDefaultNamespace(const string& ns)
120 {
121 m_DefaultNamespace = ns;
122 }
123
ResetDefaultNamespace(void)124 void CCodeGenerator::ResetDefaultNamespace(void)
125 {
126 m_DefaultNamespace.Reset();
127 }
128
LoadConfig(CNcbiIstream & in)129 void CCodeGenerator::LoadConfig(CNcbiIstream& in)
130 {
131 m_Config.Read(in);
132 }
133
LoadConfig(const string & fileName,bool ignoreAbsense,bool warningAbsense)134 void CCodeGenerator::LoadConfig(const string& fileName,
135 bool ignoreAbsense, bool warningAbsense)
136 {
137 m_DefFile.erase();
138 // load descriptions from registry file
139 if ( fileName == "stdin" || fileName == "-" ) {
140 LoadConfig(NcbiCin);
141 }
142 else {
143 CNcbiIfstream in(fileName.c_str());
144 if ( !in ) {
145 if ( ignoreAbsense ) {
146 return;
147 } else if (warningAbsense) {
148 ERR_POST_X(1, Warning << "cannot open file " << fileName);
149 } else {
150 ERR_POST_X(2, Fatal << "cannot open file " << fileName);
151 }
152 }
153 else {
154 m_DefFile = fileName;
155 LoadConfig(in);
156 }
157 }
158 }
159
AddConfigLine(const string & line)160 void CCodeGenerator::AddConfigLine(const string& line)
161 {
162 SIZE_TYPE bra = line.find('[');
163 SIZE_TYPE ket = line.find(']');
164 SIZE_TYPE eq = line.find('=', ket + 1);
165 if ( bra != 0 || ket == NPOS || eq == NPOS )
166 ERR_POST_X(3, Fatal << "bad config line: " << line);
167
168 m_Config.Set(line.substr(bra + 1, ket - bra - 1),
169 line.substr(ket + 1, eq - ket - 1),
170 line.substr(eq + 1));
171 }
172
ExternalResolve(const string & module,const string & name,bool exported) const173 CDataType* CCodeGenerator::ExternalResolve(const string& module,
174 const string& name,
175 bool exported) const
176 {
177 string loc("CCodeGenerator::ExternalResolve: failed");
178 try {
179 return m_MainFiles.ExternalResolve(module, name, exported);
180 }
181 catch ( CAmbiguiousTypes& exc) {
182 _TRACE(exc.what());
183 NCBI_RETHROW_SAME(exc,loc);
184 }
185 catch ( CNotFoundException& _DEBUG_ARG(exc)) {
186 _TRACE(exc.what());
187 return m_ImportFiles.ExternalResolve(module, name, exported);
188 }
189 return NULL; // Eliminate "return value expected" warning
190 }
191
ResolveInAnyModule(const string & name,bool exported) const192 CDataType* CCodeGenerator::ResolveInAnyModule(const string& name,
193 bool exported) const
194 {
195 string loc("CCodeGenerator::ResolveInAnyModule: failed");
196 try {
197 return m_MainFiles.ResolveInAnyModule(name, exported);
198 }
199 catch ( CAmbiguiousTypes& exc) {
200 _TRACE(exc.what());
201 NCBI_RETHROW_SAME(exc,loc);
202 }
203 catch ( CNotFoundException& _DEBUG_ARG(exc)) {
204 _TRACE(exc.what());
205 return m_ImportFiles.ResolveInAnyModule(name, exported);
206 }
207 return NULL; // Eliminate "return value expected" warning
208 }
209
ResolveMain(const string & fullName) const210 CDataType* CCodeGenerator::ResolveMain(const string& fullName) const
211 {
212 SIZE_TYPE dot = fullName.find('.');
213 if ( dot != NPOS ) {
214 // module specified
215 return m_MainFiles.ExternalResolve(fullName.substr(0, dot),
216 fullName.substr(dot + 1),
217 true);
218 }
219 else {
220 // module not specified - we'll scan all modules for type
221 return m_MainFiles.ResolveInAnyModule(fullName, true);
222 }
223 }
224
ResolveFileName(const string & name) const225 const string& CCodeGenerator::ResolveFileName(const string& name) const
226 {
227 TOutputFiles::const_iterator i = m_Files.find(name);
228 if (i != m_Files.end()) {
229 return i->second->GetFileBaseName();
230 }
231 return name;
232 }
233
IncludeAllMainTypes(void)234 void CCodeGenerator::IncludeAllMainTypes(void)
235 {
236 ITERATE ( CFileSet::TModuleSets, msi, m_MainFiles.GetModuleSets() ) {
237 ITERATE ( CFileModules::TModules, mi, (*msi)->GetModules() ) {
238 const CDataTypeModule* module = mi->get();
239 ITERATE ( CDataTypeModule::TDefinitions, ti,
240 module->GetDefinitions() ) {
241 const string& name = ti->first;
242 const CDataType* type = ti->second.get();
243 if ( !name.empty() && !type->Skipped() ) {
244 m_GenerateTypes.insert(module->GetName() + '.' + name);
245 }
246 }
247 }
248 }
249 }
250
GetTypes(TTypeNames & typeSet,const string & types)251 void CCodeGenerator::GetTypes(TTypeNames& typeSet, const string& types)
252 {
253 SIZE_TYPE pos = 0;
254 SIZE_TYPE next = types.find(',');
255 while ( next != NPOS ) {
256 typeSet.insert(types.substr(pos, next - pos));
257 pos = next + 1;
258 next = types.find(',', pos);
259 }
260 typeSet.insert(types.substr(pos));
261 }
262
Check(void) const263 bool CCodeGenerator::Check(void) const
264 {
265 return m_MainFiles.CheckNames() && m_ImportFiles.CheckNames() &&
266 m_MainFiles.Check();
267 }
268
ExcludeTypes(const string & typeList)269 void CCodeGenerator::ExcludeTypes(const string& typeList)
270 {
271 TTypeNames typeNames;
272 GetTypes(typeNames, typeList);
273 ITERATE ( TTypeNames, i, typeNames ) {
274 m_Config.Set(*i, "_class", "-");
275 m_GenerateTypes.erase(*i);
276 }
277 }
278
IncludeTypes(const string & typeList)279 void CCodeGenerator::IncludeTypes(const string& typeList)
280 {
281 GetTypes(m_GenerateTypes, typeList);
282 }
283
284 struct string_nocase_less
285 {
operator ()string_nocase_less286 bool operator()(const string& s1, const string& s2) const
287 {
288 return (NStr::CompareNocase(s1, s2) < 0);
289 }
290 };
291
UndoGenerateCode(void)292 void CCodeGenerator::UndoGenerateCode(void)
293 {
294 if ( m_FileListFileName.empty() ) {
295 return;
296 }
297 string fileName( MakeAbsolutePath(
298 Path(m_CPPDir,Path(m_FileNamePrefix,m_FileListFileName))));
299 CNcbiIfstream fileList(fileName.c_str());
300 if ( fileList.is_open() ) {
301 string strline;
302 string fileroot;
303 while ( NcbiGetlineEOL(fileList, strline) ) {
304 if ( !NStr::StartsWith(strline, "GENFILES =") ) {
305 continue;
306 }
307 NStr::ReplaceInPlace(strline,"GENFILES =","");
308 list<string> files;
309 NStr::Split( strline, " ", files, NStr::fSplit_MergeDelimiters | NStr::fSplit_Truncate);
310 ITERATE(list<string>, f, files) {
311 CFile fo;
312 fo.Reset( MakeAbsolutePath( Path(m_HPPDir, *f)) + "_.hpp");
313 if (fo.Exists()) {
314 fo.Remove();
315 }
316 fo.Reset( MakeAbsolutePath( Path(m_CPPDir, *f)) + "_.cpp");
317 if (fo.Exists()) {
318 fo.Remove();
319 }
320 }
321 }
322 fileList.close();
323 CFile fo(fileName);
324 if (fo.Exists()) {
325 fo.Remove();
326 }
327 }
328 }
329
CheckFileNames(void)330 void CCodeGenerator::CheckFileNames(void)
331 {
332 set<string,string_nocase_less> names;
333 ITERATE ( TOutputFiles, filei, m_Files ) {
334 CFileCode* code = filei->second.get();
335 string fname;
336 for ( fname = code->GetFileBaseName();
337 names.find(fname) != names.end();) {
338 fname = code->ChangeFileBaseName();
339 }
340 names.insert(fname);
341 }
342 }
343
GenerateCode(void)344 void CCodeGenerator::GenerateCode(void)
345 {
346 // collect types
347 ITERATE ( TTypeNames, ti, m_GenerateTypes ) {
348 CollectTypes(ResolveMain(*ti), eRoot);
349 }
350 CheckFileNames();
351
352 // generate output files
353 string outdir_cpp, outdir_hpp;
354 list<string> listGenerated, listUntouched;
355 list<string> allGeneratedHpp, allGeneratedCpp, allSkippedHpp, allSkippedCpp;
356 map<string, pair<string,string> > module_names;
357 ITERATE ( TOutputFiles, filei, m_Files ) {
358 CFileCode* code = filei->second.get();
359 code->GetModuleNames( module_names);
360 code->UseQuotedForm(m_UseQuotedForm);
361 code->GenerateCode();
362 string fileName;
363 code->GenerateHPP(m_HPPDir, fileName);
364 allGeneratedHpp.push_back(fileName);
365 if (outdir_hpp.empty()) {
366 CDirEntry entry(fileName);
367 outdir_hpp = entry.GetDir();
368 }
369 code->GenerateCPP(m_CPPDir, fileName);
370 allGeneratedCpp.push_back(fileName);
371 if (outdir_cpp.empty()) {
372 CDirEntry entry(fileName);
373 outdir_cpp = entry.GetDir();
374 }
375 if (code->GenerateUserHPP(m_HPPDir, fileName)) {
376 listGenerated.push_back( fileName);
377 allGeneratedHpp.push_back(fileName);
378 } else {
379 listUntouched.push_back( fileName);
380 allSkippedHpp.push_back(fileName);
381 }
382 if (code->GenerateUserCPP(m_CPPDir, fileName)) {
383 listGenerated.push_back( fileName);
384 allGeneratedCpp.push_back(fileName);
385 } else {
386 listUntouched.push_back( fileName);
387 allSkippedCpp.push_back(fileName);
388 }
389 }
390 list<string> module_inc, module_src;
391 GenerateModuleHPP(Path(m_HPPDir,m_FileNamePrefix), module_inc);
392 GenerateModuleCPP(Path(m_CPPDir,m_FileNamePrefix), module_src);
393
394 GenerateDoxygenGroupDescription(module_names);
395 GenerateCombiningFile(module_inc, module_src, allGeneratedHpp, allGeneratedCpp);
396 listGenerated.insert(listGenerated.end(), module_inc.begin(), module_inc.end());
397 allGeneratedHpp.insert(allGeneratedHpp.end(), module_inc.begin(), module_inc.end());
398 listGenerated.insert(listGenerated.end(), module_src.begin(), module_src.end());
399 allGeneratedCpp.insert(allGeneratedCpp.end(), module_src.begin(), module_src.end());
400 GenerateFileList(listGenerated, listUntouched,
401 allGeneratedHpp, allGeneratedCpp, allSkippedHpp, allSkippedCpp);
402 GenerateCvsignore(outdir_cpp, outdir_hpp, listGenerated, module_names);
403 GenerateClientCode();
404 }
405
406
GenerateDoxygenGroupDescription(map<string,pair<string,string>> & module_names)407 void CCodeGenerator::GenerateDoxygenGroupDescription(
408 map<string, pair<string,string> >& module_names)
409 {
410 if (!CClassCode::GetDoxygenComments() || module_names.empty()) {
411 return;
412 }
413 string ingroup_name =
414 m_DoxygenIngroup.empty() ? "DatatoolGeneratedClasses" : m_DoxygenIngroup;
415 CDirEntry entry(GetMainModules().GetModuleSets().front()->GetSourceFileName());
416 string fileName = MakeAbsolutePath(
417 Path(m_HPPDir, Path(m_FileNamePrefix, entry.GetBase() + "_doxygen.h")));
418 CNcbiOfstream doxyfile(fileName.c_str());
419 if ( doxyfile.is_open() ) {
420 CFileCode::WriteCopyrightHeader( doxyfile);
421 doxyfile <<
422 " * File Description:\n"
423 " * This file was generated by application DATATOOL\n"
424 " * It contains comment blocks for DOXYGEN metamodules\n"
425 " *\n"
426 " * ===========================================================================\n"
427 " */\n";
428 if (CClassCode::GetDoxygenGroup().empty()) {
429 map<string, pair<string,string> >::iterator i;
430 for (i = module_names.begin(); i != module_names.end(); ++i) {
431 doxyfile << "\n\n/** @defgroup dataspec_" << i->second.second << " ";
432 if (m_DoxygenGroupDescription.empty()) {
433 doxyfile << "Code generated by DATATOOL from "
434 << i->second.first << " (module \'" << i->first << "\')";
435 } else {
436 doxyfile << m_DoxygenGroupDescription;
437 }
438 doxyfile << "\n * @ingroup " << ingroup_name << "\n */\n\n";
439 }
440 } else {
441 doxyfile << "\n\n/** @defgroup ";
442 doxyfile << CClassCode::GetDoxygenGroup() << " ";
443 if (m_DoxygenGroupDescription.empty()) {
444 doxyfile << "Code generated by DATATOOL";
445 } else {
446 doxyfile << m_DoxygenGroupDescription;
447 }
448 doxyfile << "\n * @ingroup " << ingroup_name << "\n */\n\n";
449 }
450 }
451 }
452
453
GenerateFileList(const list<string> & generated,const list<string> & untouched,list<string> & allGeneratedHpp,list<string> & allGeneratedCpp,list<string> & allSkippedHpp,list<string> & allSkippedCpp)454 void CCodeGenerator::GenerateFileList(
455 const list<string>& generated, const list<string>& untouched,
456 list<string>& allGeneratedHpp, list<string>& allGeneratedCpp,
457 list<string>& allSkippedHpp, list<string>& allSkippedCpp)
458 {
459 if ( m_FileListFileName.empty() ) {
460 return;
461 }
462 string fileName( MakeAbsolutePath(
463 Path(m_CPPDir,Path(m_FileNamePrefix,m_FileListFileName))));
464 CNcbiOfstream fileList(fileName.c_str());
465 if ( !fileList ) {
466 ERR_POST_X(4, Fatal <<
467 "cannot create file list file: " << m_FileListFileName);
468 }
469
470 fileList << "GENFILES =";
471 {
472 ITERATE ( TOutputFiles, filei, m_Files ) {
473 string tmp(filei->second->GetFileBaseName());
474 #if defined(NCBI_OS_MSWIN)
475 tmp = NStr::Replace(tmp,"\\","/");
476 #endif
477 fileList << ' ' << tmp;
478 }
479 }
480 fileList << "\n";
481 fileList << "GENFILES_LOCAL =";
482 {
483 ITERATE ( TOutputFiles, filei, m_Files ) {
484 fileList << ' ' << BaseName(
485 filei->second->GetFileBaseName());
486 }
487 }
488 fileList << "\n";
489
490 // generation report
491 for (int user=0; user<2; ++user) {
492 for (int local=0; local<2; ++local) {
493 for (int cpp=0; cpp<2; ++cpp) {
494 fileList << (user ? "SKIPPED" : "GENERATED") << "_"
495 << (cpp ? "CPP" : "HPP") << (local ? "_LOCAL" : "") << " =";
496 const list<string> *lst = (user ? &untouched : &generated);
497 for (list<string>::const_iterator i=lst->begin();
498 i != lst->end(); ++i) {
499 CDirEntry entry(*i);
500 bool is_cpp = (NStr::CompareNocase(entry.GetExt(),".cpp")==0);
501 if ((is_cpp && cpp) || (!is_cpp && !cpp)) {
502 fileList << ' ';
503 if (local) {
504 fileList << entry.GetBase();
505 } else {
506 string pp = entry.GetPath();
507 size_t found;
508 if (is_cpp) {
509 if (!m_CPPDir.empty() &&
510 (found = pp.find(m_CPPDir)) == 0) {
511 pp.erase(0,m_CPPDir.length()+1);
512 }
513 } else {
514 if (!m_HPPDir.empty() &&
515 (found = pp.find(m_HPPDir)) == 0) {
516 pp.erase(0,m_HPPDir.length()+1);
517 }
518 }
519 CDirEntry ent(CDirEntry::ConvertToOSPath(pp));
520 string tmp(ent.GetDir(CDirEntry::eIfEmptyPath_Empty));
521 #if defined(NCBI_OS_MSWIN)
522 tmp = NStr::Replace(tmp,"\\","/");
523 #endif
524 fileList << tmp << ent.GetBase();
525 }
526 }
527
528 }
529 fileList << endl;
530 }
531 }
532 }
533
534 string flist, flist_local;
535 ITERATE( list<string>, p, allGeneratedHpp) {
536 string tmp(*p);
537 flist_local += ' ' + CDirEntry(tmp).GetName();
538 if (!m_HPPDir.empty() && tmp.find(m_HPPDir) == 0) {
539 tmp.erase(0,m_HPPDir.length()+1);
540 }
541 tmp = CDirEntry::ConvertToOSPath(tmp);
542 #if defined(NCBI_OS_MSWIN)
543 tmp = NStr::Replace(tmp,"\\","/");
544 #endif
545 flist += ' ' +tmp;
546 }
547 fileList << "ALLGENERATED_HPP =" << flist << '\n';
548 fileList << "ALLGENERATED_HPP_LOCAL =" << flist_local << '\n';
549
550 flist.erase(); flist_local.erase();
551 ITERATE( list<string>, p, allSkippedHpp) {
552 string tmp(*p);
553 flist_local += ' ' + CDirEntry(tmp).GetName();
554 if (!m_HPPDir.empty() && tmp.find(m_HPPDir) == 0) {
555 tmp.erase(0,m_HPPDir.length()+1);
556 }
557 tmp = CDirEntry::ConvertToOSPath(tmp);
558 #if defined(NCBI_OS_MSWIN)
559 tmp = NStr::Replace(tmp,"\\","/");
560 #endif
561 flist += ' ' +tmp;
562 }
563 fileList << "ALLSKIPPED_HPP =" << flist << '\n';
564 fileList << "ALLSKIPPED_HPP_LOCAL =" << flist_local << '\n';
565
566 flist.erase(); flist_local.erase();
567 ITERATE( list<string>, p, allGeneratedCpp) {
568 string tmp(*p);
569 flist_local += ' ' + CDirEntry(tmp).GetName();
570 if (!m_CPPDir.empty() && tmp.find(m_CPPDir) == 0) {
571 tmp.erase(0,m_CPPDir.length()+1);
572 }
573 tmp = CDirEntry::ConvertToOSPath(tmp);
574 #if defined(NCBI_OS_MSWIN)
575 tmp = NStr::Replace(tmp,"\\","/");
576 #endif
577 flist += ' ' +tmp;
578 }
579 fileList << "ALLGENERATED_CPP =" << flist << '\n';
580 fileList << "ALLGENERATED_CPP_LOCAL =" << flist_local << '\n';
581
582 flist.erase(); flist_local.erase();
583 ITERATE( list<string>, p, allSkippedCpp) {
584 string tmp(*p);
585 flist_local += ' ' + CDirEntry(tmp).GetName();
586 if (!m_CPPDir.empty() && tmp.find(m_CPPDir) == 0) {
587 tmp.erase(0,m_CPPDir.length()+1);
588 }
589 tmp = CDirEntry::ConvertToOSPath(tmp);
590 #if defined(NCBI_OS_MSWIN)
591 tmp = NStr::Replace(tmp,"\\","/");
592 #endif
593 flist += ' ' +tmp;
594 }
595 fileList << "ALLSKIPPED_CPP =" << flist << '\n';
596 fileList << "ALLSKIPPED_CPP_LOCAL =" << flist_local << '\n';
597
598 }
599
GenerateCombiningFile(const list<string> & module_inc,const list<string> & module_src,list<string> & allHpp,list<string> & allCpp)600 void CCodeGenerator::GenerateCombiningFile(
601 const list<string>& module_inc, const list<string>& module_src,
602 list<string>& allHpp, list<string>& allCpp)
603 {
604 if ( m_CombiningFileName.empty() ) {
605 return;
606 }
607 // write combined files *__.cpp and *___.cpp
608 for ( int i = 0; i < 2; ++i ) {
609 const char* suffix = i? "_.cpp": ".cpp";
610 string fileName = m_CombiningFileName + "__" + suffix;
611 fileName = Path(m_CPPDir,Path(m_FileNamePrefix,fileName));
612 allCpp.push_back(fileName);
613 fileName = MakeAbsolutePath(fileName);
614 CNcbiOfstream out(fileName.c_str());
615 if ( !out )
616 ERR_POST_X(5, Fatal << "Cannot create file: "<<fileName);
617
618 if (!CFileCode::GetPchHeader().empty()) {
619 out <<
620 "#include <" << CFileCode::GetPchHeader() << ">\n";
621 }
622
623 ITERATE ( TOutputFiles, filei, m_Files ) {
624 out << "#include \""<<BaseName(
625 filei->second->GetFileBaseName())<<
626 suffix<<"\"\n";
627 }
628 if (i) {
629 ITERATE( list<string>, m, module_src) {
630 out << "#include \"" << CDirEntry(*m).GetBase() << ".cpp\"\n";
631 }
632 }
633
634 out.close();
635 if ( !out )
636 ERR_POST_X(6, Fatal << "Error writing file "<<fileName);
637 }
638 // write combined *__.hpp file
639 const char* suffix = ".hpp";
640 // save to the includes directory
641 string fileName = Path(m_HPPDir, Path(m_FileNamePrefix,
642 m_CombiningFileName + "__" + suffix));
643 allHpp.push_back(fileName);
644 fileName = MakeAbsolutePath(fileName);
645
646 CNcbiOfstream out(fileName.c_str());
647 if ( !out )
648 ERR_POST_X(7, Fatal << "Cannot create file: " << fileName);
649
650 ITERATE ( TOutputFiles, filei, m_Files ) {
651 out << "#include " << (m_UseQuotedForm ? '\"' : '<') << GetStdPath(
652 Path(m_FileNamePrefix, BaseName(
653 filei->second->GetFileBaseName())) + suffix) <<
654 (m_UseQuotedForm ? '\"' : '>') << "\n";
655 }
656 ITERATE( list<string>, m, module_inc) {
657 out << "#include " << (m_UseQuotedForm ? '\"' : '<')
658 << GetStdPath(Path(m_FileNamePrefix, CDirEntry(*m).GetBase())) << ".hpp"
659 << (m_UseQuotedForm ? '\"' : '>') << "\n";
660 }
661
662 out.close();
663 if ( !out )
664 ERR_POST_X(8, Fatal << "Error writing file " << fileName);
665 }
666
GenerateCvsignore(const string & outdir_cpp,const string & outdir_hpp,const list<string> & generated,map<string,pair<string,string>> & module_names)667 void CCodeGenerator::GenerateCvsignore(
668 const string& outdir_cpp, const string& outdir_hpp,
669 const list<string>& generated, map<string, pair<string,string> >& module_names)
670 {
671 if (!m_CreateCvsignore) {
672 return;
673 }
674 string ignoreName(".cvsignore");
675 string extraName(".cvsignore.extra");
676
677 for (int i=0; i<2; ++i) {
678 bool is_cpp = (i==0);
679 bool different_dirs = (outdir_cpp != outdir_hpp);
680 string out_dir(is_cpp ? outdir_cpp : outdir_hpp);
681
682 string ignorePath(MakeAbsolutePath(Path(out_dir,ignoreName)));
683 // ios::out should be redundant, but some compilers
684 // (GCC 2.9x, for one) seem to need it. :-/
685 CNcbiOfstream ignoreFile(ignorePath.c_str(),
686 ios::out | ((different_dirs || is_cpp) ? ios::trunc : ios::app));
687
688 if (ignoreFile.is_open()) {
689
690 if (different_dirs || is_cpp) {
691 ignoreFile << ignoreName << endl;
692 }
693
694 // .cvsignore.extra
695 if (different_dirs || is_cpp) {
696 string extraPath(Path(out_dir,extraName));
697 CNcbiIfstream extraFile(extraPath.c_str());
698 if (extraFile.is_open()) {
699 char buf[256];
700 while (extraFile.good()) {
701 extraFile.getline(buf, sizeof(buf));
702 CTempString sbuf(NStr::TruncateSpaces_Unsafe(buf));
703 if (!sbuf.empty()) {
704 ignoreFile << sbuf << endl;
705 }
706 }
707 }
708 }
709
710 // base classes (always generated)
711 ITERATE ( TOutputFiles, filei, m_Files ) {
712 ignoreFile
713 << BaseName(filei->second->GetFileBaseName())
714 << "_." << (is_cpp ? "cpp" : "hpp") << endl;
715 }
716
717 // user classes
718 for (list<string>::const_iterator it = generated.begin();
719 it != generated.end(); ++it) {
720 CDirEntry entry(*it);
721 if (is_cpp == (NStr::CompareNocase(entry.GetExt(),".cpp")==0)) {
722 ignoreFile << entry.GetName() << endl;
723 }
724 }
725
726 // combining files
727 if ( !m_CombiningFileName.empty() ) {
728 if (is_cpp) {
729 ignoreFile << m_CombiningFileName << "__" << "_.cpp" << endl;
730 ignoreFile << m_CombiningFileName << "__" << ".cpp" << endl;
731 } else {
732 ignoreFile << m_CombiningFileName << "__" << ".hpp" << endl;
733 }
734 }
735
736 // doxygen header
737 if ( !is_cpp && CClassCode::GetDoxygenComments()
738 && !module_names.empty() ) {
739 CDirEntry entry(GetMainModules().GetModuleSets().front()
740 ->GetSourceFileName());
741 ignoreFile << entry.GetBase() << "_doxygen.h" << endl;
742 }
743
744 // file list
745 if ( is_cpp && !m_FileListFileName.empty() ) {
746 CDirEntry entry(Path(m_FileNamePrefix,m_FileListFileName));
747 ignoreFile << entry.GetName() << endl;
748 }
749
750 // specification dump (somewhat hackishly)
751 if ( const CArgValue& f
752 = CNcbiApplication::Instance()->GetArgs()["fd"] ) {
753 ignoreFile << f.AsString() << endl;
754 }
755 }
756 }
757 }
758
GenerateModuleHPP(const string & path,list<string> & generated) const759 void CCodeGenerator::GenerateModuleHPP(const string& path, list<string>& generated) const
760 {
761 set<string> modules;
762 string module_name, current_module, filename, hppDefine;
763 unique_ptr<CDelayedOfstream> out;
764 CNamespace ns;
765
766 bool isfound = false;
767 do {
768 isfound = false;
769 bool types_found = false;
770 ITERATE ( TOutputFiles, filei, m_Files ) {
771 CFileCode* code = filei->second.get();
772 list<CTypeStrings*> filetypes;
773 code->GetClasses( filetypes );
774 module_name.clear();
775 ITERATE(list<CTypeStrings*>, t, filetypes) {
776 string module = (*t)->GetDoxygenModuleName();
777 if (module_name.empty() || module.size() < module_name.size()) {
778 module_name = module;
779 }
780 }
781 if (current_module.empty()) {
782 if (modules.find(module_name) != modules.end()) {
783 continue;
784 }
785 modules.insert(module_name);
786 current_module = module_name;
787 } else if (current_module != module_name) {
788 continue;
789 }
790 isfound = true;
791 if ( !out.get() || !out->is_open() ) {
792 if (isdigit((unsigned char)current_module[0])) {
793 current_module.insert(current_module.begin(),'x');
794 }
795 filename = Path(path, current_module + "_module.hpp");
796 out.reset(new CDelayedOfstream(filename.c_str()));
797 if (!out->is_open()) {
798 ERR_POST_X(9, Fatal << "Cannot create file: " << filename);
799 return;
800 }
801 generated.push_back(filename);
802 hppDefine = current_module + "_REGISTERMODULECLASSES_HPP";
803 code->WriteCopyright(*out, false) <<
804 "\n"
805 "#ifndef " << hppDefine << "\n"
806 "#define " << hppDefine << "\n"
807 "\n#include <serial/serialbase.hpp>\n\n";
808 ns.Set(code->GetNamespace(), *out, true);
809 *out << '\n';
810 if (!CClassCode::GetExportSpecifier().empty()) {
811 *out << CClassCode::GetExportSpecifier() << '\n';
812 }
813 *out <<
814 "void " << current_module << "_RegisterModuleClasses(void);\n\n";
815 }
816 ITERATE(list<CTypeStrings*>, t, filetypes) {
817 if ((*t)->GetKind() == CTypeStrings::eKindObject) {
818 CClassTypeStrings* classtype = dynamic_cast<CClassTypeStrings*>(*t);
819 if (classtype && classtype->HaveTypeInfo()) {
820 types_found = true;
821 }
822 }
823 }
824 }
825 if (isfound && !types_found) {
826 generated.pop_back();
827 out->Discard();
828 }
829 if (out->is_open()) {
830 ns.Reset(*out);
831 *out <<
832 "\n"
833 "#endif // " << hppDefine << "\n";
834 out->close();
835 if ( !*out )
836 ERR_POST_X(10, Fatal << "Error writing file " << filename);
837 }
838 current_module.erase();
839 } while (isfound);
840 }
841
GenerateModuleCPP(const string & path,list<string> & generated) const842 void CCodeGenerator::GenerateModuleCPP(const string& path, list<string>& generated) const
843 {
844 set<string> modules;
845 string module_name, current_module, filename, hppDefine;
846 unique_ptr<CDelayedOfstream> out;
847 CNcbiOstrstream out_inc;
848 CNcbiOstrstream out_code;
849 CNamespace ns;
850
851 bool isfound = false;
852 do {
853 isfound = false;
854 bool types_found = false;
855 ITERATE ( TOutputFiles, filei, m_Files ) {
856 CFileCode* code = filei->second.get();
857 list<CTypeStrings*> filetypes;
858 code->GetClasses( filetypes );
859 module_name.clear();
860 ITERATE(list<CTypeStrings*>, t, filetypes) {
861 string module = (*t)->GetDoxygenModuleName();
862 if (module_name.empty() || module.size() < module_name.size()) {
863 module_name = module;
864 }
865 }
866 if (current_module.empty()) {
867 if (modules.find(module_name) != modules.end()) {
868 continue;
869 }
870 modules.insert(module_name);
871 current_module = module_name;
872 } else if (current_module != module_name) {
873 continue;
874 }
875 isfound = true;
876 if ( !out.get() || !out->is_open()) {
877 if (isdigit((unsigned char)current_module[0])) {
878 current_module.insert(current_module.begin(),'x');
879 }
880 filename = Path(path, current_module + "_module.cpp");
881 string module_inc =
882 CDirEntry::ConcatPath( CDirEntry( code->GetUserHPPName() ).GetDir(),
883 current_module + "_module.hpp");
884 out_inc <<
885 "#include " << code->Include(module_inc) << "\n";
886 out.reset(new CDelayedOfstream(filename.c_str()));
887 if (!out->is_open()) {
888 ERR_POST_X(11, Fatal << "Cannot create file: " << filename);
889 return;
890 }
891 generated.push_back(filename);
892 code->WriteCopyright(*out, false);
893 *out << "\n";
894 if (!CFileCode::GetPchHeader().empty()) {
895 *out <<
896 "#include <" << CFileCode::GetPchHeader() << ">\n";
897 }
898 ns.Set(code->GetNamespace(), out_code, false);
899 out_code <<
900 "void " << current_module << "_RegisterModuleClasses(void)\n{\n";
901 }
902 set<string> user_includes;
903 ITERATE(list<CTypeStrings*>, t, filetypes) {
904 if ((*t)->GetKind() == CTypeStrings::eKindObject) {
905 CClassTypeStrings* classtype = dynamic_cast<CClassTypeStrings*>(*t);
906 if (classtype && classtype->HaveTypeInfo()) {
907 types_found = true;
908 string userhpp(code->Include(code->GetUserHPPName()));
909 if (user_includes.find(userhpp) == user_includes.end()) {
910 user_includes.insert(userhpp);
911 out_inc <<
912 "#include " << code->Include(code->GetUserHPPName()) << "\n";
913 }
914 out_code << " "
915 << code->GetClassNamespace(*t).ToString()
916 << classtype->GetClassNameDT() << "::GetTypeInfo();\n";
917 }
918 }
919 }
920 }
921 if (isfound && !types_found) {
922 generated.pop_back();
923 out->Discard();
924 }
925 if (out->is_open()) {
926 out_code << "}\n\n";
927 ns.Reset(out_code);
928 *out << string(CNcbiOstrstreamToString(out_inc))
929 << "\n\n"
930 << string(CNcbiOstrstreamToString(out_code));
931 out_inc.seekp(0);
932 out_code.seekp(0);
933 #ifdef NCBI_SHUN_OSTRSTREAM
934 out_inc.str(kEmptyStr);
935 out_code.str(kEmptyStr);
936 #endif
937 out->close();
938 if ( !*out )
939 ERR_POST_X(12, Fatal << "Error writing file " << filename);
940 }
941 current_module.erase();
942 } while (isfound);
943 }
944
GenerateClientCode(void)945 void CCodeGenerator::GenerateClientCode(void)
946 {
947 string clients = m_Config.Get("-", "clients");
948 if (clients.empty()) {
949 // // for compatibility with older specifications
950 // GenerateClientCode("client", false);
951 } else {
952 // explicit name; must be enabled
953 list<string> l;
954 // if multiple items, may have whitespace, commas, or both...
955 NStr::Split(clients, ", \t", l, NStr::fSplit_MergeDelimiters);
956 ITERATE (list<string>, it, l) {
957 if ( !it->empty() ) {
958 GenerateClientCode(*it, true);
959 }
960 }
961 }
962 }
963
GenerateClientCode(const string & name,bool mandatory)964 void CCodeGenerator::GenerateClientCode(const string& name, bool mandatory)
965 {
966 string class_name = m_Config.Get(name, "class");
967 if (class_name.empty()) {
968 if (mandatory) {
969 ERR_POST_X(13, Fatal << "No configuration for mandatory client " + name);
970 }
971 return; // not configured
972 }
973 CFileCode code(this,Path(m_FileNamePrefix, name));
974 CClientPseudoDataType type(*this, name, class_name);
975 code.UseQuotedForm(m_UseQuotedForm);
976 code.AddType(&type);
977 code.GenerateCode();
978 string filename;
979 code.GenerateHPP(m_HPPDir, filename);
980 code.GenerateCPP(m_CPPDir, filename);
981 code.GenerateUserHPP(m_HPPDir, filename);
982 code.GenerateUserCPP(m_CPPDir, filename);
983 }
984
AddType(const CDataType * type)985 bool CCodeGenerator::AddType(const CDataType* type)
986 {
987 string fileName = type->FileName();
988 AutoPtr<CFileCode>& file = m_Files[fileName];
989 if ( !file )
990 file = new CFileCode(this,fileName);
991 return file->AddType(type);
992 }
993
Imported(const CDataType * type) const994 bool CCodeGenerator::Imported(const CDataType* type) const
995 {
996 try {
997 m_MainFiles.ExternalResolve(type->GetModule()->GetName(),
998 type->IdName(),
999 true);
1000 return false;
1001 }
1002 catch ( CNotFoundException& /* ignored */) {
1003 }
1004 return true;
1005 }
1006
CollectTypes(const CDataType * type,EContext)1007 void CCodeGenerator::CollectTypes(const CDataType* type, EContext /*context*/)
1008 {
1009 if ( type->GetParentType() == 0 ) {
1010 const CWsdlDataType* w = dynamic_cast<const CWsdlDataType*>(type);
1011 if (!w || w->GetWsdlType() == CWsdlDataType::eWsdlEndpoint) {
1012 if ( !AddType(type) ) {
1013 return;
1014 }
1015 }
1016 }
1017
1018 if ( m_ExcludeRecursion )
1019 return;
1020
1021 const CUniSequenceDataType* array =
1022 dynamic_cast<const CUniSequenceDataType*>(type);
1023 if ( array != 0 ) {
1024 // we should add element type
1025 CollectTypes(array->GetElementType(), eElement);
1026 return;
1027 }
1028
1029 const CReferenceDataType* user =
1030 dynamic_cast<const CReferenceDataType*>(type);
1031 if ( user != 0 ) {
1032 // reference to another type
1033 const CDataType* resolved;
1034 try {
1035 resolved = user->Resolve();
1036 }
1037 catch ( CNotFoundException& exc) {
1038 ERR_POST_X(14, Warning <<
1039 "Skipping type: " << user->GetUserTypeName() <<
1040 ": " << exc.what());
1041 return;
1042 }
1043 if ( resolved->Skipped() ) {
1044 ERR_POST_X(15, Warning << "Skipping type: " << user->GetUserTypeName());
1045 return;
1046 }
1047 if ( !Imported(resolved) ) {
1048 CollectTypes(resolved, eReference);
1049 }
1050 return;
1051 }
1052
1053 const CDataMemberContainerType* cont =
1054 dynamic_cast<const CDataMemberContainerType*>(type);
1055 if ( cont != 0 ) {
1056 // collect member's types
1057 ITERATE ( CDataMemberContainerType::TMembers, mi,
1058 cont->GetMembers() ) {
1059 const CDataType* memberType = mi->get()->GetType();
1060 CollectTypes(memberType, eMember);
1061 }
1062 return;
1063 }
1064 }
1065
1066 #if 0
1067 void CCodeGenerator::CollectTypes(const CDataType* type, EContext context)
1068 {
1069 const CUniSequenceDataType* array =
1070 dynamic_cast<const CUniSequenceDataType*>(type);
1071 if ( array != 0 ) {
1072 // SET OF or SEQUENCE OF
1073 if ( type->GetParentType() == 0 || context == eChoice ) {
1074 if ( !AddType(type) )
1075 return;
1076 }
1077 if ( m_ExcludeRecursion )
1078 return;
1079 // we should add element type
1080 CollectTypes(array->GetElementType(), eElement);
1081 return;
1082 }
1083
1084 const CReferenceDataType* user =
1085 dynamic_cast<const CReferenceDataType*>(type);
1086 if ( user != 0 ) {
1087 // reference to another type
1088 const CDataType* resolved;
1089 try {
1090 resolved = user->Resolve();
1091 }
1092 catch ( CNotFoundException& exc) {
1093 ERR_POST_X(16, Warning <<
1094 "Skipping type: " << user->GetUserTypeName() <<
1095 ": " << exc.what());
1096 return;
1097 }
1098 if ( resolved->Skipped() ) {
1099 ERR_POST_X(17, Warning << "Skipping type: " << user->GetUserTypeName());
1100 return;
1101 }
1102 if ( context == eChoice ) {
1103 // in choice
1104 if ( resolved->InheritFromType() != user->GetParentType() ||
1105 dynamic_cast<const CEnumDataType*>(resolved) != 0 ) {
1106 // add intermediate class
1107 AddType(user);
1108 }
1109 }
1110 else if ( type->GetParentType() == 0 ) {
1111 // alias declaration
1112 // generate empty class
1113 AddType(user);
1114 }
1115 if ( !Imported(resolved) ) {
1116 CollectTypes(resolved, eReference);
1117 }
1118 return;
1119 }
1120
1121 if ( dynamic_cast<const CStaticDataType*>(type) != 0 ) {
1122 // STD type
1123 if ( type->GetParentType() == 0 || context == eChoice ) {
1124 AddType(type);
1125 }
1126 return;
1127 }
1128
1129 if ( dynamic_cast<const CEnumDataType*>(type) != 0 ) {
1130 // ENUMERATED type
1131 if ( type->GetParentType() == 0 || context == eChoice ) {
1132 AddType(type);
1133 }
1134 return;
1135 }
1136
1137 if ( type->GetParentType() == 0 || context == eChoice ) {
1138 if ( type->Skipped() ) {
1139 ERR_POST_X(18, Warning << "Skipping type: " << type->IdName());
1140 return;
1141 }
1142 }
1143
1144 const CChoiceDataType* choice =
1145 dynamic_cast<const CChoiceDataType*>(type);
1146 if ( choice != 0 ) {
1147 if ( !AddType(type) )
1148 return;
1149
1150 if ( m_ExcludeRecursion )
1151 return;
1152
1153 // collect member's types
1154 ITERATE ( CDataMemberContainerType::TMembers, mi,
1155 choice->GetMembers() ) {
1156 const CDataType* memberType = mi->get()->GetType();
1157 CollectTypes(memberType, eMember); // eChoice
1158 }
1159 }
1160
1161 const CDataMemberContainerType* cont =
1162 dynamic_cast<const CDataMemberContainerType*>(type);
1163 if ( cont != 0 ) {
1164 if ( !AddType(type) )
1165 return;
1166
1167 if ( m_ExcludeRecursion )
1168 return;
1169
1170 // collect member's types
1171 ITERATE ( CDataMemberContainerType::TMembers, mi,
1172 cont->GetMembers() ) {
1173 const CDataType* memberType = mi->get()->GetType();
1174 CollectTypes(memberType, eMember);
1175 }
1176 return;
1177 }
1178 if ( !AddType(type) )
1179 return;
1180 }
1181 #endif
1182
ResolveImportRefs(void)1183 void CCodeGenerator::ResolveImportRefs(void)
1184 {
1185 ITERATE( CFileSet::TModuleSets, fs, GetMainModules().GetModuleSets()) {
1186 ITERATE(CFileModules::TModules, fm, (*fs)->GetModules()) {
1187 CDataTypeModule* head = const_cast<CDataTypeModule*>(fm->get());
1188 head->AddImportRef( head->GetName());
1189 ResolveImportRefs(*head,head);
1190 }
1191 }
1192 }
1193
ResolveImportRefs(CDataTypeModule & head,const CDataTypeModule * ref)1194 void CCodeGenerator::ResolveImportRefs(CDataTypeModule& head, const CDataTypeModule* ref)
1195 {
1196 if (ref) {
1197 ITERATE ( CDataTypeModule::TImports, i, ref->GetImports() ) {
1198 const string& s = (*i)->moduleName;
1199 if (head.AddImportRef(s)) {
1200 ResolveImportRefs(head,FindModuleByName(s));
1201 }
1202 }
1203 }
1204 }
1205
FindModuleByName(const string & name) const1206 const CDataTypeModule* CCodeGenerator::FindModuleByName(const string& name) const
1207 {
1208 ITERATE( CFileSet::TModuleSets, mm, GetMainModules().GetModuleSets()) {
1209 ITERATE(CFileModules::TModules, fm, (*mm)->GetModules()) {
1210 if ((*fm)->GetName() == name) {
1211 return fm->get();
1212 }
1213 }
1214 }
1215 ITERATE( CFileSet::TModuleSets, mi, GetImportModules().GetModuleSets()) {
1216 ITERATE(CFileModules::TModules, fm, (*mi)->GetModules()) {
1217 if ((*fm)->GetName() == name) {
1218 return fm->get();
1219 }
1220 }
1221 }
1222 ERR_POST_X(19, Error << "cannot find module " << name);
1223 return 0;
1224 }
1225
GetOpt(const string & opt,string * value)1226 bool CCodeGenerator::GetOpt(const string& opt, string* value)
1227 {
1228 string result;
1229 string key("-");
1230 string xopt = key + opt;
1231 if (m_Config.HasEntry(key, xopt)) {
1232 result = m_Config.Get(key, xopt);
1233 if (value) {
1234 *value = result;
1235 }
1236 return (result != key);
1237 }
1238 const CArgs& args = CNcbiApplication::Instance()->GetArgs();
1239 const CArgValue& argv = args[opt];
1240 if (argv.HasValue()) {
1241 if (value) {
1242 *value = argv.AsString();
1243 }
1244 return true;
1245 }
1246 return false;
1247 }
1248
1249
1250 END_NCBI_SCOPE
1251