1 /* $Id: msvc_dlls_info.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 "msvc_dlls_info.hpp"
32 #include "proj_builder_app.hpp"
33 #include "msvc_prj_defines.hpp"
34 #include "proj_projects.hpp"
35 #include "proj_tree_builder.hpp"
36 #include "msvc_project_context.hpp"
37 #include "msvc_prj_files_collector.hpp"
38 #include "msvc_dlls_info_utils.hpp"
39 #include "ptb_err_codes.hpp"
40 
41 #include <corelib/ncbistre.hpp>
42 
43 #include <algorithm>
44 
45 BEGIN_NCBI_SCOPE
46 
47 
48 //-----------------------------------------------------------------------------
FilterOutDllHostedProjects(const CProjectItemsTree & tree_src,CProjectItemsTree * tree_dst)49 void FilterOutDllHostedProjects(const CProjectItemsTree& tree_src,
50                                 CProjectItemsTree*       tree_dst)
51 {
52     tree_dst->m_RootSrc = tree_src.m_RootSrc;
53 
54     tree_dst->m_Projects.clear();
55     ITERATE(CProjectItemsTree::TProjects, p, tree_src.m_Projects) {
56 
57         const CProjKey&  proj_id = p->first;
58         const CProjItem& project = p->second;
59 
60         if (proj_id.Type() == CProjKey::eDll) {
61             continue;
62         }
63         bool dll_hosted =
64             (proj_id.Type() == CProjKey::eLib) &&
65             !project.m_DllHost.empty();
66         if ( !dll_hosted) {
67             tree_dst->m_Projects[proj_id] = project;
68         }
69     }
70 }
71 
s_IsInTree(CProjKey::TProjType proj_type,const string & proj_id,const CProjectItemsTree * tree)72 static bool s_IsInTree(CProjKey::TProjType      proj_type,
73                        const string&            proj_id,
74                        const CProjectItemsTree* tree)
75 {
76     return tree->m_Projects.find
77                   (CProjKey(proj_type,
78                             proj_id)) !=
79                                     tree->m_Projects.end();
80 }
81 
82 
s_InitalizeDllProj(const string & dll_id,CProjItem * dll,const CProjectItemsTree & tree_src,CProjectItemsTree * tree_dst)83 static void s_InitalizeDllProj(const string&                  dll_id,
84                                CProjItem*                     dll,
85                                const CProjectItemsTree&       tree_src,
86                                CProjectItemsTree*             tree_dst)
87 {
88     list<CProjKey> new_depends;
89     ITERATE(list<CProjKey>, p, dll->m_Depends) {
90 
91         const string& depend_id = p->Id();
92         const CProjectItemsTree* tree;
93 
94         // Is this a dll?
95         if ( s_IsInTree(CProjKey::eDll, depend_id, tree = &tree_src) ||
96              s_IsInTree(CProjKey::eDll, depend_id, tree = &GetApp().GetWholeTree())) {
97             new_depends.push_back(CProjKey(CProjKey::eDll, depend_id));
98         } else  {
99             if ( s_IsInTree(CProjKey::eApp, depend_id, tree = &tree_src) ||
100                  s_IsInTree(CProjKey::eApp, depend_id, tree = &GetApp().GetWholeTree()) ) {
101 
102                 CProjKey depend_key(CProjKey::eApp, depend_id);
103                 new_depends.push_back(depend_key);
104                 tree_dst->m_Projects[depend_key] =
105                     (tree->m_Projects.find(depend_key))->second;
106             }
107             else if ( s_IsInTree(CProjKey::eLib, depend_id, tree = &tree_src) ||
108                       s_IsInTree(CProjKey::eLib, depend_id, tree = &GetApp().GetWholeTree()) ) {
109 
110                 string dll_host = tree->m_Projects.find(CProjKey(CProjKey::eLib, depend_id))->second.m_DllHost;
111                 if (!dll_host.empty()) {
112                     new_depends.push_back(CProjKey(CProjKey::eDll, dll_host));
113                 } else {
114                     CProjKey depend_key(CProjKey::eLib, depend_id);
115                     new_depends.push_back(depend_key);
116                     tree_dst->m_Projects[depend_key] =
117                         (tree->m_Projects.find(depend_key))->second;
118                 }
119 
120             } else  {
121                 if (GetApp().m_AddMissingLibs) {
122                     CProjKey depend_key(CProjKey::eLib, depend_id);
123                     new_depends.push_back(depend_key);
124                 }
125                 PTB_WARNING_EX(dll_id, ePTB_MissingDependency,
126                             "Missing dependency: " << depend_id);
127             }
128         }
129     }
130     dll->m_Depends = new_depends;
131 
132     if (CMsvc7RegSettings::GetMsvcPlatform() < CMsvc7RegSettings::eUnix) {
133         string dll_main = GetApp().GetProjectTreeInfo().m_Compilers;
134         dll_main = CDirEntry::ConcatPath(dll_main, GetApp().GetRegSettings().m_CompilersSubdir);
135         dll_main = CDirEntry::ConcatPath(dll_main, GetApp().GetBuildType().GetTypeStr());
136         dll_main = CDirEntry::ConcatPath(dll_main, "dll_main");
137         dll->m_Sources.push_back(CDirEntry::CreateRelativePath(dll->m_SourcesBaseDir, dll_main));
138     }
139 }
140 
141 
s_AddProjItemToDll(const CProjectItemsTree & tree_src,const CProjItem & lib,CProjItem & dll)142 static void s_AddProjItemToDll(const CProjectItemsTree& tree_src,
143     const CProjItem& lib, CProjItem& dll)
144 {
145     // If this library is available as a third-party,
146     // then we'll require it
147     if (GetApp().GetSite().GetChoiceForLib(lib.m_ID)
148                                                    == CMsvcSite::e3PartyLib ) {
149         CMsvcSite::SLibChoice choice =
150             GetApp().GetSite().GetLibChoiceForLib(lib.m_ID);
151         dll.m_Requires.push_back(choice.m_3PartyLib);
152         dll.m_Requires.sort();
153         dll.m_Requires.unique();
154         return;
155     }
156     if (!lib.m_External) {
157         dll.m_External = false;
158     }
159     if (lib.m_StyleObjcpp) {
160         dll.m_StyleObjcpp = true;
161     }
162 
163     CMsvcPrjProjectContext lib_context(lib);
164     // Define empty configuration list -- to skip configurable file
165     // generation on this step. They will be generated later.
166     const list<SConfigInfo> no_configs;
167     CMsvcPrjFilesCollector collector(lib_context, no_configs, lib);
168 
169     // Sources - all pathes are relative to one dll->m_SourcesBaseDir
170     ITERATE(list<string>, p, collector.SourceFiles()) {
171         const string& rel_path = *p;
172         string abs_path =
173             CDirEntry::ConcatPath(lib_context.ProjectDir(), rel_path);
174         abs_path = CDirEntry::NormalizePath(abs_path);
175 
176         // Register DLL source files as belongs to lib
177         // With .ext
178         GetApp().GetDllFilesDistr().RegisterSource
179             (abs_path,
180              CProjKey(CProjKey::eDll, dll.m_ID),
181              CProjKey(CProjKey::eLib, lib.m_ID) );
182 
183         string dir;
184         string base;
185         CDirEntry::SplitPath(abs_path, &dir, &base);
186         string abs_source_path = dir + base;
187 
188         string new_rel_path =
189             CDirEntry::CreateRelativePath(dll.m_SourcesBaseDir,
190                                           abs_source_path);
191         dll.m_Sources.push_back(new_rel_path);
192     }
193     dll.m_Sources.sort();
194     dll.m_Sources.unique();
195 
196     copy(lib_context.IncludeDirsAbs().begin(),
197          lib_context.IncludeDirsAbs().end(), back_inserter(dll.m_Includes));
198     copy(lib_context.InlineDirsAbs().begin(),
199          lib_context.InlineDirsAbs().end(), back_inserter(dll.m_Inlines));
200 
201     // Header files - also register them
202     ITERATE(list<string>, p, collector.HeaderFiles()) {
203         const string& rel_path = *p;
204         string abs_path =
205             CDirEntry::ConcatPath(lib_context.ProjectDir(), rel_path);
206         abs_path = CDirEntry::NormalizePath(abs_path);
207         GetApp().GetDllFilesDistr().RegisterHeader
208             (abs_path,
209              CProjKey(CProjKey::eDll, dll.m_ID),
210              CProjKey(CProjKey::eLib, lib.m_ID) );
211     }
212     // Inline files - also register them
213     ITERATE(list<string>, p, collector.InlineFiles()) {
214         const string& rel_path = *p;
215         string abs_path =
216             CDirEntry::ConcatPath(lib_context.ProjectDir(), rel_path);
217         abs_path = CDirEntry::NormalizePath(abs_path);
218         GetApp().GetDllFilesDistr().RegisterInline
219             (abs_path,
220              CProjKey(CProjKey::eDll, dll.m_ID),
221              CProjKey(CProjKey::eLib, lib.m_ID) );
222     }
223 
224     if (!collector.GetExtraFiles().empty()) {
225         const map<string, list<string> >& extra(collector.GetExtraFiles());
226         for (map<string, list<string> >::const_iterator g = extra.begin(); g != extra.end(); ++g) {
227             const list<string>& lst(g->second);
228 
229             ITERATE(list<string>, f, lst) {
230                 string abs_path = CDirEntry::NormalizePath(
231                     CDirEntry::ConcatPath(lib_context.ProjectDir(), *f));
232 
233                 GetApp().GetDllFilesDistr().RegisterExtraFile
234                     (abs_path,
235                      CProjKey(CProjKey::eDll, dll.m_ID),
236                      CProjKey(CProjKey::eLib, lib.m_ID) );
237 
238                 string new_rel_path =
239                     CDirEntry::CreateRelativePath(dll.m_SourcesBaseDir,  abs_path);
240                 (dll.m_ExtraFiles[g->first]).push_back(new_rel_path);
241             }
242         }
243     }
244     copy(lib_context.GetCustomBuildInfo().begin(),
245          lib_context.GetCustomBuildInfo().end(), back_inserter(dll.m_CustomBuild));
246 
247     // Depends
248     ITERATE(list<CProjKey>, p, lib.m_Depends) {
249 
250         const CProjKey& depend_id = *p;
251 
252         CProjectItemsTree::TProjects::const_iterator i = tree_src.m_Projects.find(depend_id);
253         if (i != tree_src.m_Projects.end()) {
254             if (i->second.m_DllHost.empty()) {
255                 dll.m_Depends.push_back(depend_id);
256             } else {
257                 dll.m_Depends.push_back(CProjKey(CProjKey::eDll, i->second.m_DllHost));
258             }
259         } else {
260             string host = GetDllHost(tree_src,depend_id.Id());
261             if (!host.empty()) {
262                 dll.m_Depends.push_back(CProjKey(CProjKey::eDll, host));
263             }
264         }
265     }
266     dll.m_Depends.sort();
267     dll.m_Depends.unique();
268 
269 
270     // m_Requires
271     copy(lib.m_Requires.begin(),
272          lib.m_Requires.end(), back_inserter(dll.m_Requires));
273     dll.m_Requires.sort();
274     dll.m_Requires.unique();
275 
276     // Libs 3-Party
277     copy(lib.m_Libs3Party.begin(),
278          lib.m_Libs3Party.end(), back_inserter(dll.m_Libs3Party));
279     dll.m_Libs3Party.sort();
280     dll.m_Libs3Party.unique();
281 
282     // m_IncludeDirs
283     copy(lib.m_IncludeDirs.begin(),
284          lib.m_IncludeDirs.end(), back_inserter(dll.m_IncludeDirs));
285     dll.m_IncludeDirs.sort();
286     dll.m_IncludeDirs.unique();
287 
288     // m_DatatoolSources
289     copy(lib.m_DatatoolSources.begin(),
290          lib.m_DatatoolSources.end(), back_inserter(dll.m_DatatoolSources));
291     dll.m_DatatoolSources.sort();
292     dll.m_DatatoolSources.unique();
293 
294     // m_Defines
295     copy(lib.m_Defines.begin(),
296          lib.m_Defines.end(), back_inserter(dll.m_Defines));
297     dll.m_Defines.sort();
298     dll.m_Defines.unique();
299 
300     // watchers
301     if (!lib.m_Watchers.empty()) {
302         if (!dll.m_Watchers.empty()) {
303             dll.m_Watchers += " ";
304         }
305         dll.m_Watchers += lib.m_Watchers;
306     }
307     {{
308         string makefile_name =
309             SMakeProjectT::CreateMakeAppLibFileName(lib.m_SourcesBaseDir,
310                                                     lib.m_Name);
311         CSimpleMakeFileContents makefile(makefile_name,eMakeType_Undefined);
312         CSimpleMakeFileContents::TContents::const_iterator p =
313             makefile.m_Contents.find("NCBI_C_LIBS");
314 
315         list<string> ncbi_clibs;
316         if (p != makefile.m_Contents.end()) {
317             SAppProjectT::CreateNcbiCToolkitLibs(makefile, &ncbi_clibs);
318 
319             dll.m_Libs3Party.push_back("NCBI_C_LIBS");
320             dll.m_Libs3Party.sort();
321             dll.m_Libs3Party.unique();
322 
323             copy(ncbi_clibs.begin(),
324                  ncbi_clibs.end(),
325                  back_inserter(dll.m_NcbiCLibs));
326             dll.m_NcbiCLibs.sort();
327             dll.m_NcbiCLibs.unique();
328 
329         }
330     }}
331 
332     // m_NcbiCLibs
333     copy(lib.m_NcbiCLibs.begin(),
334          lib.m_NcbiCLibs.end(), back_inserter(dll.m_NcbiCLibs));
335     dll.m_NcbiCLibs.sort();
336     dll.m_NcbiCLibs.unique();
337 
338     dll.m_MakeType = max(lib.m_MakeType, dll.m_MakeType);
339 }
340 
AnalyzeDllData(CProjectItemsTree & tree)341 void AnalyzeDllData(CProjectItemsTree& tree)
342 {
343     set<string> dll_to_erase;
344     NON_CONST_ITERATE(CProjectItemsTree::TProjects, p, tree.m_Projects) {
345         const CProjKey& key = p->first;
346         CProjItem& project = p->second;
347         if (key.Type() == CProjKey::eDll) {
348             ITERATE( list<string>, h,  project.m_HostedLibs) {
349                 CProjectItemsTree::TProjects::iterator i;
350                 i = tree.m_Projects.find(CProjKey(CProjKey::eLib, *h));
351                 if (i != tree.m_Projects.end()) {
352                     if (*h != key.Id()) {
353                         i->second.m_DllHost = key.Id();
354                         i = tree.m_Projects.find(CProjKey(CProjKey::eDll, *h));
355                         if (i != tree.m_Projects.end()) {
356                             dll_to_erase.insert(*h);
357                         }
358                     } else if (i->second.m_DllHost.empty()) {
359                         i->second.m_DllHost = key.Id();
360                     }
361                 }
362             }
363         }
364     }
365     ITERATE(set<string>, d, dll_to_erase) {
366         CProjectItemsTree::TProjects::iterator i;
367         i = tree.m_Projects.find(CProjKey(CProjKey::eDll, *d));
368         if (i != tree.m_Projects.end()) {
369             tree.m_Projects.erase(i);
370         }
371     }
372 }
373 
GetDllHost(const CProjectItemsTree & tree,const string & lib)374 string GetDllHost(const CProjectItemsTree& tree, const string& lib)
375 {
376     ITERATE(CProjectItemsTree::TProjects, p, tree.m_Projects) {
377         const CProjKey& key = p->first;
378         const CProjItem& project = p->second;
379         if (key.Type() == CProjKey::eDll) {
380             ITERATE( list<string>, h,  project.m_HostedLibs) {
381                 if (*h == lib) {
382                     return key.Id();
383                 }
384             }
385         }
386     }
387     return kEmptyStr;
388 }
389 
CreateDllBuildTree(const CProjectItemsTree & tree_src,CProjectItemsTree * tree_dst)390 void CreateDllBuildTree(const CProjectItemsTree& tree_src,
391                         CProjectItemsTree*       tree_dst)
392 {
393     tree_dst->m_RootSrc = tree_src.m_RootSrc;
394 
395     FilterOutDllHostedProjects(tree_src, tree_dst);
396 
397     list<string> dll_ids;
398     CreateDllsList(tree_src, &dll_ids);
399 
400     list<string> dll_depends_ids;
401     CollectDllsDepends(tree_src, dll_ids, &dll_depends_ids);
402     copy(dll_depends_ids.begin(),
403         dll_depends_ids.end(), back_inserter(dll_ids));
404     dll_ids.sort();
405     dll_ids.unique();
406 
407     ITERATE(list<string>, p, dll_ids) {
408 
409         const string& dll_id = *p;
410         CProjectItemsTree::TProjects::const_iterator d;
411         d = tree_src.m_Projects.find(CProjKey(CProjKey::eDll, dll_id));
412         if (d == tree_src.m_Projects.end()) {
413             d = GetApp().GetWholeTree().m_Projects.find(CProjKey(CProjKey::eDll, dll_id));
414             if (d == GetApp().GetWholeTree().m_Projects.end()) {
415                 PTB_ERROR_EX(kEmptyStr, ePTB_ProjectNotFound, "DLL project not found: " << dll_id);
416                 continue;
417             }
418         }
419         CProjItem dll( d->second);
420         s_InitalizeDllProj(dll_id, &dll, tree_src, tree_dst);
421 
422         CProjectItemsTree::TProjects::const_iterator k;
423         bool is_empty = true;
424         string str_log;
425         ITERATE(list<string>, n, dll.m_HostedLibs) {
426             const string& lib_id = *n;
427             k = tree_src.m_Projects.find(CProjKey(CProjKey::eLib,lib_id));
428             if (k == tree_src.m_Projects.end()) {
429                 k = GetApp().GetWholeTree().m_Projects.find(CProjKey(CProjKey::eLib, lib_id));
430                 if (k != GetApp().GetWholeTree().m_Projects.end()) {
431                     const CProjItem& lib = k->second;
432                     s_AddProjItemToDll(tree_src, lib, dll);
433                     is_empty = false;
434                 } else if (GetApp().GetSite().GetChoiceForLib(lib_id)
435                                                    == CMsvcSite::e3PartyLib ) {
436                     CMsvcSite::SLibChoice choice =
437                         GetApp().GetSite().GetLibChoiceForLib(lib_id);
438                     dll.m_Requires.push_back(choice.m_3PartyLib);
439                     dll.m_Requires.sort();
440                     dll.m_Requires.unique();
441                 } else {
442                     str_log += " " + lib_id;
443                 }
444                 continue;
445             }
446             const CProjItem& lib = k->second;
447             s_AddProjItemToDll(tree_src, lib, dll);
448             is_empty = false;
449         }
450         if ( !is_empty ) {
451             tree_dst->m_Projects[CProjKey(CProjKey::eDll, dll_id)] = dll;
452             if ( !str_log.empty() ) {
453                 string path = CDirEntry::ConcatPath(dll.m_SourcesBaseDir, dll_id);
454                 path += CMsvc7RegSettings::GetVcprojExt();
455                 PTB_WARNING_EX(path, ePTB_ConfigurationError,
456                                "Missing libraries not found: " << str_log);
457             }
458         } else {
459             string path = CDirEntry::ConcatPath(dll.m_SourcesBaseDir, dll_id);
460             path += CMsvc7RegSettings::GetVcprojExt();
461             PTB_WARNING_EX(path, ePTB_ProjectExcluded,
462                            "Skipped empty project: " << dll_id);
463         }
464     }
465     NON_CONST_ITERATE(CProjectItemsTree::TProjects, p, tree_dst->m_Projects) {
466 
467         list<CProjKey> new_depends;
468         CProjItem& project = p->second;
469         ITERATE(list<CProjKey>, n, project.m_Depends) {
470             const CProjKey& depend_id = *n;
471 
472             bool found = false;
473             for (int pass=0; !found && pass<2; ++pass) {
474                 const CProjectItemsTree& tree = pass ? tree_src : *tree_dst;
475                 CProjectItemsTree::TProjects::const_iterator i = tree.m_Projects.find(depend_id);
476                 if (i != tree.m_Projects.end()) {
477                     if (i->second.m_DllHost.empty()) {
478                         new_depends.push_back(depend_id);
479                     } else {
480                         new_depends.push_back(CProjKey(CProjKey::eDll, i->second.m_DllHost));
481                     }
482                     found = true;
483                     if (pass == 1 && GetApp().m_AddMissingLibs &&
484                         i->second.m_MakeType >= eMakeType_Excluded) {
485                         copy(i->second.m_Depends.begin(), i->second.m_Depends.end(),
486                             back_inserter(new_depends));
487                     }
488                 } else /* if (!GetApp().m_ScanWholeTree)*/ {
489                     ITERATE(CProjectItemsTree::TProjects, d, tree.m_Projects) {
490                         const list<string>& lst = d->second.m_HostedLibs;
491                         if ( find (lst.begin(), lst.end(), depend_id.Id()) != lst.end()) {
492                             new_depends.push_back(d->first);
493                             found = true;
494                             break;
495                         }
496                     }
497                 }
498             }
499             if (!found) {
500                 string path = CDirEntry::ConcatPath(project.m_SourcesBaseDir, project.m_ID);
501                 if (!SMakeProjectT::IsConfigurableDefine(depend_id.Id())) {
502                     if (GetApp().m_AddMissingLibs) {
503                         new_depends.push_back(depend_id);
504                     } else {
505                         PTB_WARNING_EX(path, ePTB_ProjectNotFound,
506                                     "Depends on missing project: " << depend_id.Id());
507                     }
508                 }
509 
510             }
511         }
512         new_depends.sort();
513         new_depends.unique();
514         project.m_Depends = new_depends;
515     }
516 }
517 
518 
CreateDllsList(const CProjectItemsTree & tree_src,list<string> * dll_ids)519 void CreateDllsList(const CProjectItemsTree& tree_src,
520                     list<string>*            dll_ids)
521 {
522     dll_ids->clear();
523 
524     set<string> dll_set;
525 
526     ITERATE(CProjectItemsTree::TProjects, p, tree_src.m_Projects) {
527         if ( !p->second.m_DllHost.empty() ) {
528             dll_set.insert(p->second.m_DllHost);
529         }
530     }
531     copy(dll_set.begin(), dll_set.end(), back_inserter(*dll_ids));
532 }
533 
534 
CollectDllsDepends(const CProjectItemsTree & tree_src,const list<string> & dll_ids,list<string> * dll_depends_ids)535 void CollectDllsDepends(const CProjectItemsTree& tree_src,
536                         const list<string>& dll_ids,
537                         list<string>*       dll_depends_ids)
538 {
539     size_t depends_cnt = dll_depends_ids->size();
540 
541     ITERATE(list<string>, p, dll_ids) {
542 
543         const string& dll_id = *p;
544         CProjectItemsTree::TProjects::const_iterator i;
545         i = tree_src.m_Projects.find( CProjKey(CProjKey::eDll,dll_id));
546         if (i != tree_src.m_Projects.end()) {
547             ITERATE(list<CProjKey>, n, i->second.m_Depends) {
548                 if ( tree_src.m_Projects.find( CProjKey(CProjKey::eDll,n->Id())) !=
549                      tree_src.m_Projects.end() &&
550                      find(dll_ids.begin(), dll_ids.end(), n->Id()) == dll_ids.end()) {
551                     dll_depends_ids->push_back(n->Id());
552                 }
553             }
554         }
555     }
556 
557     dll_depends_ids->sort();
558     dll_depends_ids->unique();
559     if ( !(dll_depends_ids->size() > depends_cnt) )
560         return;
561 
562     list<string> total_dll_ids(dll_ids);
563     copy(dll_depends_ids->begin(),
564          dll_depends_ids->end(), back_inserter(total_dll_ids));
565     total_dll_ids.sort();
566     total_dll_ids.unique();
567 
568     CollectDllsDepends(tree_src, total_dll_ids, dll_depends_ids);
569 }
570 
571 
572 END_NCBI_SCOPE
573