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