1 /* $Id: proj_tree.cpp 602953 2020-03-04 19:52:40Z gouriano $
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:  Viatcheslav Gorelenkov
27  *
28  */
29 
30 #include <ncbi_pch.hpp>
31 #include "proj_tree.hpp"
32 #include "proj_tree_builder.hpp"
33 #include "proj_utils.hpp"
34 #include "proj_builder_app.hpp"
35 #include "msvc_prj_defines.hpp"
36 #include "ptb_err_codes.hpp"
37 
38 #include <algorithm>
39 
40 BEGIN_NCBI_SCOPE
41 
42 
43 //-----------------------------------------------------------------------------
CProjectItemsTree(void)44 CProjectItemsTree::CProjectItemsTree(void)
45 {
46     Clear();
47 }
48 
49 
CProjectItemsTree(const string & root_src)50 CProjectItemsTree::CProjectItemsTree(const string& root_src)
51 {
52     Clear();
53     m_RootSrc = root_src;
54 }
55 
56 
CProjectItemsTree(const CProjectItemsTree & projects)57 CProjectItemsTree::CProjectItemsTree(const CProjectItemsTree& projects)
58 {
59     SetFrom(projects);
60 }
61 
62 
63 CProjectItemsTree&
operator =(const CProjectItemsTree & projects)64 CProjectItemsTree::operator= (const CProjectItemsTree& projects)
65 {
66     if (this != &projects) {
67         Clear();
68         SetFrom(projects);
69     }
70     return *this;
71 }
72 
73 
~CProjectItemsTree(void)74 CProjectItemsTree::~CProjectItemsTree(void)
75 {
76     Clear();
77 }
78 
79 
Clear(void)80 void CProjectItemsTree::Clear(void)
81 {
82     m_RootSrc.erase();
83     m_Projects.clear();
84 }
85 
86 
SetFrom(const CProjectItemsTree & projects)87 void CProjectItemsTree::SetFrom(const CProjectItemsTree& projects)
88 {
89     m_RootSrc  = projects.m_RootSrc;
90     m_Projects = projects.m_Projects;
91 }
92 
93 
CreateFrom(const string & root_src,const TFiles & makein,const TFiles & makelib,const TFiles & makedll,const TFiles & makeapp,const TFiles & makemsvc,CProjectItemsTree * tree)94 void CProjectItemsTree::CreateFrom(const string& root_src,
95                                    const TFiles& makein,
96                                    const TFiles& makelib,
97                                    const TFiles& makedll,
98                                    const TFiles& makeapp,
99                                    const TFiles& makemsvc,
100                                    CProjectItemsTree* tree)
101 {
102     tree->m_Projects.clear();
103     tree->m_RootSrc = root_src;
104 
105     string dataspec = GetApp().GetDataspecProjId();
106     string utility_projects_dir = GetApp().GetUtilityProjectsSrcDir();
107     SLibProjectT::DoCreateDataSpec(
108         utility_projects_dir, dataspec, dataspec, tree, eMakeType_Undefined);
109 
110     ITERATE(TFiles, p, makein) {
111 
112         const string& fc_path = p->first;
113         const CSimpleMakeFileContents& fc_makein = p->second;
114 
115         string source_base_dir;
116         CDirEntry::SplitPath(fc_path, &source_base_dir);
117 
118         SMakeProjectT::TMakeInInfoList list_info;
119         SMakeProjectT::AnalyzeMakeIn(fc_makein, &list_info);
120         ITERATE(SMakeProjectT::TMakeInInfoList, i, list_info) {
121 
122             const SMakeProjectT::SMakeInInfo& info = *i;
123 
124             //Iterate all project_name(s) from makefile.in
125             ITERATE(list<string>, n, info.m_ProjNames) {
126 
127                 //project id will be defined latter
128                 const string& proj_name = *n;
129                 if (proj_name[0] == '#') {
130                     break;
131                 }
132                 if (proj_name[0] == '@'  &&
133                     proj_name[proj_name.size() - 1] == '@') {
134                     /// skip these - these are dead macros
135                     continue;
136                 }
137 
138                 string applib_mfilepath =
139                     SMakeProjectT::CreateMakeAppLibFileName(source_base_dir,
140                                                             proj_name, info.m_Type);
141                 if ( applib_mfilepath.empty() )
142                     continue;
143 
144                 if (info.m_Type == SMakeProjectT::SMakeInInfo::eApp) {
145 
146                     SAsnProjectT::TAsnType asn_type =
147                         SAsnProjectT::GetAsnProjectType(applib_mfilepath,
148                                                         makeapp,
149                                                         makelib);
150 
151                     if (asn_type == SAsnProjectT::eMultiple) {
152                         SAsnProjectT::DoCreate(source_base_dir,
153                                                proj_name,
154                                                applib_mfilepath,
155                                                makeapp, makelib, tree, info);
156                     } else {
157                         SAppProjectT::DoCreate(source_base_dir,
158                                                proj_name,
159                                                applib_mfilepath,
160                                                makeapp, tree, info.m_MakeType);
161                     }
162                 }
163                 else if (info.m_Type == SMakeProjectT::SMakeInInfo::eLib) {
164 
165                     SAsnProjectT::TAsnType asn_type =
166                         SAsnProjectT::GetAsnProjectType(applib_mfilepath,
167                                                         makeapp,
168                                                         makelib);
169 
170                     if (asn_type == SAsnProjectT::eMultiple) {
171                         SAsnProjectT::DoCreate(source_base_dir,
172                                                proj_name,
173                                                applib_mfilepath,
174                                                makeapp, makelib, tree, info);
175                     } else {
176                         SLibProjectT::DoCreate(source_base_dir,
177                                                proj_name,
178                                                applib_mfilepath,
179                                                makelib, tree, info.m_MakeType);
180                     }
181                 }
182                 else if (info.m_Type == SMakeProjectT::SMakeInInfo::eDll) {
183                     SDllProjectT::DoCreate(source_base_dir,
184                                            proj_name,
185                                            applib_mfilepath,
186                                            makedll, tree, info.m_MakeType);
187                 }
188                 else if (info.m_Type == SMakeProjectT::SMakeInInfo::eASN ||
189                          info.m_Type == SMakeProjectT::SMakeInInfo::eDTD ||
190                          info.m_Type == SMakeProjectT::SMakeInInfo::eXSD ||
191                          info.m_Type == SMakeProjectT::SMakeInInfo::eWSDL ||
192                          info.m_Type == SMakeProjectT::SMakeInInfo::eJSD ||
193                          info.m_Type == SMakeProjectT::SMakeInInfo::eProtobuf) {
194 
195                     SAsnProjectT::DoCreate(source_base_dir,
196                                            proj_name,
197                                            applib_mfilepath,
198                                            makeapp, makelib, tree, info);
199                 }
200                 else if (info.m_Type == SMakeProjectT::SMakeInInfo::eMsvc) {
201 
202                     SMsvcProjectT::DoCreate(source_base_dir,
203                                             proj_name,
204                                             applib_mfilepath,
205                                             makemsvc, tree, info.m_MakeType);
206                 }
207                 else if (info.m_Type == SMakeProjectT::SMakeInInfo::eMetal) {
208                     SLibProjectT::DoCreate(source_base_dir,
209                                             proj_name,
210                                             applib_mfilepath,
211                                             makelib, tree, info.m_MakeType);
212                 }
213             }
214         }
215     }
216     AnalyzeDllData(*tree);
217 }
218 
219 
GetInternalDepends(list<CProjKey> * depends) const220 void CProjectItemsTree::GetInternalDepends(list<CProjKey>* depends) const
221 {
222     depends->clear();
223 
224     set<CProjKey> depends_set;
225 
226     ITERATE(TProjects, p, m_Projects) {
227         const CProjItem& proj_item = p->second;
228 // DLL dependencies will be handled later
229         if (proj_item.m_ProjType != CProjKey::eDll) {
230             ITERATE(list<CProjKey>, n, proj_item.m_Depends) {
231                 depends_set.insert(*n);
232             }
233             if (proj_item.m_ProjType == CProjKey::eLib &&
234                 GetApp().GetBuildType().GetType() == CBuildType::eDll &&
235                 !proj_item.m_DllHost.empty()) {
236                     depends_set.insert(CProjKey(
237                         CProjKey::eDll, proj_item.m_DllHost));
238             }
239         } else {
240             if (GetApp().GetBuildType().GetType() == CBuildType::eDll) {
241                 ITERATE(list<string>, n, proj_item.m_HostedLibs) {
242                     depends_set.insert(CProjKey(CProjKey::eLib, *n));
243                 }
244             }
245         }
246     }
247 
248     copy(depends_set.begin(), depends_set.end(), back_inserter(*depends));
249 }
250 
251 
252 void
GetExternalDepends(list<CProjKey> * external_depends) const253 CProjectItemsTree::GetExternalDepends(list<CProjKey>* external_depends) const
254 {
255     external_depends->clear();
256 
257     list<CProjKey> depends;
258     GetInternalDepends(&depends);
259     ITERATE(list<CProjKey>, p, depends) {
260         const CProjKey& depend_id = *p;
261         if (m_Projects.find(depend_id) == m_Projects.end())
262             external_depends->push_back(depend_id);
263     }
264 }
265 
VerifyExternalDepends(void)266 void CProjectItemsTree::VerifyExternalDepends(void)
267 {
268     ITERATE(TProjects, p, m_Projects) {
269         const CProjItem& proj_item = p->second;
270         if (proj_item.m_External) {
271             continue;
272         }
273         ITERATE(list<CProjKey>, n, proj_item.m_Depends) {
274             if (*n == p->first) {
275                 continue;
276             }
277             TProjects::iterator d = m_Projects.find(*n);
278             if (d != m_Projects.end() /*&& d->second.m_External*/) {
279                 d->second.m_MakeType = min(d->second.m_MakeType, p->second.m_MakeType);
280             }
281         }
282     }
283 
284     set<CProjKey> depends_set;
285     ITERATE(TProjects, p, m_Projects) {
286         const CProjItem& proj_item = p->second;
287         if (proj_item.m_External) {
288             continue;
289         }
290         ITERATE(list<CProjKey>, n, proj_item.m_Depends) {
291             if (*n == p->first) {
292                 continue;
293             }
294             depends_set.insert(*n);
295         }
296     }
297     bool modified = false;
298     ITERATE(set<CProjKey>, d, depends_set) {
299         TProjects::iterator p = m_Projects.find(*d);
300         if (p != m_Projects.end() && p->second.m_External) {
301             p->second.m_External = false;
302             modified = true;
303         }
304     }
305     if (modified) {
306         VerifyExternalDepends();
307     }
308 }
309 
VerifyDataspecProj(void)310 void CProjectItemsTree::VerifyDataspecProj(void)
311 {
312     CProjKey proj_key(CProjKey::eDataSpec, GetApp().GetDataspecProjId());
313     CProjectItemsTree::TProjects::iterator z = m_Projects.find(proj_key);
314     if (z == m_Projects.end()) {
315         return;
316     }
317     CProjItem& dataspec_prj(z->second);
318     list<CDataToolGeneratedSrc>::iterator s;
319     for (s = dataspec_prj.m_DatatoolSources.begin();
320         s != dataspec_prj.m_DatatoolSources.end(); ) {
321         bool found = false;
322         TProjects::const_iterator p;
323         for(p = m_Projects.begin(); !found && p != m_Projects.end(); ++p) {
324             if (p->first.Type() != CProjKey::eDataSpec) {
325                 const CProjItem& project = p->second;
326                 list<CDataToolGeneratedSrc>::const_iterator n;
327                 for (n = project.m_DatatoolSources.begin();
328                     !found && n != project.m_DatatoolSources.end(); ++n) {
329                     found = (*n == *s);
330                 }
331             }
332         }
333         list<CDataToolGeneratedSrc>::iterator prev = s;
334         ++s;
335         if (!found) {
336             dataspec_prj.m_DatatoolSources.erase(prev);
337         }
338     }
339 /*
340     maybe, it is better to keep even an empty one
341     for consistency..
342     if (dataspec_prj.m_DatatoolSources.empty()) {
343         m_Projects.erase(z);
344     }
345 */
346 }
347 
348 
349 //-----------------------------------------------------------------------------
FindCyclesNew(const TProjects & tree,TDependsCycles * cycles)350 void CCyclicDepends::FindCyclesNew(const TProjects& tree,
351                         TDependsCycles*  cycles)
352 {
353     cycles->clear();
354 
355     set< CProjKey> projkeys;
356     TDependsChain chain;
357 
358     // For all projects in tree.
359     ITERATE(TProjects, p, tree) {
360         const CProjKey& proj_id = p->first;
361         if (p->second.m_ProjType == CProjKey::eDll) {
362             continue;
363         }
364         if (AnalyzeProjItemNew(tree, proj_id, projkeys, chain)) {
365             cycles->insert(chain);
366             chain.clear();
367         }
368         _ASSERT(chain.size() == 0);
369         _ASSERT(projkeys.size() == 0);
370     }
371 }
372 
AnalyzeProjItemNew(const TProjects & tree,const CProjKey & proj_id,set<CProjKey> & projkeys,TDependsChain & chain)373 bool CCyclicDepends::AnalyzeProjItemNew(
374     const TProjects& tree,
375     const CProjKey&  proj_id,
376     set< CProjKey>& projkeys,
377     TDependsChain& chain)
378 {
379     if (projkeys.find(proj_id) != projkeys.end()) {
380         chain.push_back(proj_id);
381         return true;
382     }
383     TProjects::const_iterator p = tree.find(proj_id);
384     if (p == tree.end()) {
385         if (!SMakeProjectT::IsConfigurableDefine(proj_id.Id())) {
386             if (!GetApp().GetSite().IsLibWithChoice(proj_id.Id()) ||
387                  GetApp().GetSite().GetChoiceForLib(proj_id.Id()) == CMsvcSite::eLib ) {
388                 string str_chain("Dependency chain: ");
389                 ITERATE(TDependsChain, n, chain) {
390                     str_chain += n->Id();
391                     str_chain += " - ";
392                 }
393                 str_chain += proj_id.Id();
394                 PTB_WARNING_EX(kEmptyStr,ePTB_ProjectNotFound, str_chain << ": Undefined project: " << proj_id.Id() );
395             }
396         }
397         return false;
398     }
399     const CProjItem& project = p->second;
400     TDependsChain::const_iterator i = project.m_Depends.begin();
401     if (i == project.m_Depends.end()) {
402         return false;
403     }
404     chain.push_back(proj_id);
405     projkeys.insert(proj_id);
406     bool found=false;
407     for ( ; !found && i != project.m_Depends.end(); ++i) {
408         if (*i == proj_id) {
409             continue;
410         }
411         found = AnalyzeProjItemNew(tree, *i, projkeys,chain);
412     }
413     if (!found) {
414         chain.pop_back();
415     }
416     projkeys.erase(proj_id);
417     return found;
418 }
419 
420 
FindCycles(const TProjects & tree,TDependsCycles * cycles)421 void CCyclicDepends::FindCycles(const TProjects& tree,
422                                 TDependsCycles*  cycles)
423 {
424     cycles->clear();
425 
426     ITERATE(TProjects, p, tree) {
427         // Look throgh all projects in tree.
428         const CProjKey& project_id = p->first;
429         // If this proj_id was already reported in some cycle,
430         // it's no need to test it again.
431         if ( !IsInAnyCycle(project_id, *cycles) ) {
432             // Analyze for cycles
433             AnalyzeProjItem(project_id, tree, cycles);
434         }
435     }
436 }
437 
438 
IsInAnyCycle(const CProjKey & proj_id,const TDependsCycles & cycles)439 bool CCyclicDepends::IsInAnyCycle(const CProjKey&       proj_id,
440                                   const TDependsCycles& cycles)
441 {
442     ITERATE(TDependsCycles, p, cycles) {
443         const TDependsChain& cycle = *p;
444         if (find(cycle.begin(), cycle.end(), proj_id) != cycle.end())
445             return true;
446     }
447     return false;
448 }
449 
450 
AnalyzeProjItem(const CProjKey & proj_id,const TProjects & tree,TDependsCycles * cycles)451 void CCyclicDepends::AnalyzeProjItem(const CProjKey&  proj_id,
452                                      const TProjects& tree,
453                                      TDependsCycles*  cycles)
454 {
455     TProjects::const_iterator p = tree.find(proj_id);
456     if (p == tree.end()) {
457         PTB_WARNING_EX(kEmptyStr,ePTB_ProjectNotFound,"Undefined project: " << proj_id.Id() );
458         return;
459     }
460 
461     const CProjItem& project = p->second;
462     // No depends - no cycles
463     if ( project.m_Depends.empty() )
464         return;
465 
466     TDependsChains chains;
467     ITERATE(list<CProjKey>, n, project.m_Depends) {
468 
469         // Prepare initial state of depends chains
470         // one depend project in each chain
471         const CProjKey& depend_id = *n;
472         if ( !IsInAnyCycle(depend_id, *cycles) ) {
473             TDependsChain one_chain;
474             one_chain.push_back(depend_id);
475             chains.push_back(one_chain);
476         }
477     }
478     // Try to extend this chains
479     TDependsChain cycle_found;
480     bool cycles_found = ExtendChains(proj_id, tree, &chains, &cycle_found);
481     if ( cycles_found ) {
482         // Report chains as a cycles
483         cycles->insert(cycle_found);
484     }
485 }
486 
487 
ExtendChains(const CProjKey & proj_id,const TProjects & tree,TDependsChains * chains,TDependsChain * cycle_found)488 bool CCyclicDepends::ExtendChains(const CProjKey&  proj_id,
489                                   const TProjects& tree,
490                                   TDependsChains*  chains,
491                                   TDependsChain*   cycle_found)
492 {
493     for (TDependsChains::iterator p = chains->begin();
494           p != chains->end();  ) {
495         // Iterate through all chains
496         TDependsChain& one_chain = *p;
497         // we'll consider last element.
498         const CProjKey& depend_id = one_chain.back();
499         TProjects::const_iterator n = tree.find(depend_id);
500         if (n == tree.end()) {
501             //LOG_POST( Error << "Unknown project: " << depend_id.Id() );
502             //return false;
503             p = chains->erase(p);
504             continue;
505         }
506         const CProjItem& depend_project = n->second;
507         if ( depend_project.m_Depends.empty() ) {
508             // If nobody depends from this project - remove this chain
509             p = chains->erase(p);
510         } else {
511             // We'll create new chains
512             // by adding depend_project dependencies to old_chain
513             TDependsChain old_chain = one_chain;
514             p = chains->erase(p);
515 
516             ITERATE(list<CProjKey>, k, depend_project.m_Depends) {
517                 const CProjKey& new_depend_id = *k;
518                 // add each new depends to the end of the old_chain.
519                 TDependsChain new_chain = old_chain;
520                 new_chain.push_back(new_depend_id);
521                 p = chains->insert(p, new_chain);
522                 ++p;
523             }
524         }
525     }
526     // No chains - no cycles
527     if ( chains->empty() )
528         return false;
529     // got cycles in chains - we done
530     if ( IsCyclic(proj_id, *chains, cycle_found) )
531         return true;
532     // otherwise - continue search.
533     return ExtendChains(proj_id, tree, chains, cycle_found);
534 }
535 
536 
IsCyclic(const CProjKey & proj_id,const TDependsChains & chains,TDependsChain * cycle_found)537 bool CCyclicDepends::IsCyclic(const CProjKey&       proj_id,
538                               const TDependsChains& chains,
539                               TDependsChain*        cycle_found)
540 {
541     // First iteration - we'll try to find project to
542     // consider inside depends chains. If we found - we have a cycle.
543     ITERATE(TDependsChains, p, chains) {
544         const TDependsChain& one_chain = *p;
545         if (find(one_chain.begin(),
546                  one_chain.end(),
547                  proj_id) != one_chain.end()) {
548             *cycle_found = one_chain;
549             return true;
550         }
551     }
552 
553     // We look into all chais
554     ITERATE(TDependsChains, p, chains) {
555         TDependsChain one_chain = *p;
556         TDependsChain original_chain = *p;
557         // remember original size of chain
558         size_t orig_size = one_chain.size();
559         // remove all non-unique elements
560         one_chain.sort();
561         one_chain.unique();
562         // if size of the chain is altered - we have a cycle.
563         if (one_chain.size() != orig_size) {
564             *cycle_found = original_chain;
565             return true;
566         }
567     }
568 
569     // Got nothing - no cycles
570     return false;
571 }
572 
573 
574 //-----------------------------------------------------------------------------
CProjectTreeFolders(const CProjectItemsTree & tree)575 CProjectTreeFolders::CProjectTreeFolders(const CProjectItemsTree& tree)
576 :m_RootParent("/", NULL)
577 {
578     ITERATE(CProjectItemsTree::TProjects, p, tree.m_Projects) {
579 
580         const CProjKey&  project_id = p->first;
581         const CProjItem& project    = p->second;
582 
583         TPath path;
584         CreatePath(GetApp().GetProjectTreeInfo().m_Src,
585                    project.m_SourcesBaseDir,
586                    &path);
587         SProjectTreeFolder* folder = FindOrCreateFolder(path);
588         folder->m_Name = path.back();
589         folder->m_Projects.insert(project_id);
590     }
591 }
592 
593 
594 SProjectTreeFolder*
CreateFolder(SProjectTreeFolder * parent,const string & folder_name)595 CProjectTreeFolders::CreateFolder(SProjectTreeFolder* parent,
596                                   const string&       folder_name)
597 {
598     m_Folders.push_back(SProjectTreeFolder(folder_name, parent));
599     SProjectTreeFolder* inserted = &(m_Folders.back());
600 
601     parent->m_Siblings.insert
602         (SProjectTreeFolder::TSiblings::value_type(folder_name, inserted));
603 
604     return inserted;
605 
606 }
607 
608 SProjectTreeFolder*
FindFolder(const TPath & path)609 CProjectTreeFolders::FindFolder(const TPath& path)
610 {
611     SProjectTreeFolder& folder_i = m_RootParent;
612     ITERATE(TPath, p, path) {
613         const string& node = *p;
614         SProjectTreeFolder::TSiblings::iterator n =
615             folder_i.m_Siblings.find(node);
616         if (n == folder_i.m_Siblings.end())
617             return NULL;
618         folder_i = *(n->second);
619     }
620     return &folder_i;
621 }
622 
623 
624 SProjectTreeFolder*
FindOrCreateFolder(const TPath & path)625 CProjectTreeFolders::FindOrCreateFolder(const TPath& path)
626 {
627     SProjectTreeFolder* folder_i = &m_RootParent;
628     ITERATE(TPath, p, path) {
629         const string& node = *p;
630         SProjectTreeFolder::TSiblings::iterator n = folder_i->m_Siblings.find(node);
631         if (n == folder_i->m_Siblings.end()) {
632             folder_i = CreateFolder(folder_i, node);
633         } else {
634             folder_i = n->second;
635         }
636     }
637     return folder_i;
638 }
639 
640 
CreatePath(const string & root_src_dir,const string & project_base_dir,TPath * path)641 void CProjectTreeFolders::CreatePath(const string& root_src_dir,
642                                      const string& project_base_dir,
643                                      TPath*        path)
644 {
645     path->clear();
646 
647     string rel_dir =
648         CDirEntry::CreateRelativePath(root_src_dir, project_base_dir);
649     string sep(1, CDirEntry::GetPathSeparator());
650     NStr::Split(rel_dir, sep, *path, NStr::fSplit_MergeDelimiters | NStr::fSplit_Truncate);
651 }
652 
653 /////////////////////////////////////////////////////////////////////////////
654 
CMakeNode(void)655 CMakeNode::CMakeNode(void)
656 {
657 }
~CMakeNode()658 CMakeNode::~CMakeNode()
659 {
660 }
CMakeNode(const CMakeNode & other)661 CMakeNode::CMakeNode(const CMakeNode& other)
662 {
663     m_NodeProjects = other.m_NodeProjects;
664     m_NodeSubdirs = other.m_NodeSubdirs;
665     m_NodeDefinitions = other.m_NodeDefinitions;
666     m_NodeIncludes = other.m_NodeIncludes;
667     m_NodeHeaders = other.m_NodeHeaders;
668 }
operator =(const CMakeNode & other)669 CMakeNode& CMakeNode::operator=(const CMakeNode& other)
670 {
671     if (&other != this) {
672         m_NodeProjects = other.m_NodeProjects;
673         m_NodeSubdirs = other.m_NodeSubdirs;
674         m_NodeDefinitions = other.m_NodeDefinitions;
675         m_NodeIncludes = other.m_NodeIncludes;
676         m_NodeHeaders = other.m_NodeHeaders;
677     }
678     return *this;
679 }
680 
AddHeader(const string & name)681 void CMakeNode::AddHeader(const string& name)
682 {
683     m_NodeHeaders.push_back(name);
684 }
685 
AddDefinition(const string & key,const string & value)686 void CMakeNode::AddDefinition(const string& key, const string& value)
687 {
688     m_NodeDefinitions.push_back(key + " " + value);
689 }
690 
AddInclude(const string & name)691 void CMakeNode::AddInclude(const string& name)
692 {
693     m_NodeIncludes.push_back(name);
694 }
695 
AddProject(const string & prj)696 void CMakeNode::AddProject( const string& prj)
697 {
698     m_NodeProjects.insert(prj);
699 }
AddSubdir(const string & dir)700 void CMakeNode::AddSubdir( const string& dir)
701 {
702     m_NodeSubdirs.insert(dir);
703 }
704 
Write(const string & dirname) const705 void CMakeNode::Write(const string& dirname) const
706 {
707     CDir(dirname).CreatePath();
708     string outname(CDirEntry::ConcatPath(dirname, "CMakeLists.txt"));
709     CNcbiOfstream out(outname.c_str());
710     if (!out.is_open()) {
711         return;
712     }
713 
714     ITERATE(vector<string>, s, m_NodeHeaders) {
715         out << *s << endl;
716     }
717     for (vector<string>::const_iterator s = m_NodeDefinitions.begin();
718         s != m_NodeDefinitions.end(); ++s) {
719         out << "set(" << *s << ")" << endl;
720     }
721     ITERATE(vector<string>, s, m_NodeIncludes) {
722         out << "include( " << *s << ")" << endl;
723     }
724     ITERATE(set<string>, s, m_NodeProjects) {
725         out << "include( CMakeLists." << *s << ".txt)" << endl;
726     }
727     ITERATE(set<string>, s, m_NodeSubdirs) {
728         out << "add_subdirectory(" << *s << ")" << endl;
729     }
730 }
731 
732 /////////////////////////////////////////////////////////////////////////////
CMakeProperty(const string & name)733 CMakeProperty::CMakeProperty(const string& name)
734     : m_Propname(name) {
735 }
~CMakeProperty(void)736 CMakeProperty::~CMakeProperty(void) {
737 }
CMakeProperty(const CMakeProperty & other)738 CMakeProperty::CMakeProperty(const CMakeProperty& other) {
739     m_Propname = other.m_Propname;
740     m_Propvalue = other.m_Propvalue;
741 }
operator =(const CMakeProperty & other)742 CMakeProperty CMakeProperty::operator=(const CMakeProperty& other) {
743     m_Propname = other.m_Propname;
744     m_Propvalue = other.m_Propvalue;
745     return *this;
746 }
AddValue(const string & value)747 CMakeProperty& CMakeProperty::AddValue(const string& value) {
748     m_Propvalue.push_back(value);
749     return *this;
750 }
Write(CNcbiOstream & out) const751 void CMakeProperty::Write(CNcbiOstream& out) const {
752     out << m_Propname  << "(" << endl;
753     ITERATE( vector<string>, s, m_Propvalue) {
754         out << "    " << *s << endl;
755     }
756     out << ")" << endl;
757 }
758 
759 /////////////////////////////////////////////////////////////////////////////
760 
CMakeProject()761 CMakeProject::CMakeProject()
762 {
763 }
764 
~CMakeProject(void)765 CMakeProject::~CMakeProject(void)
766 {
767 }
CMakeProject(const CMakeProject & other)768 CMakeProject::CMakeProject(const CMakeProject& other)
769 {
770     m_Prj_key = other.m_Prj_key;
771     m_Definitions = other.m_Definitions;
772     m_CompDefines = other.m_CompDefines;
773     m_CompFlags = other.m_CompFlags;
774     m_Sources = other.m_Sources;
775     m_IncludeDir = other.m_IncludeDir;
776     m_Libraries = other.m_Libraries;
777     m_Dependencies = other.m_Dependencies;
778     m_Properties = other.m_Properties;
779 }
operator =(const CMakeProject & other)780 CMakeProject& CMakeProject::operator=(const CMakeProject& other)
781 {
782     if (&other != this) {
783         m_Prj_key = other.m_Prj_key;
784         m_Definitions = other.m_Definitions;
785         m_CompDefines = other.m_CompDefines;
786         m_CompFlags = other.m_CompFlags;
787         m_Sources = other.m_Sources;
788         m_IncludeDir = other.m_IncludeDir;
789         m_Libraries = other.m_Libraries;
790         m_Dependencies = other.m_Dependencies;
791         m_Properties = other.m_Properties;
792     }
793     return *this;
794 }
795 
SetProjKey(const CProjKey & prj_key)796 void CMakeProject::SetProjKey(const CProjKey& prj_key)
797 {
798     m_Prj_key = prj_key;
799 }
AddDefinition(const string & key,const string & value)800 void CMakeProject::AddDefinition(const string& key, const string& value)
801 {
802     m_Definitions.push_back( key + " " + value);
803 }
AddSourceFile(const string & folder,const string & name)804 void CMakeProject::AddSourceFile(const string& folder, const string& name)
805 {
806 #if 0
807     if (folder.empty()) {
808         m_Sources.insert(name);
809     } else {
810         m_Sources.insert(CDirEntry::ConcatPath(folder, name));
811     }
812 #else
813     m_Sources[folder].insert(name);
814 #endif
815 }
816 
AddCompilationDefine(const string & value)817 void CMakeProject::AddCompilationDefine(const string& value)
818 {
819     m_CompDefines.push_back(value);
820 }
AddCompilationFlag(const string & value)821 void CMakeProject::AddCompilationFlag(const string& value)
822 {
823     m_CompFlags.push_back(value);
824 }
AddIncludeDirectory(const string & name)825 void CMakeProject::AddIncludeDirectory(const string& name)
826 {
827     m_IncludeDir.push_back(name);
828 }
AddLibrary(const string & name)829 void CMakeProject::AddLibrary(const string& name)
830 {
831     m_Libraries.push_back(name);
832 }
AddDependency(const string & name)833 void CMakeProject::AddDependency(const string& name)
834 {
835     m_Dependencies.push_back(name);
836 }
AddProperty(const CMakeProperty & prop)837 void CMakeProject::AddProperty(const CMakeProperty& prop)
838 {
839     m_Properties.push_back(prop);
840 }
841 
842 
Write(const string & dirname) const843 void CMakeProject::Write(const string& dirname) const
844 {
845     string prj_name(CreateProjectName(m_Prj_key));
846     string prj_id(m_Prj_key.Id());
847     string target_output_name(prj_id);
848 
849     if (m_Prj_key.Type() == CProjKey::eApp) {
850         prj_id += ".exe";
851     } else  if (m_Prj_key.Type() == CProjKey::eLib) {
852         if (prj_id == "general") {
853             target_output_name = prj_id = "general-lib";
854         }
855     }
856 
857 
858     CDir(dirname).CreatePath();
859     string outname(CDirEntry::ConcatPath(dirname, "CMakeLists." + prj_name + ".txt"));
860     CNcbiOfstream out(outname.c_str());
861     if (!out.is_open()) {
862         return;
863     }
864 
865     for (vector<string>::const_iterator s = m_Definitions.begin();
866         s != m_Definitions.end(); ++s) {
867         out << "set(" << *s << ")" << endl;
868     }
869     out << endl;
870 
871     if (m_Prj_key.Type() == CProjKey::eLib) {
872         out << "add_library( "  << prj_id << " STATIC" << endl;
873     } else if (m_Prj_key.Type() == CProjKey::eDll) {
874         out << "add_library( " << prj_id << " SHARED" << endl;
875     } else if (m_Prj_key.Type() == CProjKey::eApp) {
876         out << "add_executable( " << prj_id << endl;
877     } else {
878         cerr << "unsupported project type: " << prj_name << endl;
879         return;
880     }
881 #if 0
882     ITERATE (set<string>, s,  m_Sources) {
883 //        out << "    " << CDirEntry::CreateRelativePath(dirname, *s) << endl;
884         out << "    " << *s << endl;
885     }
886 #else
887     for ( map<string, set<string> >::const_iterator s = m_Sources.begin();
888             s != m_Sources.end(); ++s) {
889         ITERATE (set<string>, n,  s->second) {
890 //            out << "    " << *n << endl;
891             out << "    " << CDirEntry::ConcatPath(s->first, *n) << endl;
892         }
893     }
894 #endif
895     out << ")" << endl;
896 #if 0
897     for ( map<string, set<string> >::const_iterator s = m_Sources.begin();
898             s != m_Sources.end(); ++s) {
899         if (!s->second.empty()) {
900             out << "set_source_files_properties(" << endl;
901             ITERATE (set<string>, n,  s->second) {
902                 out << "    " << *n << endl;
903             }
904             out << "    PROPERTIES LOCATION " << s->first << ")" << endl;
905         }
906     }
907 #endif
908 
909 #if 0
910     if (prj_name != prj_id) {
911         out << "set_target_properties(" << prj_name << endl <<
912             " PROPERTIES OUTPUT_NAME " << prj_id << ")" << endl;
913     }
914 #endif
915 
916 #if 0
917     if (!m_IncludeDir.empty()) {
918         out << "include_directories( " << endl;
919         ITERATE (vector<string>, s,  m_IncludeDir) {
920             out << "    " << *s << endl;
921         }
922         out << ")" << endl;
923     }
924 #endif
925     out << "set_target_properties( " << prj_id << " PROPERTIES" << endl;
926     if (target_output_name != prj_id) {
927         out << "    " << "OUTPUT_NAME " << target_output_name << endl;
928     }
929     if (!m_CompDefines.empty()) {
930         out << "    " << "COMPILE_DEFINITIONS \"" << NStr::Join(m_CompDefines,";") << "\"" << endl;
931     }
932 #if 1
933     if (!m_IncludeDir.empty()) {
934         out << "    " << "INCLUDE_DIRECTORIES \"" << NStr::Join(m_IncludeDir, ";") << "\"" << endl;
935     }
936     out << "    " << "COMPILE_FLAGS \""  << NStr::Join(m_CompFlags, " ") << "\"" << endl;
937 #else
938     out << "    " << "COMPILE_FLAGS \"" << NStr::Join(m_IncludeDir, " ") << " " << NStr::Join(m_CompFlags, " ") << "\"" << endl;
939 #endif
940     out << ")" << endl;
941 
942     if (!m_Dependencies.empty()) {
943         out << "add_dependencies( " << prj_id << endl;
944         ITERATE (vector<string>, s,  m_Dependencies) {
945             if (*s == "general") {
946                 out << "    " << "general-lib" << endl;
947             } else {
948                 out << "    " << *s << endl;
949             }
950         }
951         out << ")" << endl;
952     }
953 
954     ITERATE( vector<CMakeProperty>, s, m_Properties) {
955         s->Write(out);
956     }
957 
958     out << "target_link_libraries( " << prj_id << endl;
959     ITERATE (vector<string>, s,  m_Libraries) {
960         if (*s == "general") {
961             out << "    " << "general-lib" << endl;
962         } else {
963             out << "    " << *s << endl;
964         }
965     }
966     out << ")" << endl;
967 
968     string custname("${NCBI_PROJECT_SRC_DIR}/CMakeLists." + prj_name + ".cmake");
969     out << "if(EXISTS "    << custname << ")" << endl;
970     out << "    include( "     << custname << ")" << endl;
971     out << "endif(EXISTS " << custname << ")" << endl;
972     out << endl;
973 }
974 
975 /////////////////////////////////////////////////////////////////////////////
PatchTreeMakefiles(const CProjectItemsTree & projects_tree)976 void CMakefilePatch::PatchTreeMakefiles(const CProjectItemsTree& projects_tree)
977 {
978     const string keyword("USES_LIBRARIES = ");
979     size_t count = 0;
980     CProjBulderApp& theApp = GetApp();
981     ITERATE(CProjectItemsTree::TProjects, p, projects_tree.m_Projects) {
982         const CProjKey& prj_key = p->first;
983 // definitely I should not modify APP projects
984 // I am not sure about DLL ones
985         if (prj_key.Type() != CProjKey::eLib) {
986             continue;
987         }
988         const CProjItem& prj = p->second;
989         map<string, set<string> >::const_iterator deps = theApp.m_GraphDepPrecedes.find(prj_key.Id());
990         if (deps != theApp.m_GraphDepPrecedes.end()) {
991             if (!deps->second.empty()) {
992 
993 cout << "Modify " << prj.m_MkName << endl;
994 // remove previous definition
995                 {
996                     bool modified = false;
997                     string tmpname = CDirEntry::GetTmpName();
998                     CNcbiIfstream ifs(prj.m_MkName.c_str(), IOS_BASE::in | IOS_BASE::binary );
999                     if (ifs.is_open()) {
1000                         CNcbiOfstream ofs(tmpname.c_str(), IOS_BASE::out | IOS_BASE::trunc );
1001                         if (ofs.is_open()) {
1002                             string strline;
1003                             bool found = false;
1004                             while ( NcbiGetlineEOL(ifs, strline) ) {
1005                                 if (found) {
1006                                     found = NStr::EndsWith(strline, "\\");
1007                                     continue;
1008                                 }
1009                                 if (NStr::Find(strline, keyword) != NPOS) {
1010                                     modified = true;
1011                                     found = NStr::EndsWith(strline, "\\");
1012                                     continue;
1013                                 }
1014                                 ofs << strline << endl;
1015                             }
1016                         }
1017                         ifs.close();
1018                         if (modified) {
1019                             if (!CDirEntry(tmpname).Copy(prj.m_MkName , CDirEntry::fCF_Overwrite)) {
1020 cout << "Error while modiying " << prj.m_MkName << endl << CNcbiError::GetLast() << endl;
1021                             }
1022                         }
1023                         CDirEntry(tmpname).Remove();
1024                     }
1025                 }
1026 // add new one
1027                 {
1028                     CNcbiOfstream ofs(prj.m_MkName.c_str(), IOS_BASE::out | IOS_BASE::app );
1029                     if (ofs.is_open()) {
1030                         ofs << keyword;
1031                         string libdep;
1032                         size_t len=80;
1033                         ITERATE( set<string>, s, deps->second) {
1034 #if 0
1035                             CProjKey t(CProjKey::eLib, *s);
1036                             if (projects_tree.m_Projects.find(t) == projects_tree.m_Projects.end()) {
1037 cout << "WARNING: library not found: " << *s << endl;
1038                             }
1039 #endif
1040                             libdep += " " + *s;
1041 		                    if (len > 60) {
1042         	                    ofs << " \\" << endl << "   ";
1043 		                        len = 0;
1044 		                    }
1045                             ofs << " " << *s;
1046                             len += s->size() + 1;
1047                         }
1048                         ofs << endl;
1049 cout << "USES_LIBRARIES =" << libdep << endl;
1050                     }
1051                 }
1052             }
1053             ++count;
1054         }
1055     }
1056 cout << "Count files to modify: " << count << endl;
1057 }
1058 
1059 
1060 END_NCBI_SCOPE
1061