1 /*
2  * This file is part of the FortranProject plugin for Code::Blocks IDE
3  * and licensed under the GNU General Public License, version 3
4  * http://www.gnu.org/licenses/gpl-3.0.html
5  *
6  * Author: Darius Markauskas
7  *
8  */
9 
10 #include "projectdependencies.h"
11 
12 #include <sdk.h>
13 #ifndef CB_PRECOMP
14     #include <wx/dir.h>
15     #include <wx/filefn.h>
16 
17     #include <compiler.h>
18     #include <compilerfactory.h>
19     #include <logmanager.h>
20     #include <macrosmanager.h>
21     #include <projectmanager.h>
22 #endif
23 
24 #include "nativeparserf.h"
25 
26 
ProjectDependencies(cbProject * project)27 ProjectDependencies::ProjectDependencies(cbProject* project)
28 {
29     m_Project = project;
30     //ctor
31 }
32 
~ProjectDependencies()33 ProjectDependencies::~ProjectDependencies()
34 {
35     //dtor
36     Clear();
37 }
38 
Clear()39 void ProjectDependencies::Clear()
40 {
41     m_prFilesArr.clear();
42     m_FileIndexMap.clear();
43     for (size_t i=0; i<m_pUseModules.size(); i++)
44     {
45         delete m_pUseModules[i];
46     }
47     m_pUseModules.clear();
48     for (size_t i=0; i<m_pDeclaredModules.size(); i++)
49     {
50         delete m_pDeclaredModules[i];
51     }
52     m_pDeclaredModules.clear();
53     for (size_t i=0; i<m_pExtendsSModules.size(); i++)
54     {
55         delete m_pExtendsSModules[i];
56     }
57     m_pExtendsSModules.clear();
58     for (size_t i=0; i<m_pDeclaredSubmodules.size(); i++)
59     {
60         delete m_pDeclaredSubmodules[i];
61     }
62     m_pDeclaredSubmodules.clear();
63 
64     for (size_t i=0; i<m_pIncludes.size(); i++)
65     {
66         delete m_pIncludes[i];
67     }
68     m_pIncludes.clear();
69 
70     m_ModuleFileIdxMap.clear();
71     m_SubmoduleFileIdxMap.clear();
72     m_IncludeFileIdxMap.clear();
73 
74     for (size_t i=0; i<m_ChildrenTable.size(); i++)
75     {
76         delete m_ChildrenTable[i];
77     }
78     m_ChildrenTable.clear();
79     m_WasInfiniteLoop = false;
80     m_FileWeights.Empty();
81 
82     m_MadeChildrenSet.clear();
83 }
84 
MakeProjectFilesDependencies(ProjectFilesArray & prFilesArr,ParserF & parser)85 void ProjectDependencies::MakeProjectFilesDependencies(ProjectFilesArray& prFilesArr, ParserF& parser)
86 {
87     Clear();
88 
89     m_prFilesArr = prFilesArr;
90     wxArrayString fnames;
91 
92     size_t nfil = m_prFilesArr.size();
93 	for (size_t i = 0; i < nfil; ++i)
94 	{
95         ProjectFile* pf = m_prFilesArr[i];
96         wxString ffp = pf->file.GetFullPath();
97         m_FileIndexMap.insert(std::make_pair(ffp,i));
98 
99         wxString fname = pf->file.GetName() + _T(".") + pf->file.GetExt();
100         fnames.Add(fname);
101 	}
102 
103 	for (size_t i = 0; i < nfil; ++i)
104 	{
105         StringSet* fileUseModules = new StringSet;
106         StringSet* fileDeclaredModules = new StringSet;
107         StringSet* fileExtendsSModules = new StringSet;
108         StringSet* fileDeclaredSubmodules = new StringSet;
109         StringSet* fileIncludes = new StringSet;
110 
111         parser.ObtainUsedDeclaredModules(m_prFilesArr[i]->file.GetFullPath(), fileUseModules, fileDeclaredModules,
112                                          fileExtendsSModules, fileDeclaredSubmodules, fileIncludes);
113         m_pUseModules.push_back(fileUseModules);
114         m_pDeclaredModules.push_back(fileDeclaredModules);
115         m_pExtendsSModules.push_back(fileExtendsSModules);
116         m_pDeclaredSubmodules.push_back(fileDeclaredSubmodules);
117         m_pIncludes.push_back(fileIncludes);
118 
119         StringSet::iterator pos;
120 	    for (pos = fileDeclaredModules->begin(); pos != fileDeclaredModules->end(); ++pos)
121 	    {
122 	        if (!m_ModuleFileIdxMap.count(*pos))
123                 m_ModuleFileIdxMap[*pos] = i;
124 	    }
125 
126 	    for (pos = fileDeclaredSubmodules->begin(); pos != fileDeclaredSubmodules->end(); ++pos)
127 	    {
128 	        if (!m_SubmoduleFileIdxMap.count(*pos))
129                 m_SubmoduleFileIdxMap[*pos] = i;
130 	    }
131 
132 	    for (pos = fileIncludes->begin(); pos != fileIncludes->end(); ++pos)
133 	    {
134 	        wxString inc = *pos;
135 	        for (size_t j=0; j < nfil; ++j)
136 	        {
137 	            wxString fn = fnames.Item(j);
138 	            if (inc.IsSameAs(fn) || inc.IsSameAs(fn.BeforeLast('.')))
139 	            {
140 	                m_IncludeFileIdxMap[*pos] = j;
141 	                break;
142 	            }
143 	        }
144 	    }
145 	}
146 
147     m_MadeChildrenSet.resize(nfil,false);
148     m_ChildrenTable.resize(nfil);
149 
150 	for (size_t i = 0; i < nfil; ++i)
151 	{
152 	    if (m_MadeChildrenSet[i] == false)
153         {
154             IntSet* children = new IntSet;
155             m_Deep = 0;
156             m_BreakChain = false;
157             MakeFileChildren(children, i);
158             m_ChildrenTable[i] = children;
159         }
160 	}
161 
162     //PrintChildrenTable();
163 
164     m_FileWeights.Alloc(nfil);
165     m_FileWeights.Add(-1,nfil);
166 }
167 
168 
HasInfiniteDependences()169 bool ProjectDependencies::HasInfiniteDependences()
170 {
171     return m_WasInfiniteLoop;
172 }
173 
174 
GetFileWeight(wxString & fileName)175 unsigned short int ProjectDependencies::GetFileWeight(wxString& fileName)
176 {
177     if (!m_FileIndexMap.count(fileName))
178         return 50; //error. default value
179 
180     m_Deep = 0;
181     m_BreakChain = false;
182     unsigned short int wt = GetFileWeightByIndex(m_FileIndexMap[fileName]);
183     return wt;
184 }
185 
186 
GetFileWeightByIndex(size_t idx)187 unsigned short int ProjectDependencies::GetFileWeightByIndex(size_t idx)
188 {
189     if (m_FileWeights[idx] != -1)
190         return m_FileWeights[idx];
191     if (m_Deep > 99)
192     {
193         m_WasInfiniteLoop = true;
194         m_BreakChain = true;
195         return 0;
196     }
197     else if (m_BreakChain)
198     {
199         return 0;
200     }
201     unsigned short int wt;
202     unsigned short int wt_max = 0;
203     StringSet* fileUseModules = m_pUseModules[idx];
204     StringSet::iterator pos;
205     for (pos = fileUseModules->begin(); pos != fileUseModules->end(); ++pos)
206     {
207         if (m_ModuleFileIdxMap.count(*pos) != 1)
208         {
209             continue;
210         }
211         else
212         {
213             size_t fidx = m_ModuleFileIdxMap[*pos];
214             if (fidx == idx)
215                 continue; // module defined and is used in the same file.
216             m_Deep++;
217             wt = 1 + GetFileWeightByIndex(fidx);
218             m_Deep--;
219 
220             if (wt > wt_max)
221                 wt_max = wt;
222         }
223     }
224 
225     StringSet* fileExtendsSModules = m_pExtendsSModules[idx];
226     for (pos = fileExtendsSModules->begin(); pos != fileExtendsSModules->end(); ++pos)
227     {
228         size_t fidx;
229         bool found = false;
230         if (m_ModuleFileIdxMap.count(*pos) == 1)
231         {
232             fidx = m_ModuleFileIdxMap[*pos];
233             found = true;
234         }
235         else if (m_SubmoduleFileIdxMap.count(*pos) == 1)
236         {
237             fidx = m_SubmoduleFileIdxMap[*pos];
238             found = true;
239         }
240 
241         if (!found)
242             continue;
243         else
244         {
245             if (fidx == idx)
246                 continue; // module defined and is used in the same file.
247             m_Deep++;
248             wt = 1 + GetFileWeightByIndex(fidx);
249             m_Deep--;
250 
251             if (wt > wt_max)
252                 wt_max = wt;
253         }
254     }
255 
256     StringSet* fileIncludes = m_pIncludes[idx];
257     for (pos = fileIncludes->begin(); pos != fileIncludes->end(); ++pos)
258     {
259         if (m_IncludeFileIdxMap.count(*pos) != 1)
260         {
261             continue;
262         }
263         else
264         {
265             size_t fidx = m_IncludeFileIdxMap[*pos];
266             if (fidx == idx)
267                 continue; // error, includes self
268             m_Deep++;
269             wt = 1 + GetFileWeightByIndex(fidx);
270             m_Deep--;
271 
272             if (wt > wt_max)
273                 wt_max = wt;
274         }
275     }
276     m_FileWeights[idx] = wt_max;
277     return wt_max;
278 }
279 
MakeFileChildren(IntSet * children,size_t fileIndex)280 void ProjectDependencies::MakeFileChildren(IntSet* children, size_t fileIndex)
281 {
282     if (m_Deep > 99)
283     {
284         m_BreakChain = true;
285         return; // maybe infinite reference loop?
286     }
287     else if (m_BreakChain)
288     {
289         return;
290     }
291 
292     StringSet* fileDeclaredModules = m_pDeclaredModules[fileIndex];
293     StringSet::iterator pos;
294     for (pos = fileDeclaredModules->begin(); pos != fileDeclaredModules->end(); ++pos)
295     {
296         wxString modName = *pos;
297         size_t nUseModules = m_pUseModules.size();
298         for (size_t k=0; k < nUseModules; ++k)
299         {
300             if (fileIndex==k)
301                 continue; // declared and used in the same file
302 
303             if (m_pUseModules[k]->count(modName))
304             {
305                 children->insert(k);
306 
307                 if (m_MadeChildrenSet[k] == true)
308                 {
309                     children->insert(m_ChildrenTable[k]->begin(),m_ChildrenTable[k]->end());
310                 }
311                 else
312                 {
313                     IntSet* childrenNew = new IntSet;
314                     m_Deep++;
315                     MakeFileChildren(childrenNew, k);
316                     m_Deep--;
317                     children->insert(childrenNew->begin(),childrenNew->end());
318                     m_ChildrenTable[k] = childrenNew;
319                     m_MadeChildrenSet[k] = true;
320                 }
321             }
322         }
323 
324         size_t nExtendsSMod = m_pExtendsSModules.size();
325         for (size_t k=0; k < nExtendsSMod; ++k)
326         {
327             if (fileIndex==k)
328                 continue; // declared and used in the same file
329 
330             if (m_pExtendsSModules[k]->count(modName))
331             {
332                 children->insert(k);
333 
334                 if (m_MadeChildrenSet[k] == true)
335                 {
336                     children->insert(m_ChildrenTable[k]->begin(),m_ChildrenTable[k]->end());
337                 }
338                 else
339                 {
340                     IntSet* childrenNew = new IntSet;
341                     m_Deep++;
342                     MakeFileChildren(childrenNew, k);
343                     m_Deep--;
344                     children->insert(childrenNew->begin(),childrenNew->end());
345                     m_ChildrenTable[k] = childrenNew;
346                     m_MadeChildrenSet[k] = true;
347                 }
348             }
349         }
350     }
351 
352     StringSet* fileDeclaredSubmodules = m_pDeclaredSubmodules[fileIndex];
353     for (pos = fileDeclaredSubmodules->begin(); pos != fileDeclaredSubmodules->end(); ++pos)
354     {
355         wxString submodName = *pos;
356         size_t nExtendsSMod = m_pExtendsSModules.size();
357         for (size_t k=0; k < nExtendsSMod; ++k)
358         {
359             if (fileIndex==k)
360                 continue; // declared and used in the same file
361 
362             if (m_pExtendsSModules[k]->count(submodName))
363             {
364                 children->insert(k);
365 
366                 if (m_MadeChildrenSet[k] == true)
367                 {
368                     children->insert(m_ChildrenTable[k]->begin(),m_ChildrenTable[k]->end());
369                 }
370                 else
371                 {
372                     IntSet* childrenNew = new IntSet;
373                     m_Deep++;
374                     MakeFileChildren(childrenNew, k);
375                     m_Deep--;
376                     children->insert(childrenNew->begin(),childrenNew->end());
377                     m_ChildrenTable[k] = childrenNew;
378                     m_MadeChildrenSet[k] = true;
379                 }
380             }
381         }
382     }
383 
384     ProjectFile* pf = m_prFilesArr[fileIndex];
385     wxString fname = pf->file.GetName();
386     wxString fnameExt = fname + _T(".") + pf->file.GetExt();
387     size_t nIncludes = m_pIncludes.size();
388     for (size_t k=0; k < nIncludes; ++k)
389     {
390         if (fileIndex==k)
391             continue;
392 
393         if (m_pIncludes[k]->count(fname) || m_pIncludes[k]->count(fnameExt))
394         {
395             children->insert(k);
396 
397             if (m_MadeChildrenSet[k] == true)
398             {
399                 children->insert(m_ChildrenTable[k]->begin(),m_ChildrenTable[k]->end());
400             }
401             else
402             {
403                 IntSet* childrenNew = new IntSet;
404                 m_Deep++;
405                 MakeFileChildren(childrenNew, k);
406                 m_Deep--;
407                 children->insert(childrenNew->begin(),childrenNew->end());
408                 m_ChildrenTable[k] = childrenNew;
409                 m_MadeChildrenSet[k] = true;
410             }
411         }
412     }
413 }
414 
EnsureUpToDateObjs()415 void ProjectDependencies::EnsureUpToDateObjs()
416 {
417     size_t nfile = m_prFilesArr.size();
418     for (size_t j=0; j<nfile; ++j)
419     {
420         ProjectFile* pf = m_prFilesArr[j];
421         const wxArrayString& btarr = pf->GetBuildTargets();
422         if (btarr.IsEmpty())
423             continue;
424         ProjectBuildTarget* bTarget = m_Project->GetBuildTarget(btarr[0]);
425         const pfDetails& pfd = pf->GetFileDetails(bTarget);
426         time_t time_src = wxFileModificationTime(pfd.source_file_absolute_native);
427 
428         IntSet* children = m_ChildrenTable[j];
429         IntSet::iterator pos;
430         for (pos=children->begin(); pos != children->end(); ++pos)
431         {
432             ProjectFile* pfChild = m_prFilesArr[*pos];
433 
434             const wxArrayString& btChild_arr = pfChild->GetBuildTargets();
435             size_t nChTag = btChild_arr.size();
436             for (size_t iCh=0; iCh < nChTag; ++iCh)
437             {
438                 ProjectBuildTarget* bTargetChild = m_Project->GetBuildTarget(btChild_arr[iCh]);
439                 Compiler* compilerChild = CompilerFactory::GetCompiler(bTargetChild->GetCompilerID());
440                 if(!compilerChild)
441                     continue;
442 
443                 const pfDetails& pfdChild = pfChild->GetFileDetails(bTargetChild);
444                 wxString objectAbsChild = (compilerChild->GetSwitches().UseFlatObjects)?
445                                        pfdChild.object_file_flat_absolute_native : pfdChild.object_file_absolute_native;
446 
447                 if (wxFileExists(objectAbsChild))
448                 {
449                     time_t time_obj = wxFileModificationTime(objectAbsChild);
450                     if (time_src > time_obj)
451                     {
452                         wxRemoveFile(objectAbsChild);
453                     }
454                 }
455             }
456         }
457     }
458 }
459 
RemoveModFiles(cbProject * pr,ProjectBuildTarget * bTarget,NativeParserF * nativeParser)460 void ProjectDependencies::RemoveModFiles(cbProject* pr, ProjectBuildTarget* bTarget, NativeParserF* nativeParser)
461 {
462     //Remove all *.mod files
463     if (!pr || !bTarget || pr->IsMakefileCustom())
464         return;
465 
466     wxString comID = bTarget->GetCompilerID();
467     if (!CompilerFactory::CompilerInheritsFrom(comID, _T("gfortran")) &&
468         !CompilerFactory::CompilerInheritsFrom(comID, _T("g95")) &&
469         !CompilerFactory::CompilerInheritsFrom(comID, _T("ifcwin")) &&
470         !CompilerFactory::CompilerInheritsFrom(comID, _T("ifclin")) &&
471         !CompilerFactory::CompilerInheritsFrom(comID, _T("pgfortran")) &&
472         !CompilerFactory::CompilerInheritsFrom(comID, _T("oracfortran")) )
473     {
474         bool haveFortran = false;
475         for (FilesList::iterator it = pr->GetFilesList().begin(); it != pr->GetFilesList().end(); ++it)
476         {
477             ProjectFile* prjfile = *it;
478             if (nativeParser->IsFileFortran(prjfile->file.GetFullPath()))
479             {
480                 haveFortran = true;
481                 break;
482             }
483         }
484         if (!haveFortran)
485             return;
486     }
487 
488     wxString objDir = bTarget->GetBasePath() + bTarget->GetObjectOutput();
489     wxDir odir;
490     if (odir.Open(objDir))
491     {
492         wxString filename;
493         wxFileName fname;
494         fname.AssignDir(objDir);
495         wxString filespec = _T("*.mod");
496         bool cont = odir.GetFirst(&filename, filespec, wxDIR_FILES);
497         while (cont)
498         {
499             fname.SetFullName(filename);
500             wxRemoveFile(fname.GetFullPath());
501             cont = odir.GetNext(&filename);
502         }
503 
504         filespec = _T("*.smod");
505         cont = odir.GetFirst(&filename, filespec, wxDIR_FILES);
506         while (cont)
507         {
508             fname.SetFullName(filename);
509             wxRemoveFile(fname.GetFullPath());
510             cont = odir.GetNext(&filename);
511         }
512     }
513 }
514 
RemoveModFilesWS(NativeParserF * nativeParser)515 void ProjectDependencies::RemoveModFilesWS(NativeParserF* nativeParser)
516 {
517     //Remove all *.mod files in Workspace
518     ProjectsArray* projects = Manager::Get()->GetProjectManager()->GetProjects();
519     for (size_t i = 0; i < projects->GetCount(); ++i)
520     {
521         cbProject* pr = projects->Item(i);
522         if (!pr->IsMakefileCustom())
523         {
524             ProjectBuildTarget* bTarget = pr->GetBuildTarget(pr->GetActiveBuildTarget());
525             RemoveModFiles(pr, bTarget, nativeParser);
526         }
527     }
528 }
529 
PrintChildrenTable()530 void ProjectDependencies::PrintChildrenTable()
531 {
532     Manager::Get()->GetLogManager()->DebugLog(_T("\nProjectDependencies::PrintChildrenTable"));
533 
534     for(size_t i=0; i < m_ChildrenTable.size(); i++)
535     {
536         ProjectFile* pfile = m_prFilesArr[i];
537 
538         Manager::Get()->GetLogManager()->DebugLog(_T("\n")+pfile->file.GetName());
539 
540         IntSet* children = m_ChildrenTable[i];
541         IntSet::iterator pos;
542         for (pos=children->begin(); pos != children->end(); ++pos)
543         {
544             ProjectFile* pf = m_prFilesArr[*pos];
545             wxString fname = pf->file.GetName();
546             Manager::Get()->GetLogManager()->DebugLog(_T("        ")+fname);
547         }
548     }
549 }
550 
GetSizeFiles()551 size_t ProjectDependencies::GetSizeFiles()
552 {
553     return m_prFilesArr.size();
554 }
555 
GetUseFilesFile(const wxString & filename,wxArrayString & useFiles)556 void ProjectDependencies::GetUseFilesFile(const wxString& filename, wxArrayString& useFiles)
557 {
558     if (m_FileIndexMap.count(filename) == 0)
559         return;
560     size_t fileIndex = m_FileIndexMap[filename];
561     StringSet* useModules = m_pUseModules[fileIndex];
562     std::set<size_t> fidxSet;
563     StringSet::iterator pos;
564     for (pos = useModules->begin(); pos != useModules->end(); ++pos)
565     {
566         wxString modName = *pos;
567         if (m_ModuleFileIdxMap.count(modName) == 1)
568         {
569             size_t fidx = m_ModuleFileIdxMap[modName];
570             if (fidx != fileIndex && fidxSet.count(fidx) == 0)
571             {
572                 StringIntMap::const_iterator it;
573                 for (it = m_FileIndexMap.begin(); it != m_FileIndexMap.end(); ++it)
574                 {
575                     if (it->second == (int) fidx)
576                     {
577                         useFiles.Add(it->first);
578                         fidxSet.insert(fidx);
579                         break;
580                     }
581                 }
582             }
583         }
584     }
585 }
586 
GetExtendsFilesFile(const wxString & filename,wxArrayString & extFiles)587 void ProjectDependencies::GetExtendsFilesFile(const wxString& filename, wxArrayString& extFiles)
588 {
589     if (m_FileIndexMap.count(filename) == 0)
590         return;
591     size_t fileIndex = m_FileIndexMap[filename];
592     StringSet* extSModules = m_pExtendsSModules[fileIndex];
593     std::set<size_t> fidxSet;
594     StringSet::iterator pos;
595     for (pos = extSModules->begin(); pos != extSModules->end(); ++pos)
596     {
597         wxString modName = *pos;
598         size_t fidx;
599         if (m_ModuleFileIdxMap.count(modName) == 1)
600             fidx = m_ModuleFileIdxMap[modName];
601         else if (m_SubmoduleFileIdxMap.count(modName) == 1)
602             fidx = m_SubmoduleFileIdxMap[modName];
603         else
604             continue;
605 
606         if (fidx != fileIndex && fidxSet.count(fidx) == 0)
607         {
608             StringIntMap::const_iterator it;
609             for (it = m_FileIndexMap.begin(); it != m_FileIndexMap.end(); ++it)
610             {
611                 if (it->second == (int) fidx)
612                 {
613                     extFiles.Add(it->first);
614                     fidxSet.insert(fidx);
615                     break;
616                 }
617             }
618         }
619     }
620 }
621 
GetIncludeFilesFile(const wxString & filename,wxArrayString & includeFiles)622 void ProjectDependencies::GetIncludeFilesFile(const wxString& filename, wxArrayString& includeFiles)
623 {
624     if (m_FileIndexMap.count(filename) == 0)
625         return;
626     size_t fileIndex = m_FileIndexMap[filename];
627     StringSet* incls = m_pIncludes[fileIndex];
628     std::set<size_t> fidxSet;
629     StringSet::iterator pos;
630     for (pos = incls->begin(); pos != incls->end(); ++pos)
631     {
632         wxString incName = *pos;
633         if (m_IncludeFileIdxMap.count(incName) == 1)
634         {
635             size_t fidx = m_IncludeFileIdxMap[incName];
636             if (fidx != fileIndex && fidxSet.count(fidx) == 0)
637             {
638                 StringIntMap::const_iterator it;
639                 for (it = m_FileIndexMap.begin(); it != m_FileIndexMap.end(); ++it)
640                 {
641                     if (it->second == (int) fidx)
642                     {
643                         includeFiles.Add(it->first);
644                         fidxSet.insert(fidx);
645                         break;
646                     }
647                 }
648             }
649         }
650     }
651 }
652