1 
2 // boostdep - a tool to generate Boost dependency reports
3 //
4 // Copyright 2014-2017 Peter Dimov
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt
9 
10 #define _CRT_SECURE_NO_WARNINGS
11 
12 #include <boost/filesystem.hpp>
13 #include <boost/filesystem/fstream.hpp>
14 #include <string>
15 #include <iostream>
16 #include <fstream>
17 #include <vector>
18 #include <map>
19 #include <set>
20 #include <algorithm>
21 #include <climits>
22 #include <cstdlib>
23 #include <streambuf>
24 #include <sstream>
25 
26 namespace fs = boost::filesystem;
27 
28 // header -> module
29 static std::map< std::string, std::string > s_header_map;
30 
31 // module -> headers
32 static std::map< std::string, std::set<std::string> > s_module_headers;
33 
34 static std::set< std::string > s_modules;
35 
scan_module_headers(fs::path const & path)36 static void scan_module_headers( fs::path const & path )
37 {
38     try
39     {
40         std::string module = path.generic_string().substr( 5 ); // strip "libs/"
41 
42         std::replace( module.begin(), module.end(), '/', '~' );
43 
44         s_modules.insert( module );
45 
46         fs::path dir = path / "include";
47         size_t n = dir.generic_string().size();
48 
49         fs::recursive_directory_iterator it( dir ), last;
50 
51         for( ; it != last; ++it )
52         {
53             if( it->status().type() == fs::directory_file )
54             {
55                 continue;
56             }
57 
58             std::string p2 = it->path().generic_string();
59             p2 = p2.substr( n+1 );
60 
61             s_header_map[ p2 ] = module;
62             s_module_headers[ module ].insert( p2 );
63         }
64     }
65     catch( fs::filesystem_error const & x )
66     {
67         std::cout << x.what() << std::endl;
68     }
69 }
70 
scan_submodules(fs::path const & path)71 static void scan_submodules( fs::path const & path )
72 {
73     fs::directory_iterator it( path ), last;
74 
75     for( ; it != last; ++it )
76     {
77         fs::directory_entry const & e = *it;
78 
79         if( e.status().type() != fs::directory_file )
80         {
81             continue;
82         }
83 
84         fs::path path = e.path();
85 
86         if( fs::exists( path / "include" ) )
87         {
88             scan_module_headers( path );
89         }
90 
91         if( fs::exists( path / "sublibs" ) )
92         {
93             scan_submodules( path );
94         }
95     }
96 }
97 
build_header_map()98 static void build_header_map()
99 {
100     scan_submodules( "libs" );
101 }
102 
scan_header_dependencies(std::string const & header,std::istream & is,std::map<std::string,std::set<std::string>> & deps,std::map<std::string,std::set<std::string>> & from)103 static void scan_header_dependencies( std::string const & header, std::istream & is, std::map< std::string, std::set< std::string > > & deps, std::map< std::string, std::set< std::string > > & from )
104 {
105     std::string line;
106 
107     while( std::getline( is, line ) )
108     {
109         while( !line.empty() && ( line[0] == ' ' || line[0] == '\t' ) )
110         {
111             line.erase( 0, 1 );
112         }
113 
114         if( line.empty() || line[0] != '#' ) continue;
115 
116         line.erase( 0, 1 );
117 
118         while( !line.empty() && ( line[0] == ' ' || line[0] == '\t' ) )
119         {
120             line.erase( 0, 1 );
121         }
122 
123         if( line.substr( 0, 7 ) != "include" ) continue;
124 
125         line.erase( 0, 7 );
126 
127         while( !line.empty() && ( line[0] == ' ' || line[0] == '\t' ) )
128         {
129             line.erase( 0, 1 );
130         }
131 
132         if( line.size() < 2 ) continue;
133 
134         char ch = line[0];
135 
136         if( ch != '<' && ch != '"' ) continue;
137 
138         if( ch == '<' )
139         {
140             ch = '>';
141         }
142 
143         line.erase( 0, 1 );
144 
145         std::string::size_type k = line.find_first_of( ch );
146 
147         if( k != std::string::npos )
148         {
149             line.erase( k );
150         }
151 
152         std::map< std::string, std::string >::const_iterator i = s_header_map.find( line );
153 
154         if( i != s_header_map.end() )
155         {
156             deps[ i->second ].insert( line );
157             from[ line ].insert( header );
158         }
159         else if( line.substr( 0, 6 ) == "boost/" )
160         {
161             deps[ "(unknown)" ].insert( line );
162             from[ line ].insert( header );
163         }
164     }
165 }
166 
167 struct module_primary_actions
168 {
169     virtual void heading( std::string const & module ) = 0;
170 
171     virtual void module_start( std::string const & module ) = 0;
172     virtual void module_end( std::string const & module ) = 0;
173 
174     virtual void header_start( std::string const & header ) = 0;
175     virtual void header_end( std::string const & header ) = 0;
176 
177     virtual void from_header( std::string const & header ) = 0;
178 };
179 
module_include_path(std::string module)180 static fs::path module_include_path( std::string module )
181 {
182     std::replace( module.begin(), module.end(), '~', '/' );
183     return fs::path( "libs" ) / module / "include";
184 }
185 
module_source_path(std::string module)186 static fs::path module_source_path( std::string module )
187 {
188     std::replace( module.begin(), module.end(), '~', '/' );
189     return fs::path( "libs" ) / module / "src";
190 }
191 
module_build_path(std::string module)192 static fs::path module_build_path( std::string module )
193 {
194     std::replace( module.begin(), module.end(), '~', '/' );
195     return fs::path( "libs" ) / module / "build";
196 }
197 
module_test_path(std::string module)198 static fs::path module_test_path( std::string module )
199 {
200     std::replace( module.begin(), module.end(), '~', '/' );
201     return fs::path( "libs" ) / module / "test";
202 }
203 
scan_module_path(fs::path const & dir,bool remove_prefix,std::map<std::string,std::set<std::string>> & deps,std::map<std::string,std::set<std::string>> & from)204 static void scan_module_path( fs::path const & dir, bool remove_prefix, std::map< std::string, std::set< std::string > > & deps, std::map< std::string, std::set< std::string > > & from )
205 {
206     size_t n = dir.generic_string().size();
207 
208     if( fs::exists( dir ) )
209     {
210         fs::recursive_directory_iterator it( dir ), last;
211 
212         for( ; it != last; ++it )
213         {
214             if( it->status().type() == fs::directory_file )
215             {
216                 continue;
217             }
218 
219             std::string header = it->path().generic_string();
220 
221             if( remove_prefix )
222             {
223                 header = header.substr( n+1 );
224             }
225 
226             fs::ifstream is( it->path() );
227 
228             scan_header_dependencies( header, is, deps, from );
229         }
230     }
231 }
232 
scan_module_dependencies(std::string const & module,module_primary_actions & actions,bool track_sources,bool track_tests,bool include_self)233 static void scan_module_dependencies( std::string const & module, module_primary_actions & actions, bool track_sources, bool track_tests, bool include_self )
234 {
235     // module -> [ header, header... ]
236     std::map< std::string, std::set< std::string > > deps;
237 
238     // header -> included from [ header, header... ]
239     std::map< std::string, std::set< std::string > > from;
240 
241     scan_module_path( module_include_path( module ), true, deps, from );
242 
243     if( track_sources )
244     {
245         scan_module_path( module_source_path( module ), false, deps, from );
246     }
247 
248     if( track_tests )
249     {
250         scan_module_path( module_test_path( module ), false, deps, from );
251     }
252 
253     actions.heading( module );
254 
255     for( std::map< std::string, std::set< std::string > >::iterator i = deps.begin(); i != deps.end(); ++i )
256     {
257         if( i->first == module && !include_self ) continue;
258 
259         actions.module_start( i->first );
260 
261         for( std::set< std::string >::iterator j = i->second.begin(); j != i->second.end(); ++j )
262         {
263             actions.header_start( *j );
264 
265             std::set< std::string > const & f = from[ *j ];
266 
267             for( std::set< std::string >::const_iterator k = f.begin(); k != f.end(); ++k )
268             {
269                 actions.from_header( *k );
270             }
271 
272             actions.header_end( *j );
273         }
274 
275         actions.module_end( i->first );
276     }
277 }
278 
279 // module depends on [ module, module... ]
280 static std::map< std::string, std::set< std::string > > s_module_deps;
281 
282 // header is included by [header, header...]
283 static std::map< std::string, std::set< std::string > > s_header_deps;
284 
285 // [ module, module... ] depend on module
286 static std::map< std::string, std::set< std::string > > s_reverse_deps;
287 
288 // header includes [header, header...]
289 static std::map< std::string, std::set< std::string > > s_header_includes;
290 
291 struct build_mdmap_actions: public module_primary_actions
292 {
293     std::string module_;
294     std::string module2_;
295     std::string header_;
296 
headingbuild_mdmap_actions297     void heading( std::string const & module )
298     {
299         module_ = module;
300     }
301 
module_startbuild_mdmap_actions302     void module_start( std::string const & module )
303     {
304         if( module != module_ )
305         {
306             s_module_deps[ module_ ].insert( module );
307             s_reverse_deps[ module ].insert( module_ );
308         }
309 
310         module2_ = module;
311     }
312 
module_endbuild_mdmap_actions313     void module_end( std::string const & /*module*/ )
314     {
315     }
316 
header_startbuild_mdmap_actions317     void header_start( std::string const & header )
318     {
319         header_ = header;
320     }
321 
header_endbuild_mdmap_actions322     void header_end( std::string const & /*header*/ )
323     {
324     }
325 
from_headerbuild_mdmap_actions326     void from_header( std::string const & header )
327     {
328         if( module_ != module2_ )
329         {
330             s_header_deps[ header_ ].insert( header );
331         }
332 
333         s_header_includes[ header ].insert( header_ );
334     }
335 };
336 
build_module_dependency_map(bool track_sources,bool track_tests)337 static void build_module_dependency_map( bool track_sources, bool track_tests )
338 {
339     for( std::set< std::string >::iterator i = s_modules.begin(); i != s_modules.end(); ++i )
340     {
341         build_mdmap_actions actions;
342         scan_module_dependencies( *i, actions, track_sources, track_tests, true );
343     }
344 }
345 
output_module_primary_report(std::string const & module,module_primary_actions & actions,bool track_sources,bool track_tests)346 static void output_module_primary_report( std::string const & module, module_primary_actions & actions, bool track_sources, bool track_tests )
347 {
348     try
349     {
350         scan_module_dependencies( module, actions, track_sources, track_tests, false );
351     }
352     catch( fs::filesystem_error const & x )
353     {
354         std::cout << x.what() << std::endl;
355     }
356 }
357 
358 struct module_secondary_actions
359 {
360     virtual void heading( std::string const & module ) = 0;
361 
362     virtual void module_start( std::string const & module ) = 0;
363     virtual void module_end( std::string const & module ) = 0;
364 
365     virtual void module_adds( std::string const & module ) = 0;
366 };
367 
exclude(std::set<std::string> & x,std::set<std::string> const & y)368 static void exclude( std::set< std::string > & x, std::set< std::string > const & y )
369 {
370     for( std::set< std::string >::const_iterator i = y.begin(); i != y.end(); ++i )
371     {
372         x.erase( *i );
373     }
374 }
375 
output_module_secondary_report(std::string const & module,std::set<std::string> deps,module_secondary_actions & actions)376 static void output_module_secondary_report( std::string const & module, std::set< std::string> deps, module_secondary_actions & actions )
377 {
378     actions.heading( module );
379 
380     deps.insert( module );
381 
382     // build transitive closure
383 
384     for( ;; )
385     {
386         std::set< std::string > deps2( deps );
387 
388         for( std::set< std::string >::iterator i = deps.begin(); i != deps.end(); ++i )
389         {
390             std::set< std::string > deps3 = s_module_deps[ *i ];
391 
392             exclude( deps3, deps );
393 
394             if( deps3.empty() )
395             {
396                 continue;
397             }
398 
399             actions.module_start( *i );
400 
401             for( std::set< std::string >::iterator j = deps3.begin(); j != deps3.end(); ++j )
402             {
403                 actions.module_adds( *j );
404             }
405 
406             actions.module_end( *i );
407 
408             deps2.insert( deps3.begin(), deps3.end() );
409         }
410 
411         if( deps == deps2 )
412         {
413             break;
414         }
415         else
416         {
417             deps = deps2;
418         }
419     }
420 }
421 
output_module_secondary_report(std::string const & module,module_secondary_actions & actions)422 static void output_module_secondary_report( std::string const & module, module_secondary_actions & actions )
423 {
424     output_module_secondary_report( module, s_module_deps[ module ], actions );
425 }
426 
427 struct header_inclusion_actions
428 {
429     virtual void heading( std::string const & header, std::string const & module ) = 0;
430 
431     virtual void module_start( std::string const & module ) = 0;
432     virtual void module_end( std::string const & module ) = 0;
433 
434     virtual void header( std::string const & header ) = 0;
435 };
436 
output_header_inclusion_report(std::string const & header,header_inclusion_actions & actions)437 static void output_header_inclusion_report( std::string const & header, header_inclusion_actions & actions )
438 {
439     std::string module = s_header_map[ header ];
440 
441     actions.heading( header, module );
442 
443     std::set< std::string > from = s_header_deps[ header ];
444 
445     // classify 'from' dependencies by module
446 
447     // module -> [header, header...]
448     std::map< std::string, std::set< std::string > > from2;
449 
450     for( std::set< std::string >::iterator i = from.begin(); i != from.end(); ++i )
451     {
452         from2[ s_header_map[ *i ] ].insert( *i );
453     }
454 
455     for( std::map< std::string, std::set< std::string > >::iterator i = from2.begin(); i != from2.end(); ++i )
456     {
457         actions.module_start( i->first );
458 
459         for( std::set< std::string >::iterator j = i->second.begin(); j != i->second.end(); ++j )
460         {
461             actions.header( *j );
462         }
463 
464         actions.module_end( i->first );
465     }
466 }
467 
468 // output_module_primary_report
469 
470 struct module_primary_txt_actions: public module_primary_actions
471 {
headingmodule_primary_txt_actions472     void heading( std::string const & module )
473     {
474         std::cout << "Primary dependencies for " << module << ":\n\n";
475     }
476 
module_startmodule_primary_txt_actions477     void module_start( std::string const & module )
478     {
479         std::cout << module << ":\n";
480     }
481 
module_endmodule_primary_txt_actions482     void module_end( std::string const & /*module*/ )
483     {
484         std::cout << "\n";
485     }
486 
header_startmodule_primary_txt_actions487     void header_start( std::string const & header )
488     {
489         std::cout << "    <" << header << ">\n";
490     }
491 
header_endmodule_primary_txt_actions492     void header_end( std::string const & /*header*/ )
493     {
494     }
495 
from_headermodule_primary_txt_actions496     void from_header( std::string const & header )
497     {
498         std::cout << "        from <" << header << ">\n";
499     }
500 };
501 
502 struct module_primary_html_actions: public module_primary_actions
503 {
headingmodule_primary_html_actions504     void heading( std::string const & module )
505     {
506         std::cout << "\n\n<h1 id=\"primary-dependencies\">Primary dependencies for <em>" << module << "</em></h1>\n";
507     }
508 
module_startmodule_primary_html_actions509     void module_start( std::string const & module )
510     {
511         std::cout << "  <h2 id=\"" << module << "\"><a href=\"" << module << ".html\"><em>" << module << "</em></a></h2>\n";
512     }
513 
module_endmodule_primary_html_actions514     void module_end( std::string const & /*module*/ )
515     {
516     }
517 
header_startmodule_primary_html_actions518     void header_start( std::string const & header )
519     {
520         std::cout << "    <h3><code>&lt;" << header << "&gt;</code></h3><ul>\n";
521     }
522 
header_endmodule_primary_html_actions523     void header_end( std::string const & /*header*/ )
524     {
525         std::cout << "    </ul>\n";
526     }
527 
from_headermodule_primary_html_actions528     void from_header( std::string const & header )
529     {
530         std::cout << "      <li>from <code>&lt;" << header << "&gt;</code></li>\n";
531     }
532 };
533 
output_module_primary_report(std::string const & module,bool html,bool track_sources,bool track_tests)534 static void output_module_primary_report( std::string const & module, bool html, bool track_sources, bool track_tests )
535 {
536     if( html )
537     {
538         module_primary_html_actions actions;
539         output_module_primary_report( module, actions, track_sources, track_tests );
540     }
541     else
542     {
543         module_primary_txt_actions actions;
544         output_module_primary_report( module, actions, track_sources, track_tests );
545     }
546 }
547 
548 // output_module_secondary_report
549 
550 struct module_secondary_txt_actions: public module_secondary_actions
551 {
headingmodule_secondary_txt_actions552     void heading( std::string const & module )
553     {
554         std::cout << "Secondary dependencies for " << module << ":\n\n";
555     }
556 
module_startmodule_secondary_txt_actions557     void module_start( std::string const & module )
558     {
559         std::cout << module << ":\n";
560     }
561 
module_endmodule_secondary_txt_actions562     void module_end( std::string const & /*module*/ )
563     {
564         std::cout << "\n";
565     }
566 
module_addsmodule_secondary_txt_actions567     void module_adds( std::string const & module )
568     {
569         std::cout << "    adds " << module << "\n";
570     }
571 };
572 
573 struct module_secondary_html_actions: public module_secondary_actions
574 {
575     std::string m2_;
576 
headingmodule_secondary_html_actions577     void heading( std::string const & module )
578     {
579         std::cout << "\n\n<h1 id=\"secondary-dependencies\">Secondary dependencies for <em>" << module << "</em></h1>\n";
580     }
581 
module_startmodule_secondary_html_actions582     void module_start( std::string const & module )
583     {
584         std::cout << "  <h2><a href=\"" << module << ".html\"><em>" << module << "</em></a></h2><ul>\n";
585         m2_ = module;
586     }
587 
module_endmodule_secondary_html_actions588     void module_end( std::string const & /*module*/ )
589     {
590         std::cout << "  </ul>\n";
591     }
592 
module_addsmodule_secondary_html_actions593     void module_adds( std::string const & module )
594     {
595         std::cout << "    <li><a href=\"" << m2_ << ".html#" << module << "\">adds <em>" << module << "</em></a></li>\n";
596     }
597 };
598 
output_module_secondary_report(std::string const & module,bool html)599 static void output_module_secondary_report( std::string const & module, bool html )
600 {
601     if( html )
602     {
603         module_secondary_html_actions actions;
604         output_module_secondary_report( module, actions );
605     }
606     else
607     {
608         module_secondary_txt_actions actions;
609         output_module_secondary_report( module, actions );
610     }
611 }
612 
613 // output_header_report
614 
615 struct header_inclusion_txt_actions: public header_inclusion_actions
616 {
headingheader_inclusion_txt_actions617     void heading( std::string const & header, std::string const & module )
618     {
619         std::cout << "Inclusion report for <" << header << "> (in module " << module << "):\n\n";
620     }
621 
module_startheader_inclusion_txt_actions622     void module_start( std::string const & module )
623     {
624         std::cout << "    from " << module << ":\n";
625     }
626 
module_endheader_inclusion_txt_actions627     void module_end( std::string const & /*module*/ )
628     {
629         std::cout << "\n";
630     }
631 
headerheader_inclusion_txt_actions632     void header( std::string const & header )
633     {
634         std::cout << "        <" << header << ">\n";
635     }
636 };
637 
638 struct header_inclusion_html_actions: public header_inclusion_actions
639 {
headingheader_inclusion_html_actions640     void heading( std::string const & header, std::string const & module )
641     {
642         std::cout << "<h1>Inclusion report for <code>&lt;" << header << "&gt;</code> (in module <em>" << module << "</em>)</h1>\n";
643     }
644 
module_startheader_inclusion_html_actions645     void module_start( std::string const & module )
646     {
647         std::cout << "  <h2>From <a href=\"" << module << ".html\"><em>" << module << "</em></a></h2><ul>\n";
648     }
649 
module_endheader_inclusion_html_actions650     void module_end( std::string const & /*module*/ )
651     {
652         std::cout << "  </ul>\n";
653     }
654 
headerheader_inclusion_html_actions655     void header( std::string const & header )
656     {
657         std::cout << "    <li><code>&lt;" << header << "&gt;</code></li>\n";
658     }
659 };
660 
output_header_report(std::string const & header,bool html)661 static void output_header_report( std::string const & header, bool html )
662 {
663     if( html )
664     {
665         header_inclusion_html_actions actions;
666         output_header_inclusion_report( header, actions );
667     }
668     else
669     {
670         header_inclusion_txt_actions actions;
671         output_header_inclusion_report( header, actions );
672     }
673 }
674 
675 // output_module_reverse_report
676 
677 struct module_reverse_actions
678 {
679     virtual void heading( std::string const & module ) = 0;
680 
681     virtual void module_start( std::string const & module ) = 0;
682     virtual void module_end( std::string const & module ) = 0;
683 
684     virtual void header_start( std::string const & header ) = 0;
685     virtual void header_end( std::string const & header ) = 0;
686 
687     virtual void from_header( std::string const & header ) = 0;
688 };
689 
output_module_reverse_report(std::string const & module,module_reverse_actions & actions)690 static void output_module_reverse_report( std::string const & module, module_reverse_actions & actions )
691 {
692     actions.heading( module );
693 
694     std::set< std::string > const from = s_reverse_deps[ module ];
695 
696     for( std::set< std::string >::const_iterator i = from.begin(); i != from.end(); ++i )
697     {
698         actions.module_start( *i );
699 
700         for( std::map< std::string, std::set< std::string > >::iterator j = s_header_deps.begin(); j != s_header_deps.end(); ++j )
701         {
702             if( s_header_map[ j->first ] == module )
703             {
704                 bool header_started = false;
705 
706                 for( std::set< std::string >::iterator k = j->second.begin(); k != j->second.end(); ++k )
707                 {
708                     if( s_header_map[ *k ] == *i )
709                     {
710                         if( !header_started )
711                         {
712                             actions.header_start( j->first );
713 
714                             header_started = true;
715                         }
716 
717                         actions.from_header( *k );
718                     }
719                 }
720 
721                 if( header_started )
722                 {
723                     actions.header_end( j->first );
724                 }
725             }
726         }
727 
728         actions.module_end( *i );
729     }
730 }
731 
732 struct module_reverse_txt_actions: public module_reverse_actions
733 {
headingmodule_reverse_txt_actions734     void heading( std::string const & module )
735     {
736         std::cout << "Reverse dependencies for " << module << ":\n\n";
737     }
738 
module_startmodule_reverse_txt_actions739     void module_start( std::string const & module )
740     {
741         std::cout << module << ":\n";
742     }
743 
module_endmodule_reverse_txt_actions744     void module_end( std::string const & /*module*/ )
745     {
746         std::cout << "\n";
747     }
748 
header_startmodule_reverse_txt_actions749     void header_start( std::string const & header )
750     {
751         std::cout << "    <" << header << ">\n";
752     }
753 
header_endmodule_reverse_txt_actions754     void header_end( std::string const & /*header*/ )
755     {
756     }
757 
from_headermodule_reverse_txt_actions758     void from_header( std::string const & header )
759     {
760         std::cout << "        from <" << header << ">\n";
761     }
762 };
763 
764 struct module_reverse_html_actions: public module_reverse_actions
765 {
headingmodule_reverse_html_actions766     void heading( std::string const & module )
767     {
768         std::cout << "\n\n<h1 id=\"reverse-dependencies\">Reverse dependencies for <em>" << module << "</em></h1>\n";
769     }
770 
module_startmodule_reverse_html_actions771     void module_start( std::string const & module )
772     {
773         std::cout << "  <h2 id=\"reverse-" << module << "\"><a href=\"" << module << ".html\"><em>" << module << "</em></a></h2>\n";
774     }
775 
module_endmodule_reverse_html_actions776     void module_end( std::string const & /*module*/ )
777     {
778     }
779 
header_startmodule_reverse_html_actions780     void header_start( std::string const & header )
781     {
782         std::cout << "    <h3><code>&lt;" << header << "&gt;</code></h3><ul>\n";
783     }
784 
header_endmodule_reverse_html_actions785     void header_end( std::string const & /*header*/ )
786     {
787         std::cout << "    </ul>\n";
788     }
789 
from_headermodule_reverse_html_actions790     void from_header( std::string const & header )
791     {
792         std::cout << "      <li>from <code>&lt;" << header << "&gt;</code></li>\n";
793     }
794 };
795 
output_module_reverse_report(std::string const & module,bool html)796 static void output_module_reverse_report( std::string const & module, bool html )
797 {
798     if( html )
799     {
800         module_reverse_html_actions actions;
801         output_module_reverse_report( module, actions );
802     }
803     else
804     {
805         module_reverse_txt_actions actions;
806         output_module_reverse_report( module, actions );
807     }
808 }
809 
810 // module_level_report
811 
812 int const unknown_level = INT_MAX / 2;
813 
814 struct module_level_actions
815 {
816     virtual void begin() = 0;
817     virtual void end() = 0;
818 
819     virtual void level_start( int level ) = 0;
820     virtual void level_end( int level ) = 0;
821 
822     virtual void module_start( std::string const & module ) = 0;
823     virtual void module_end( std::string const & module ) = 0;
824 
825     virtual void module2( std::string const & module, int level ) = 0;
826 };
827 
output_module_level_report(module_level_actions & actions)828 static void output_module_level_report( module_level_actions & actions )
829 {
830     // build module level map
831 
832     std::map< std::string, int > level_map;
833 
834     for( std::set< std::string >::iterator i = s_modules.begin(); i != s_modules.end(); ++i )
835     {
836         if( s_module_deps[ *i ].empty() )
837         {
838             level_map[ *i ] = 0;
839             // std::cerr << *i << ": " << 0 << std::endl;
840         }
841         else
842         {
843             level_map[ *i ] = unknown_level;
844         }
845     }
846 
847     // build transitive closure to see through cycles
848 
849     std::map< std::string, std::set< std::string > > deps2 = s_module_deps;
850 
851     {
852         bool done;
853 
854         do
855         {
856             done = true;
857 
858             for( std::map< std::string, std::set< std::string > >::iterator i = deps2.begin(); i != deps2.end(); ++i )
859             {
860                 std::set< std::string > tmp = i->second;
861 
862                 for( std::set< std::string >::iterator j = i->second.begin(); j != i->second.end(); ++j )
863                 {
864                     std::set< std::string > tmp2 = deps2[ *j ];
865                     tmp.insert( tmp2.begin(), tmp2.end() );
866                 }
867 
868                 if( tmp.size() != i->second.size() )
869                 {
870                     i->second = tmp;
871                     done = false;
872                 }
873             }
874         }
875         while( !done );
876     }
877 
878     // compute acyclic levels
879 
880     for( int k = 1, n = s_modules.size(); k < n; ++k )
881     {
882         for( std::map< std::string, std::set< std::string > >::iterator i = s_module_deps.begin(); i != s_module_deps.end(); ++i )
883         {
884             // i->first depends on i->second
885 
886             if( level_map[ i->first ] >= unknown_level )
887             {
888                 int level = 0;
889 
890                 for( std::set< std::string >::iterator j = i->second.begin(); j != i->second.end(); ++j )
891                 {
892                     level = std::max( level, level_map[ *j ] + 1 );
893                 }
894 
895                 if( level == k )
896                 {
897                     level_map[ i->first ] = level;
898                     // std::cerr << i->first << ": " << level << std::endl;
899                 }
900             }
901         }
902     }
903 
904     // min_level_map[ M ] == L means the level is unknown, but at least L
905     std::map< std::string, int > min_level_map;
906 
907     // initialize min_level_map for acyclic dependencies
908 
909     for( std::map< std::string, int >::iterator i = level_map.begin(); i != level_map.end(); ++i )
910     {
911         if( i->second < unknown_level )
912         {
913             min_level_map[ i->first ] = i->second;
914         }
915     }
916 
917     // compute levels for cyclic modules
918 
919     for( int k = 1, n = s_modules.size(); k < n; ++k )
920     {
921         for( std::map< std::string, std::set< std::string > >::iterator i = s_module_deps.begin(); i != s_module_deps.end(); ++i )
922         {
923             if( level_map[ i->first ] >= unknown_level )
924             {
925                 int level = 0;
926 
927                 for( std::set< std::string >::iterator j = i->second.begin(); j != i->second.end(); ++j )
928                 {
929                     int jl = level_map[ *j ];
930 
931                     if( jl < unknown_level )
932                     {
933                         level = std::max( level, jl + 1 );
934                     }
935                     else
936                     {
937                         int ml = min_level_map[ *j ];
938 
939                         if( deps2[ *j ].count( i->first ) == 0 )
940                         {
941                             // *j does not depend on i->first, so
942                             // the level of i->first is at least
943                             // 1 + the minimum level of *j
944 
945                             ++ml;
946                         }
947 
948                         level = std::max( level, ml );
949                     }
950                 }
951 
952                 min_level_map[ i->first ] = level;
953             }
954         }
955     }
956 
957     // reverse level map
958 
959     std::map< int, std::set< std::string > > reverse_level_map;
960 
961     for( std::map< std::string, int >::iterator i = level_map.begin(); i != level_map.end(); ++i )
962     {
963         int level = i->second;
964 
965         if( level >= unknown_level )
966         {
967             int min_level = min_level_map[ i->first ];
968 
969             if( min_level != 0 )
970             {
971                 level = min_level;
972             }
973         }
974 
975         reverse_level_map[ level ].insert( i->first );
976     }
977 
978     // output report
979 
980     actions.begin();
981 
982     for( std::map< int, std::set< std::string > >::iterator i = reverse_level_map.begin(); i != reverse_level_map.end(); ++i )
983     {
984         actions.level_start( i->first );
985 
986         for( std::set< std::string >::iterator j = i->second.begin(); j != i->second.end(); ++j )
987         {
988             actions.module_start( *j );
989 
990             std::set< std::string > mdeps = s_module_deps[ *j ];
991 
992             for( std::set< std::string >::iterator k = mdeps.begin(); k != mdeps.end(); ++k )
993             {
994                 int level = level_map[ *k ];
995 
996                 if( level >= unknown_level )
997                 {
998                     int min_level = min_level_map[ *k ];
999 
1000                     if( min_level != 0 )
1001                     {
1002                         level = min_level;
1003                     }
1004                 }
1005 
1006                 actions.module2( *k, level );
1007             }
1008 
1009             actions.module_end( *j );
1010         }
1011 
1012         actions.level_end( i->first );
1013     }
1014 
1015     actions.end();
1016 }
1017 
1018 struct module_level_txt_actions: public module_level_actions
1019 {
1020     int level_;
1021 
beginmodule_level_txt_actions1022     void begin()
1023     {
1024         std::cout << "Module Levels:\n\n";
1025     }
1026 
endmodule_level_txt_actions1027     void end()
1028     {
1029     }
1030 
level_startmodule_level_txt_actions1031     void level_start( int level )
1032     {
1033         if( level >= unknown_level )
1034         {
1035             std::cout << "Level (undetermined):\n";
1036         }
1037         else
1038         {
1039             std::cout << "Level " << level << ":\n";
1040         }
1041 
1042         level_ = level;
1043     }
1044 
level_endmodule_level_txt_actions1045     void level_end( int /*level*/ )
1046     {
1047         std::cout << "\n";
1048     }
1049 
module_startmodule_level_txt_actions1050     void module_start( std::string const & module )
1051     {
1052         std::cout << "    " << module;
1053 
1054         if( level_ > 0 )
1055         {
1056             std::cout << " ->";
1057         }
1058     }
1059 
module_endmodule_level_txt_actions1060     void module_end( std::string const & /*module*/ )
1061     {
1062         std::cout << "\n";
1063     }
1064 
module2module_level_txt_actions1065     void module2( std::string const & module, int level )
1066     {
1067         std::cout << " " << module << "(";
1068 
1069         if( level >= unknown_level )
1070         {
1071             std::cout << "-";
1072         }
1073         else
1074         {
1075             std::cout << level;
1076         }
1077 
1078         std::cout << ")";
1079     }
1080 };
1081 
1082 struct module_level_html_actions: public module_level_actions
1083 {
1084     int level_;
1085 
beginmodule_level_html_actions1086     void begin()
1087     {
1088         std::cout << "<div id='module-levels'><h1>Module Levels</h1>\n";
1089     }
1090 
endmodule_level_html_actions1091     void end()
1092     {
1093         std::cout << "</div>\n";
1094     }
1095 
level_startmodule_level_html_actions1096     void level_start( int level )
1097     {
1098         if( level >= unknown_level )
1099         {
1100             std::cout << "  <h2>Level <em>undetermined</em></h2>\n";
1101         }
1102         else
1103         {
1104             std::cout << "  <h2 id='level:" << level << "'>Level " << level << "</h2>\n";
1105         }
1106 
1107         level_ = level;
1108     }
1109 
level_endmodule_level_html_actions1110     void level_end( int /*level*/ )
1111     {
1112     }
1113 
module_startmodule_level_html_actions1114     void module_start( std::string const & module )
1115     {
1116         std::cout << "    <h3 id='" << module << "'><a href=\"" << module << ".html\">" << module << "</a></h3><p class='primary-list'>";
1117     }
1118 
module_endmodule_level_html_actions1119     void module_end( std::string const & /*module*/ )
1120     {
1121         std::cout << "</p>\n";
1122     }
1123 
module2module_level_html_actions1124     void module2( std::string const & module, int level )
1125     {
1126         std::cout << " ";
1127 
1128         bool important = level < unknown_level && level > 1 && level >= level_ - 1;
1129 
1130         if( important )
1131         {
1132             std::cout << "<strong>";
1133         }
1134 
1135         std::cout << module;
1136 
1137         if( level < unknown_level )
1138         {
1139             std::cout << "<sup>" << level << "</sup>";
1140         }
1141 
1142         if( important )
1143         {
1144             std::cout << "</strong>";
1145         }
1146     }
1147 };
1148 
output_module_level_report(bool html)1149 static void output_module_level_report( bool html )
1150 {
1151     if( html )
1152     {
1153         module_level_html_actions actions;
1154         output_module_level_report( actions );
1155     }
1156     else
1157     {
1158         module_level_txt_actions actions;
1159         output_module_level_report( actions );
1160     }
1161 }
1162 
1163 // module_overview_report
1164 
1165 struct module_overview_actions
1166 {
1167     virtual void begin() = 0;
1168     virtual void end() = 0;
1169 
1170     virtual void module_start( std::string const & module ) = 0;
1171     virtual void module_end( std::string const & module ) = 0;
1172 
1173     virtual void module2( std::string const & module ) = 0;
1174 };
1175 
output_module_overview_report(module_overview_actions & actions)1176 static void output_module_overview_report( module_overview_actions & actions )
1177 {
1178     actions.begin();
1179 
1180     for( std::set< std::string >::iterator i = s_modules.begin(); i != s_modules.end(); ++i )
1181     {
1182         actions.module_start( *i );
1183 
1184         std::set< std::string > const mdeps = s_module_deps[ *i ];
1185 
1186         for( std::set< std::string >::const_iterator j = mdeps.begin(); j != mdeps.end(); ++j )
1187         {
1188             actions.module2( *j );
1189         }
1190 
1191         actions.module_end( *i );
1192     }
1193 
1194     actions.end();
1195 }
1196 
1197 struct module_overview_txt_actions: public module_overview_actions
1198 {
1199     bool deps_;
1200 
beginmodule_overview_txt_actions1201     void begin()
1202     {
1203         std::cout << "Module Overview:\n\n";
1204     }
1205 
endmodule_overview_txt_actions1206     void end()
1207     {
1208     }
1209 
module_startmodule_overview_txt_actions1210     void module_start( std::string const & module )
1211     {
1212         std::cout << module;
1213         deps_ = false;
1214     }
1215 
module_endmodule_overview_txt_actions1216     void module_end( std::string const & /*module*/ )
1217     {
1218         std::cout << "\n";
1219     }
1220 
module2module_overview_txt_actions1221     void module2( std::string const & module )
1222     {
1223         if( !deps_ )
1224         {
1225             std::cout << " ->";
1226             deps_ = true;
1227         }
1228 
1229         std::cout << " " << module;
1230     }
1231 };
1232 
1233 struct module_overview_html_actions: public module_overview_actions
1234 {
beginmodule_overview_html_actions1235     void begin()
1236     {
1237         std::cout << "<div id='module-overview'><h1>Module Overview</h1>\n";
1238     }
1239 
endmodule_overview_html_actions1240     void end()
1241     {
1242         std::cout << "</div>\n";
1243     }
1244 
module_startmodule_overview_html_actions1245     void module_start( std::string const & module )
1246     {
1247         std::cout << "  <h2 id='" << module << "'><a href=\"" << module << ".html\"><em>" << module << "</em></a></h2><p class='primary-list'>";
1248     }
1249 
module_endmodule_overview_html_actions1250     void module_end( std::string const & /*module*/ )
1251     {
1252         std::cout << "</p>\n";
1253     }
1254 
module2module_overview_html_actions1255     void module2( std::string const & module )
1256     {
1257         std::cout << " " << module;
1258     }
1259 };
1260 
output_module_overview_report(bool html)1261 static void output_module_overview_report( bool html )
1262 {
1263     if( html )
1264     {
1265         module_overview_html_actions actions;
1266         output_module_overview_report( actions );
1267     }
1268     else
1269     {
1270         module_overview_txt_actions actions;
1271         output_module_overview_report( actions );
1272     }
1273 }
1274 
1275 // list_dependencies
1276 
1277 struct list_dependencies_actions: public module_overview_actions
1278 {
beginlist_dependencies_actions1279     void begin()
1280     {
1281     }
1282 
endlist_dependencies_actions1283     void end()
1284     {
1285     }
1286 
module_startlist_dependencies_actions1287     void module_start( std::string const & module )
1288     {
1289         std::cout << module << " ->";
1290     }
1291 
module_endlist_dependencies_actions1292     void module_end( std::string const & /*module*/ )
1293     {
1294         std::cout << "\n";
1295     }
1296 
module2list_dependencies_actions1297     void module2( std::string const & module )
1298     {
1299         if( module != "(unknown)" )
1300         {
1301             std::cout << " " << module;
1302         }
1303     }
1304 };
1305 
list_dependencies()1306 static void list_dependencies()
1307 {
1308     list_dependencies_actions actions;
1309     output_module_overview_report( actions );
1310 }
1311 
1312 //
1313 
output_html_header(std::string const & title,std::string const & stylesheet,std::string const & prefix)1314 static void output_html_header( std::string const & title, std::string const & stylesheet, std::string const & prefix )
1315 {
1316     std::cout << "<html>\n";
1317     std::cout << "<head>\n";
1318     std::cout << "<title>" << title << "</title>\n";
1319 
1320     if( !stylesheet.empty() )
1321     {
1322         std::cout << "<link rel=\"stylesheet\" type=\"text/css\" href=\"" << stylesheet << "\" />\n";
1323     }
1324 
1325     std::cout << "</head>\n";
1326     std::cout << "<body>\n";
1327 
1328     if( !prefix.empty() )
1329     {
1330         std::cout << prefix << std::endl;
1331     }
1332 }
1333 
output_html_footer(std::string const & footer)1334 static void output_html_footer( std::string const & footer )
1335 {
1336     std::cout << "<hr />\n";
1337     std::cout << "<p class=\"footer\">" << footer << "</p>\n";
1338     std::cout << "</body>\n";
1339     std::cout << "</html>\n";
1340 }
1341 
enable_secondary(bool & secondary,bool track_sources,bool track_tests)1342 static void enable_secondary( bool & secondary, bool track_sources, bool track_tests )
1343 {
1344     if( !secondary )
1345     {
1346         try
1347         {
1348             build_module_dependency_map( track_sources, track_tests );
1349         }
1350         catch( fs::filesystem_error const & x )
1351         {
1352             std::cout << x.what() << std::endl;
1353         }
1354 
1355         secondary = true;
1356     }
1357 }
1358 
list_modules()1359 static void list_modules()
1360 {
1361     for( std::set< std::string >::iterator i = s_modules.begin(); i != s_modules.end(); ++i )
1362     {
1363         std::cout << *i << "\n";
1364     }
1365 }
1366 
list_buildable()1367 static void list_buildable()
1368 {
1369     for( std::set< std::string >::iterator i = s_modules.begin(); i != s_modules.end(); ++i )
1370     {
1371         if( fs::exists( module_build_path( *i ) ) && fs::exists( module_source_path( *i ) ) )
1372         {
1373             std::cout << *i << "\n";
1374         }
1375     }
1376 }
1377 
1378 // module_weight_report
1379 
1380 struct module_weight_actions
1381 {
1382     virtual void begin() = 0;
1383     virtual void end() = 0;
1384 
1385     virtual void weight_start( int weight ) = 0;
1386     virtual void weight_end( int weight ) = 0;
1387 
1388     virtual void module_start( std::string const & module ) = 0;
1389     virtual void module_end( std::string const & module ) = 0;
1390 
1391     virtual void module_primary_start() = 0;
1392     virtual void module_primary( std::string const & module, int weight ) = 0;
1393     virtual void module_primary_end() = 0;
1394 
1395     virtual void module_secondary_start() = 0;
1396     virtual void module_secondary( std::string const & module, int weight ) = 0;
1397     virtual void module_secondary_end() = 0;
1398 };
1399 
output_module_weight_report(module_weight_actions & actions)1400 static void output_module_weight_report( module_weight_actions & actions )
1401 {
1402     // gather secondary dependencies
1403 
1404     struct build_secondary_deps: public module_secondary_actions
1405     {
1406         std::map< std::string, std::set< std::string > > * pm_;
1407 
1408         build_secondary_deps( std::map< std::string, std::set< std::string > > * pm ): pm_( pm )
1409         {
1410         }
1411 
1412         std::string module_;
1413 
1414         void heading( std::string const & module )
1415         {
1416             module_ = module;
1417         }
1418 
1419         void module_start( std::string const & /*module*/ )
1420         {
1421         }
1422 
1423         void module_end( std::string const & /*module*/ )
1424         {
1425         }
1426 
1427         void module_adds( std::string const & module )
1428         {
1429             (*pm_)[ module_ ].insert( module );
1430         }
1431     };
1432 
1433     std::map< std::string, std::set< std::string > > secondary_deps;
1434 
1435     build_secondary_deps bsd( &secondary_deps );
1436 
1437     for( std::set< std::string >::const_iterator i = s_modules.begin(); i != s_modules.end(); ++i )
1438     {
1439         output_module_secondary_report( *i, bsd );
1440     }
1441 
1442     // build weight map
1443 
1444     std::map< int, std::set< std::string > > modules_by_weight;
1445 
1446     for( std::set< std::string >::const_iterator i = s_modules.begin(); i != s_modules.end(); ++i )
1447     {
1448         int w = s_module_deps[ *i ].size() + secondary_deps[ *i ].size();
1449         modules_by_weight[ w ].insert( *i );
1450     }
1451 
1452     // output report
1453 
1454     actions.begin();
1455 
1456     for( std::map< int, std::set< std::string > >::const_iterator i = modules_by_weight.begin(); i != modules_by_weight.end(); ++i )
1457     {
1458         actions.weight_start( i->first );
1459 
1460         for( std::set< std::string >::const_iterator j = i->second.begin(); j != i->second.end(); ++j )
1461         {
1462             actions.module_start( *j );
1463 
1464             if( !s_module_deps[ *j ].empty() )
1465             {
1466                 actions.module_primary_start();
1467 
1468                 for( std::set< std::string >::const_iterator k = s_module_deps[ *j ].begin(); k != s_module_deps[ *j ].end(); ++k )
1469                 {
1470                     int w = s_module_deps[ *k ].size() + secondary_deps[ *k ].size();
1471                     actions.module_primary( *k, w );
1472                 }
1473 
1474                 actions.module_primary_end();
1475             }
1476 
1477             if( !secondary_deps[ *j ].empty() )
1478             {
1479                 actions.module_secondary_start();
1480 
1481                 for( std::set< std::string >::const_iterator k = secondary_deps[ *j ].begin(); k != secondary_deps[ *j ].end(); ++k )
1482                 {
1483                     int w = s_module_deps[ *k ].size() + secondary_deps[ *k ].size();
1484                     actions.module_secondary( *k, w );
1485                 }
1486 
1487                 actions.module_secondary_end();
1488             }
1489 
1490             actions.module_end( *j );
1491         }
1492 
1493         actions.weight_end( i->first );
1494     }
1495 
1496     actions.end();
1497 }
1498 
1499 struct module_weight_txt_actions: public module_weight_actions
1500 {
beginmodule_weight_txt_actions1501     void begin()
1502     {
1503         std::cout << "Module Weights:\n\n";
1504     }
1505 
endmodule_weight_txt_actions1506     void end()
1507     {
1508     }
1509 
weight_startmodule_weight_txt_actions1510     void weight_start( int weight )
1511     {
1512         std::cout << "Weight " << weight << ":\n";
1513     }
1514 
weight_endmodule_weight_txt_actions1515     void weight_end( int /*weight*/ )
1516     {
1517         std::cout << "\n";
1518     }
1519 
module_startmodule_weight_txt_actions1520     void module_start( std::string const & module )
1521     {
1522         std::cout << "    " << module;
1523     }
1524 
module_endmodule_weight_txt_actions1525     void module_end( std::string const & /*module*/ )
1526     {
1527         std::cout << "\n";
1528     }
1529 
module_primary_startmodule_weight_txt_actions1530     void module_primary_start()
1531     {
1532         std::cout << " ->";
1533     }
1534 
module_primarymodule_weight_txt_actions1535     void module_primary( std::string const & module, int weight )
1536     {
1537         std::cout << " " << module << "(" << weight << ")";
1538     }
1539 
module_primary_endmodule_weight_txt_actions1540     void module_primary_end()
1541     {
1542     }
1543 
module_secondary_startmodule_weight_txt_actions1544     void module_secondary_start()
1545     {
1546         std::cout << " ->";
1547     }
1548 
module_secondarymodule_weight_txt_actions1549     void module_secondary( std::string const & module, int /*weight*/ )
1550     {
1551         std::cout << " " << module;
1552     }
1553 
module_secondary_endmodule_weight_txt_actions1554     void module_secondary_end()
1555     {
1556     }
1557 };
1558 
1559 struct module_weight_html_actions: public module_weight_actions
1560 {
1561     int weight_;
1562 
beginmodule_weight_html_actions1563     void begin()
1564     {
1565         std::cout << "<div id='module-weights'>\n<h1>Module Weights</h1>\n";
1566     }
1567 
endmodule_weight_html_actions1568     void end()
1569     {
1570         std::cout << "</div>\n";
1571     }
1572 
weight_startmodule_weight_html_actions1573     void weight_start( int weight )
1574     {
1575         std::cout << "  <h2 id='weight:" << weight << "'>Weight " << weight << "</h2>\n";
1576         weight_ = weight;
1577     }
1578 
weight_endmodule_weight_html_actions1579     void weight_end( int /*weight*/ )
1580     {
1581     }
1582 
module_startmodule_weight_html_actions1583     void module_start( std::string const & module )
1584     {
1585         std::cout << "    <h3 id='" << module << "'><a href=\"" << module << ".html\">" << module << "</a></h3>";
1586     }
1587 
module_endmodule_weight_html_actions1588     void module_end( std::string const & /*module*/ )
1589     {
1590         std::cout << "\n";
1591     }
1592 
module_primary_startmodule_weight_html_actions1593     void module_primary_start()
1594     {
1595         std::cout << "<p class='primary-list'>";
1596     }
1597 
module_primarymodule_weight_html_actions1598     void module_primary( std::string const & module, int weight )
1599     {
1600         std::cout << " ";
1601 
1602         bool heavy = weight >= 0.8 * weight_;
1603 
1604         if( heavy )
1605         {
1606             std::cout << "<strong>";
1607         }
1608 
1609         std::cout << module << "<sup>" << weight << "</sup>";
1610 
1611         if( heavy )
1612         {
1613             std::cout << "</strong>";
1614         }
1615     }
1616 
module_primary_endmodule_weight_html_actions1617     void module_primary_end()
1618     {
1619         std::cout << "</p>";
1620     }
1621 
module_secondary_startmodule_weight_html_actions1622     void module_secondary_start()
1623     {
1624         std::cout << "<p class='secondary-list'>";
1625     }
1626 
module_secondarymodule_weight_html_actions1627     void module_secondary( std::string const & module, int /*weight*/ )
1628     {
1629         std::cout << " " << module;
1630     }
1631 
module_secondary_endmodule_weight_html_actions1632     void module_secondary_end()
1633     {
1634         std::cout << "</p>";
1635     }
1636 };
1637 
output_module_weight_report(bool html)1638 static void output_module_weight_report( bool html )
1639 {
1640     if( html )
1641     {
1642         module_weight_html_actions actions;
1643         output_module_weight_report( actions );
1644     }
1645     else
1646     {
1647         module_weight_txt_actions actions;
1648         output_module_weight_report( actions );
1649     }
1650 }
1651 
1652 // output_module_subset_report
1653 
1654 struct module_subset_actions
1655 {
1656     virtual void heading( std::string const & module ) = 0;
1657 
1658     virtual void module_start( std::string const & module ) = 0;
1659     virtual void module_end( std::string const & module ) = 0;
1660 
1661     virtual void from_path( std::vector<std::string> const & path ) = 0;
1662 };
1663 
add_module_headers(fs::path const & dir,std::set<std::string> & headers)1664 static void add_module_headers( fs::path const & dir, std::set<std::string> & headers )
1665 {
1666     if( fs::exists( dir ) )
1667     {
1668         fs::recursive_directory_iterator it( dir ), last;
1669 
1670         for( ; it != last; ++it )
1671         {
1672             if( it->status().type() == fs::directory_file )
1673             {
1674                 continue;
1675             }
1676 
1677             headers.insert( it->path().generic_string() );
1678         }
1679     }
1680 }
1681 
output_module_subset_report_(std::string const & module,std::set<std::string> const & headers,module_subset_actions & actions)1682 static void output_module_subset_report_( std::string const & module, std::set<std::string> const & headers, module_subset_actions & actions )
1683 {
1684     // build header closure
1685 
1686     // header -> (header)*
1687     std::map< std::string, std::set<std::string> > inc2;
1688 
1689     // (header, header) -> path
1690     std::map< std::pair<std::string, std::string>, std::vector<std::string> > paths;
1691 
1692     for( std::set<std::string>::const_iterator i = headers.begin(); i != headers.end(); ++i )
1693     {
1694         std::set<std::string> & s = inc2[ *i ];
1695 
1696         s = s_header_includes[ *i ];
1697 
1698         for( std::set<std::string>::const_iterator j = s.begin(); j != s.end(); ++j )
1699         {
1700             std::vector<std::string> & v = paths[ std::make_pair( *i, *j ) ];
1701 
1702             v.resize( 0 );
1703             v.push_back( *i );
1704             v.push_back( *j );
1705         }
1706     }
1707 
1708     for( ;; )
1709     {
1710         bool r = false;
1711 
1712         for( std::map< std::string, std::set<std::string> >::iterator i = inc2.begin(); i != inc2.end(); ++i )
1713         {
1714             std::set<std::string> & s2 = i->second;
1715 
1716             for( std::set<std::string>::const_iterator j = s2.begin(); j != s2.end(); ++j )
1717             {
1718                 std::set<std::string> const & s = s_header_includes[ *j ];
1719 
1720                 for( std::set<std::string>::const_iterator k = s.begin(); k != s.end(); ++k )
1721                 {
1722                     if( s2.count( *k ) == 0 )
1723                     {
1724                         s2.insert( *k );
1725 
1726                         std::vector<std::string> const & v1 = paths[ std::make_pair( i->first, *j ) ];
1727                         std::vector<std::string> & v2 = paths[ std::make_pair( i->first, *k ) ];
1728 
1729                         v2 = v1;
1730                         v2.push_back( *k );
1731 
1732                         r = true;
1733                     }
1734                 }
1735             }
1736         }
1737 
1738         if( !r ) break;
1739     }
1740 
1741     // module -> header -> path [header -> header -> header]
1742     std::map< std::string, std::map< std::string, std::vector<std::string> > > subset;
1743 
1744     for( std::set<std::string>::const_iterator i = headers.begin(); i != headers.end(); ++i )
1745     {
1746         std::set<std::string> const & s = inc2[ *i ];
1747 
1748         for( std::set<std::string>::const_iterator j = s.begin(); j != s.end(); ++j )
1749         {
1750             std::string const & m = s_header_map[ *j ];
1751 
1752             if( m.empty() ) continue;
1753 
1754             std::vector<std::string> const & path = paths[ std::make_pair( *i, *j ) ];
1755 
1756             if( subset.count( m ) == 0 || subset[ m ].count( *i ) == 0 || subset[ m ][ *i ].size() > path.size() )
1757             {
1758                 subset[ m ][ *i ] = path;
1759             }
1760         }
1761     }
1762 
1763     actions.heading( module );
1764 
1765     for( std::map< std::string, std::map< std::string, std::vector<std::string> > >::const_iterator i = subset.begin(); i != subset.end(); ++i )
1766     {
1767         if( i->first == module ) continue;
1768 
1769         actions.module_start( i->first );
1770 
1771         int k = 0;
1772 
1773         for( std::map< std::string, std::vector<std::string> >::const_iterator j = i->second.begin(); j != i->second.end() && k < 4; ++j, ++k )
1774         {
1775             actions.from_path( j->second );
1776         }
1777 
1778         actions.module_end( i->first );
1779     }
1780 }
1781 
output_module_subset_report(std::string const & module,bool track_sources,bool track_tests,module_subset_actions & actions)1782 static void output_module_subset_report( std::string const & module, bool track_sources, bool track_tests, module_subset_actions & actions )
1783 {
1784     std::set<std::string> headers = s_module_headers[ module ];
1785 
1786     if( track_sources )
1787     {
1788         add_module_headers( module_source_path( module ), headers );
1789     }
1790 
1791     if( track_tests )
1792     {
1793         add_module_headers( module_test_path( module ), headers );
1794     }
1795 
1796     output_module_subset_report_( module, headers, actions );
1797 }
1798 
1799 struct module_subset_txt_actions: public module_subset_actions
1800 {
headingmodule_subset_txt_actions1801     void heading( std::string const & module )
1802     {
1803         std::cout << "Subset dependencies for " << module << ":\n\n";
1804     }
1805 
module_startmodule_subset_txt_actions1806     void module_start( std::string const & module )
1807     {
1808         std::cout << module << ":\n";
1809     }
1810 
module_endmodule_subset_txt_actions1811     void module_end( std::string const & /*module*/ )
1812     {
1813         std::cout << "\n";
1814     }
1815 
from_pathmodule_subset_txt_actions1816     void from_path( std::vector<std::string> const & path )
1817     {
1818         for( std::vector<std::string>::const_iterator i = path.begin(); i != path.end(); ++i )
1819         {
1820             if( i == path.begin() )
1821             {
1822                 std::cout << "  ";
1823             }
1824             else
1825             {
1826                 std::cout << " -> ";
1827             }
1828 
1829             std::cout << *i;
1830         }
1831 
1832         std::cout << "\n";
1833     }
1834 };
1835 
1836 struct module_subset_html_actions: public module_subset_actions
1837 {
headingmodule_subset_html_actions1838     void heading( std::string const & module )
1839     {
1840         std::cout << "\n\n<h1 id=\"subset-dependencies\">Subset dependencies for <em>" << module << "</em></h1>\n";
1841     }
1842 
module_startmodule_subset_html_actions1843     void module_start( std::string const & module )
1844     {
1845         std::cout << "  <h2 id=\"subset-" << module << "\"><a href=\"" << module << ".html\"><em>" << module << "</em></a></h2><ul>\n";
1846     }
1847 
module_endmodule_subset_html_actions1848     void module_end( std::string const & /*module*/ )
1849     {
1850         std::cout << "</ul>\n";
1851     }
1852 
from_pathmodule_subset_html_actions1853     void from_path( std::vector<std::string> const & path )
1854     {
1855         std::cout << "    <li>";
1856 
1857         for( std::vector<std::string>::const_iterator i = path.begin(); i != path.end(); ++i )
1858         {
1859             if( i != path.begin() )
1860             {
1861                 std::cout << " &#8674; ";
1862             }
1863 
1864             std::cout << "<code>" << *i << "</code>";
1865         }
1866 
1867         std::cout << "</li>\n";
1868     }
1869 };
1870 
output_module_subset_report(std::string const & module,bool track_sources,bool track_tests,bool html)1871 static void output_module_subset_report( std::string const & module, bool track_sources, bool track_tests, bool html )
1872 {
1873     if( html )
1874     {
1875         module_subset_html_actions actions;
1876         output_module_subset_report( module, track_sources, track_tests, actions );
1877     }
1878     else
1879     {
1880         module_subset_txt_actions actions;
1881         output_module_subset_report( module, track_sources, track_tests, actions );
1882     }
1883 }
1884 
1885 // --list-exceptions
1886 
list_exceptions()1887 static void list_exceptions()
1888 {
1889     std::string lm;
1890 
1891     for( std::map< std::string, std::set<std::string> >::const_iterator i = s_module_headers.begin(); i != s_module_headers.end(); ++i )
1892     {
1893         std::string module = i->first;
1894 
1895         std::replace( module.begin(), module.end(), '~', '/' );
1896 
1897         std::string const prefix = "boost/" + module;
1898         size_t const n = prefix.size();
1899 
1900         for( std::set< std::string >::const_iterator j = i->second.begin(); j != i->second.end(); ++j )
1901         {
1902             std::string const & header = *j;
1903 
1904             if( header.substr( 0, n+1 ) != prefix + '/' && header != prefix + ".hpp" )
1905             {
1906                 if( lm != module )
1907                 {
1908                     std::cout << module << ":\n";
1909                     lm = module;
1910                 }
1911 
1912                 std::cout << "  " << header << '\n';
1913             }
1914         }
1915     }
1916 }
1917 
1918 // --test
1919 
1920 struct module_test_primary_actions: public module_primary_actions
1921 {
1922     std::set< std::string > & m_;
1923 
module_test_primary_actionsmodule_test_primary_actions1924     module_test_primary_actions( std::set< std::string > & m ): m_( m )
1925     {
1926     }
1927 
headingmodule_test_primary_actions1928     void heading( std::string const & module )
1929     {
1930         std::cout << "Test dependencies for " << module << ":\n\n";
1931     }
1932 
module_startmodule_test_primary_actions1933     void module_start( std::string const & module )
1934     {
1935         std::cout << module << "\n";
1936         m_.insert( module );
1937     }
1938 
module_endmodule_test_primary_actions1939     void module_end( std::string const & /*module*/ )
1940     {
1941     }
1942 
header_startmodule_test_primary_actions1943     void header_start( std::string const & /*header*/ )
1944     {
1945     }
1946 
header_endmodule_test_primary_actions1947     void header_end( std::string const & /*header*/ )
1948     {
1949     }
1950 
from_headermodule_test_primary_actions1951     void from_header( std::string const & /*header*/ )
1952     {
1953     }
1954 };
1955 
1956 struct module_test_secondary_actions: public module_secondary_actions
1957 {
1958     std::set< std::string > & m_;
1959     std::string m2_;
1960 
module_test_secondary_actionsmodule_test_secondary_actions1961     module_test_secondary_actions( std::set< std::string > & m ): m_( m )
1962     {
1963     }
1964 
headingmodule_test_secondary_actions1965     void heading( std::string const & /*module*/ )
1966     {
1967     }
1968 
module_startmodule_test_secondary_actions1969     void module_start( std::string const & module )
1970     {
1971         m2_ = module;
1972     }
1973 
module_endmodule_test_secondary_actions1974     void module_end( std::string const & /*module*/ )
1975     {
1976     }
1977 
module_addsmodule_test_secondary_actions1978     void module_adds( std::string const & module )
1979     {
1980         if( m_.count( module ) == 0 )
1981         {
1982             std::cout << module << " (from " << m2_ << ")\n";
1983             m_.insert( module );
1984         }
1985     }
1986 };
1987 
output_module_test_report(std::string const & module)1988 static void output_module_test_report( std::string const & module )
1989 {
1990     std::set< std::string > m;
1991 
1992     module_test_primary_actions a1( m );
1993     output_module_primary_report( module, a1, true, true );
1994 
1995     std::cout << "\n";
1996 
1997     bool secondary = false;
1998     enable_secondary( secondary, true, false );
1999 
2000     std::set< std::string > m2( m );
2001     m2.insert( module );
2002 
2003     module_test_secondary_actions a2( m2 );
2004 
2005     output_module_secondary_report( module, m, a2 );
2006 }
2007 
2008 // --cmake
2009 
2010 struct collect_primary_dependencies: public module_primary_actions
2011 {
2012     std::set< std::string > set_;
2013 
headingcollect_primary_dependencies2014     void heading( std::string const & )
2015     {
2016     }
2017 
module_startcollect_primary_dependencies2018     void module_start( std::string const & module )
2019     {
2020         if( module == "(unknown)" ) return;
2021 
2022         set_.insert( module );
2023     }
2024 
module_endcollect_primary_dependencies2025     void module_end( std::string const & /*module*/ )
2026     {
2027     }
2028 
header_startcollect_primary_dependencies2029     void header_start( std::string const & /*header*/ )
2030     {
2031     }
2032 
header_endcollect_primary_dependencies2033     void header_end( std::string const & /*header*/ )
2034     {
2035     }
2036 
from_headercollect_primary_dependencies2037     void from_header( std::string const & /*header*/ )
2038     {
2039     }
2040 };
2041 
module_cmake_name(std::string module)2042 static std::string module_cmake_name( std::string module )
2043 {
2044     std::replace( module.begin(), module.end(), '~', '_' );
2045     return module;
2046 }
2047 
output_module_cmake_report(std::string module)2048 static void output_module_cmake_report( std::string module )
2049 {
2050     std::cout << "# Generated by `boostdep --cmake " << module << "`\n\n";
2051 
2052     std::replace( module.begin(), module.end(), '/', '~' );
2053 
2054     collect_primary_dependencies a1;
2055     output_module_primary_report( module, a1, false, false );
2056 
2057     if( !fs::exists( module_source_path( module ) ) )
2058     {
2059         for( std::set< std::string >::const_iterator i = a1.set_.begin(); i != a1.set_.end(); ++i )
2060         {
2061             std::cout << "boost_declare_dependency(INTERFACE " << module_cmake_name( *i ) << ")\n";
2062         }
2063     }
2064     else
2065     {
2066         collect_primary_dependencies a2;
2067         output_module_primary_report( module, a2, true, false );
2068 
2069         for( std::set< std::string >::const_iterator i = a1.set_.begin(); i != a1.set_.end(); ++i )
2070         {
2071             a2.set_.erase( *i );
2072             std::cout << "boost_declare_dependency(PUBLIC " << module_cmake_name( *i ) << ")\n";
2073         }
2074 
2075         std::cout << "\n";
2076 
2077         for( std::set< std::string >::const_iterator i = a2.set_.begin(); i != a2.set_.end(); ++i )
2078         {
2079             std::cout << "boost_declare_dependency(PRIVATE " << module_cmake_name( *i ) << ")\n";
2080         }
2081     }
2082 }
2083 
2084 // --list-missing-headers
2085 
2086 struct missing_header_actions: public module_primary_actions
2087 {
2088     std::string module_, module2_;
2089 
headingmissing_header_actions2090     void heading( std::string const & module )
2091     {
2092         module_ = module;
2093     }
2094 
module_startmissing_header_actions2095     void module_start( std::string const & module )
2096     {
2097         module2_ = module;
2098     }
2099 
module_endmissing_header_actions2100     void module_end( std::string const & /*module*/ )
2101     {
2102     }
2103 
header_startmissing_header_actions2104     void header_start( std::string const & header )
2105     {
2106         if( module2_ == "(unknown)" )
2107         {
2108             if( !module_.empty() )
2109             {
2110                 std::cout << module_ << ":\n";
2111                 module_.clear();
2112             }
2113 
2114             std::cout << "    <" << header << ">\n";
2115         }
2116     }
2117 
header_endmissing_header_actions2118     void header_end( std::string const & /*header*/ )
2119     {
2120     }
2121 
from_headermissing_header_actions2122     void from_header( std::string const & header )
2123     {
2124         if( module2_ == "(unknown)" )
2125         {
2126             std::cout << "        from <" << header << ">\n";
2127         }
2128     }
2129 };
2130 
list_missing_headers(std::string const & module)2131 static void list_missing_headers( std::string const & module )
2132 {
2133     missing_header_actions a;
2134     output_module_primary_report( module, a, false, false );
2135 }
2136 
list_missing_headers()2137 static void list_missing_headers()
2138 {
2139     for( std::set< std::string >::const_iterator i = s_modules.begin(); i != s_modules.end(); ++i )
2140     {
2141         list_missing_headers( *i );
2142     }
2143 }
2144 
2145 // --pkgconfig
2146 
2147 struct primary_pkgconfig_actions: public module_primary_actions
2148 {
2149     std::string version_;
2150     std::string list_;
2151 
headingprimary_pkgconfig_actions2152     void heading( std::string const & )
2153     {
2154     }
2155 
module_startprimary_pkgconfig_actions2156     void module_start( std::string const & module )
2157     {
2158         if( module == "(unknown)" ) return;
2159 
2160         std::string m2( module );
2161         std::replace( m2.begin(), m2.end(), '~', '_' );
2162 
2163         if( !list_.empty() )
2164         {
2165             list_ += ", ";
2166         }
2167 
2168         list_ += "boost_" + m2 + " = " + version_;
2169     }
2170 
module_endprimary_pkgconfig_actions2171     void module_end( std::string const & )
2172     {
2173     }
2174 
header_startprimary_pkgconfig_actions2175     void header_start( std::string const & )
2176     {
2177     }
2178 
header_endprimary_pkgconfig_actions2179     void header_end( std::string const & )
2180     {
2181     }
2182 
from_headerprimary_pkgconfig_actions2183     void from_header( std::string const & )
2184     {
2185     }
2186 };
2187 
output_requires(std::string const & section,std::string const & version,std::set<std::string> const & s)2188 static void output_requires( std::string const & section, std::string const & version, std::set< std::string > const & s )
2189 {
2190     bool first = true;
2191 
2192     for( std::set< std::string >::const_iterator i = s.begin(); i != s.end(); ++i )
2193     {
2194         if( first )
2195         {
2196             std::cout << section << ": ";
2197             first = false;
2198         }
2199         else
2200         {
2201             std::cout << ", ";
2202         }
2203 
2204         std::string m2( *i );
2205         std::replace( m2.begin(), m2.end(), '~', '_' );
2206 
2207         std::cout << "boost_" << m2 << " = " << version;
2208     }
2209 }
2210 
output_pkgconfig(std::string const & module,std::string const & version,int argc,char const * argv[])2211 static void output_pkgconfig( std::string const & module, std::string const & version, int argc, char const* argv[] )
2212 {
2213     for( int i = 0; i < argc; ++i )
2214     {
2215         std::cout << argv[ i ] << '\n';
2216     }
2217 
2218     std::cout << '\n';
2219 
2220     std::string m2( module );
2221     std::replace( m2.begin(), m2.end(), '/', '_' );
2222 
2223     std::string m3( module );
2224     std::replace( m3.begin(), m3.end(), '/', '~' );
2225 
2226     std::cout << "Name: boost_" << module << '\n';
2227     std::cout << "Description: Boost C++ library '" << module << "'\n";
2228     std::cout << "Version: " << version << '\n';
2229     std::cout << "URL: http://www.boost.org/libs/" << module << '\n';
2230     std::cout << "Cflags: -I${includedir}\n";
2231 
2232     if( fs::exists( module_build_path( module ) ) && fs::exists( module_source_path( module ) ) )
2233     {
2234         std::cout << "Libs: -L${libdir} -lboost_" << m2 << "\n";
2235     }
2236 
2237     collect_primary_dependencies a1;
2238     output_module_primary_report( m3, a1, false, false );
2239 
2240     if( !a1.set_.empty() )
2241     {
2242         output_requires( "Requires", version, a1.set_ );
2243         std::cout << std::endl;
2244     }
2245 
2246     collect_primary_dependencies a2;
2247     output_module_primary_report( m3, a2, true, false );
2248 
2249     for( std::set< std::string >::const_iterator i = a1.set_.begin(); i != a1.set_.end(); ++i )
2250     {
2251         a2.set_.erase( *i );
2252     }
2253 
2254     if( !a2.set_.empty() )
2255     {
2256         output_requires( "Requires.private", version, a2.set_ );
2257         std::cout << std::endl;
2258     }
2259 }
2260 
2261 // --subset-for
2262 
output_directory_subset_report(std::string const & module,std::set<std::string> const & headers,bool html)2263 static void output_directory_subset_report( std::string const & module, std::set<std::string> const & headers, bool html )
2264 {
2265     for( std::set<std::string>::const_iterator i = headers.begin(); i != headers.end(); ++i )
2266     {
2267         std::map< std::string, std::set< std::string > > deps;
2268         std::map< std::string, std::set< std::string > > from;
2269 
2270         std::ifstream is( i->c_str() );
2271         scan_header_dependencies( *i, is, deps, from );
2272 
2273         for( std::map< std::string, std::set< std::string > >::const_iterator j = from.begin(); j != from.end(); ++j )
2274         {
2275             for( std::set<std::string>::const_iterator k = j->second.begin(); k != j->second.end(); ++k )
2276             {
2277                 s_header_includes[ *k ].insert( j->first );
2278             }
2279         }
2280     }
2281 
2282     if( html )
2283     {
2284         module_subset_html_actions actions;
2285         output_module_subset_report_( module, headers, actions );
2286     }
2287     else
2288     {
2289         module_subset_txt_actions actions;
2290         output_module_subset_report_( module, headers, actions );
2291     }
2292 }
2293 
2294 // list_buildable_dependencies
2295 
2296 struct list_buildable_dependencies_actions: public module_overview_actions
2297 {
2298     std::set< std::string > buildable_;
2299 
2300     std::set< std::string > deps_;
2301     bool headers_;
2302 
list_buildable_dependencies_actionslist_buildable_dependencies_actions2303     list_buildable_dependencies_actions(): headers_()
2304     {
2305     }
2306 
beginlist_buildable_dependencies_actions2307     void begin()
2308     {
2309         std::cout << "# Generated by `boostdep --list-buildable-dependencies`\n\n";
2310     }
2311 
endlist_buildable_dependencies_actions2312     void end()
2313     {
2314     }
2315 
module_startlist_buildable_dependencies_actions2316     void module_start( std::string const & module )
2317     {
2318         deps_.clear();
2319         headers_ = false;
2320 
2321         if( buildable_.count( module ) )
2322         {
2323             std::cout << module << " =";
2324         }
2325     }
2326 
module_endlist_buildable_dependencies_actions2327     void module_end( std::string const & module )
2328     {
2329         if( buildable_.count( module ) )
2330         {
2331             if( headers_ )
2332             {
2333                 std::cout << " headers";
2334             }
2335 
2336             for( std::set< std::string >::iterator i = deps_.begin(); i != deps_.end(); ++i )
2337             {
2338                 std::cout << " " << *i;
2339             }
2340 
2341             std::cout << " ;\n";
2342         }
2343     }
2344 
module2list_buildable_dependencies_actions2345     void module2( std::string const & module )
2346     {
2347         if( module == "(unknown)" ) return;
2348 
2349         if( buildable_.count( module ) == 0 )
2350         {
2351             headers_ = true;
2352         }
2353         else
2354         {
2355             deps_.insert( module );
2356         }
2357     }
2358 };
2359 
list_buildable_dependencies()2360 static void list_buildable_dependencies()
2361 {
2362     list_buildable_dependencies_actions actions;
2363 
2364     for( std::set< std::string >::iterator i = s_modules.begin(); i != s_modules.end(); ++i )
2365     {
2366         if( fs::exists( module_build_path( *i ) ) && fs::exists( module_source_path( *i ) ) )
2367         {
2368             actions.buildable_.insert( *i );
2369         }
2370     }
2371 
2372     output_module_overview_report( actions );
2373 }
2374 
2375 //
2376 
find_boost_root()2377 static bool find_boost_root()
2378 {
2379     for( int i = 0; i < 32; ++i )
2380     {
2381         if( fs::exists( "Jamroot" ) )
2382         {
2383             return true;
2384         }
2385 
2386         fs::path p = fs::current_path();
2387 
2388         if( p == p.root_path() )
2389         {
2390             return false;
2391         }
2392 
2393         fs::current_path( p.parent_path() );
2394     }
2395 
2396     return false;
2397 }
2398 
is_boost_root(fs::path const & p)2399 static bool is_boost_root( fs::path const & p )
2400 {
2401     return fs::exists( p / "Jamroot" );
2402 }
2403 
2404 // teebuf
2405 
2406 class teebuf: public std::streambuf
2407 {
2408 private:
2409 
2410     std::streambuf * sb1_;
2411     std::streambuf * sb2_;
2412 
2413 public:
2414 
teebuf(std::streambuf * sb1,std::streambuf * sb2)2415     teebuf( std::streambuf * sb1, std::streambuf * sb2 ): sb1_( sb1 ), sb2_( sb2 )
2416     {
2417     }
2418 
2419 private:
2420 
overflow(int c)2421     virtual int overflow( int c )
2422     {
2423         int r1 = sb1_->sputc( c );
2424         int r2 = sb2_->sputc( c );
2425 
2426         return r1 == EOF || r2 == EOF? EOF : c;
2427     }
2428 
sync()2429     virtual int sync()
2430     {
2431         int r1 = sb1_->pubsync();
2432         int r2 = sb2_->pubsync();
2433 
2434         return r1 == 0 && r2 == 0? 0 : -1;
2435     }
2436 };
2437 
2438 // save_cout_rdbuf
2439 
2440 class save_cout_rdbuf
2441 {
2442 private:
2443 
2444     std::streambuf * sb_;
2445 
2446 public:
2447 
save_cout_rdbuf()2448     save_cout_rdbuf(): sb_( std::cout.rdbuf() )
2449     {
2450     }
2451 
~save_cout_rdbuf()2452     ~save_cout_rdbuf()
2453     {
2454         std::cout.rdbuf( sb_ );
2455     }
2456 };
2457 
2458 // main
2459 
main(int argc,char const * argv[])2460 int main( int argc, char const* argv[] )
2461 {
2462     if( argc < 2 )
2463     {
2464         std::cout <<
2465 
2466             "Usage:\n"
2467             "\n"
2468             "    boostdep --list-modules\n"
2469             "    boostdep --list-buildable\n"
2470             "    boostdep [--track-sources] [--track-tests] --list-dependencies\n"
2471             "    boostdep --list-exceptions\n"
2472             "    boostdep --list-missing-headers\n"
2473             "    boostdep --list-buildable-dependencies\n"
2474             "\n"
2475             "    boostdep [options] --module-overview\n"
2476             "    boostdep [options] --module-levels\n"
2477             "    boostdep [options] --module-weights\n"
2478             "\n"
2479             "    boostdep [options] [--primary] <module>\n"
2480             "    boostdep [options] --secondary <module>\n"
2481             "    boostdep [options] --reverse <module>\n"
2482             "    boostdep [options] --subset <module>\n"
2483             "    boostdep [options] [--header] <header>\n"
2484             "    boostdep --test <module>\n"
2485             "    boostdep --cmake <module>\n"
2486             "    boostdep --pkgconfig <module> <version> [<var>=<value>] [<var>=<value>]...\n"
2487             "    boostdep [options] --subset-for <directory>\n"
2488             "\n"
2489             "    [options]: [--boost-root <path-to-boost>]\n"
2490             "               [--[no-]track-sources] [--[no-]track-tests]\n"
2491             "               [--html-title <title>] [--html-footer <footer>]\n"
2492             "               [--html-stylesheet <stylesheet>] [--html-prefix <prefix>]\n"
2493             "               [--html]\n";
2494 
2495         return -1;
2496     }
2497 
2498     bool root_set = false;
2499 
2500     for( int i = 0; i < argc; ++i )
2501     {
2502         std::string option = argv[ i ];
2503 
2504         if( option == "--boost-root" )
2505         {
2506             if( i + 1 < argc )
2507             {
2508                 fs::path p( argv[ ++i ] );
2509 
2510                 if( is_boost_root( p ) )
2511                 {
2512                     fs::current_path( p );
2513                     root_set = true;
2514                 }
2515                 else
2516                 {
2517                     std::cerr << "'" << p.string() << "': not a valid Boost root.\n";
2518                     return -2;
2519                 }
2520             }
2521             else
2522             {
2523                 std::cerr << "'" << option << "': missing argument.\n";
2524                 return -2;
2525             }
2526         }
2527     }
2528 
2529     if( !root_set && !find_boost_root() )
2530     {
2531         char const * env_root = std::getenv( "BOOST_ROOT" );
2532 
2533         if( env_root && is_boost_root( env_root ) )
2534         {
2535             fs::current_path( env_root );
2536         }
2537         else
2538         {
2539             std::cerr << "boostdep: Could not find Boost root.\n";
2540             return -2;
2541         }
2542     }
2543 
2544     try
2545     {
2546         build_header_map();
2547     }
2548     catch( fs::filesystem_error const & x )
2549     {
2550         std::cerr << x.what() << std::endl;
2551     }
2552 
2553     bool html = false;
2554     bool secondary = false;
2555     bool track_sources = false;
2556     bool track_tests = false;
2557 
2558     std::string html_title = "Boost Dependency Report";
2559     std::string html_footer;
2560     std::string html_stylesheet;
2561     std::string html_prefix;
2562 
2563     std::ostringstream captured_output;
2564     teebuf tsb( std::cout.rdbuf(), captured_output.rdbuf() );
2565 
2566     save_cout_rdbuf scrdb;
2567 
2568     for( int i = 1; i < argc; ++i )
2569     {
2570         std::string option = argv[ i ];
2571 
2572         if( option == "--boost-root" )
2573         {
2574             ++i;
2575         }
2576         else if( option == "--list-modules" )
2577         {
2578             list_modules();
2579         }
2580         else if( option == "--list-buildable" )
2581         {
2582             list_buildable();
2583         }
2584         else if( option == "--title" || option == "--html-title" )
2585         {
2586             if( i + 1 < argc )
2587             {
2588                 html_title = argv[ ++i ];
2589             }
2590         }
2591         else if( option == "--footer" || option == "--html-footer" )
2592         {
2593             if( i + 1 < argc )
2594             {
2595                 html_footer = argv[ ++i ];
2596             }
2597         }
2598         else if( option == "--html-stylesheet" )
2599         {
2600             if( i + 1 < argc )
2601             {
2602                 html_stylesheet = argv[ ++i ];
2603             }
2604         }
2605         else if( option == "--html-prefix" )
2606         {
2607             if( i + 1 < argc )
2608             {
2609                 html_prefix = argv[ ++i ];
2610             }
2611         }
2612         else if( option == "--html" )
2613         {
2614             if( !html )
2615             {
2616                 html = true;
2617                 output_html_header( html_title, html_stylesheet, html_prefix );
2618             }
2619         }
2620         else if( option == "--track-sources" )
2621         {
2622             track_sources = true;
2623         }
2624         else if( option == "--no-track-sources" )
2625         {
2626             track_sources = false;
2627         }
2628         else if( option == "--track-tests" )
2629         {
2630             track_tests = true;
2631         }
2632         else if( option == "--no-track-tests" )
2633         {
2634             track_tests = false;
2635         }
2636         else if( option == "--primary" )
2637         {
2638             if( i + 1 < argc )
2639             {
2640                 output_module_primary_report( argv[ ++i ], html, track_sources, track_tests );
2641             }
2642         }
2643         else if( option == "--secondary" )
2644         {
2645             if( i + 1 < argc )
2646             {
2647                 enable_secondary( secondary, track_sources, track_tests );
2648                 output_module_secondary_report( argv[ ++i ], html );
2649             }
2650         }
2651         else if( option == "--reverse" )
2652         {
2653             if( i + 1 < argc )
2654             {
2655                 enable_secondary( secondary, track_sources, track_tests );
2656                 output_module_reverse_report( argv[ ++i ], html );
2657             }
2658         }
2659         else if( option == "--header" )
2660         {
2661             if( i + 1 < argc )
2662             {
2663                 enable_secondary( secondary, track_sources, track_tests );
2664                 output_header_report( argv[ ++i ], html );
2665             }
2666         }
2667         else if( option == "--subset" )
2668         {
2669             if( i + 1 < argc )
2670             {
2671                 enable_secondary( secondary, track_sources, track_tests );
2672                 output_module_subset_report( argv[ ++i ], track_sources, track_tests, html );
2673             }
2674         }
2675         else if( option == "--test" )
2676         {
2677             if( i + 1 < argc )
2678             {
2679                 output_module_test_report( argv[ ++i ] );
2680             }
2681         }
2682         else if( option == "--cmake" )
2683         {
2684             if( i + 1 < argc )
2685             {
2686                 output_module_cmake_report( argv[ ++i ] );
2687             }
2688         }
2689         else if( option == "--module-levels" )
2690         {
2691             enable_secondary( secondary, track_sources, track_tests );
2692             output_module_level_report( html );
2693         }
2694         else if( option == "--module-overview" )
2695         {
2696             enable_secondary( secondary, track_sources, track_tests );
2697             output_module_overview_report( html );
2698         }
2699         else if( option == "--module-weights" )
2700         {
2701             enable_secondary( secondary, track_sources, track_tests );
2702             output_module_weight_report( html );
2703         }
2704         else if( option == "--list-dependencies" )
2705         {
2706             enable_secondary( secondary, track_sources, track_tests );
2707             list_dependencies();
2708         }
2709         else if( option == "--list-exceptions" )
2710         {
2711             list_exceptions();
2712         }
2713         else if( option == "--list-missing-headers" )
2714         {
2715             list_missing_headers();
2716         }
2717         else if( option == "--pkgconfig" )
2718         {
2719             if( i + 2 < argc )
2720             {
2721                 std::string module = argv[ ++i ];
2722                 std::string version = argv[ ++i ];
2723 
2724                 ++i;
2725 
2726                 output_pkgconfig( module, version, argc - i, argv + i );
2727             }
2728             else
2729             {
2730                 std::cerr << "'" << option << "': missing module or version.\n";
2731             }
2732 
2733             break;
2734         }
2735         else if( option == "--subset-for" )
2736         {
2737             if( i + 1 < argc )
2738             {
2739                 std::string module = argv[ ++i ];
2740 
2741                 enable_secondary( secondary, track_sources, track_tests );
2742 
2743                 std::set<std::string> headers;
2744                 add_module_headers( module, headers );
2745 
2746                 output_directory_subset_report( module, headers, html );
2747             }
2748             else
2749             {
2750                 std::cerr << "'" << option << "': missing argument.\n";
2751             }
2752 
2753             break;
2754         }
2755         else if( option == "--list-buildable-dependencies" )
2756         {
2757             enable_secondary( secondary, true, false );
2758             list_buildable_dependencies();
2759         }
2760         else if( option == "--capture-output" )
2761         {
2762             std::cout.rdbuf( &tsb );
2763         }
2764         else if( option == "--compare-output" )
2765         {
2766             if( i + 1 < argc )
2767             {
2768                 std::string fn = argv[ ++i ];
2769                 std::fstream is( fn.c_str() );
2770 
2771                 if( !is )
2772                 {
2773                     std::cerr << option << " '" << fn << "': could not open file.\n";
2774                     return 1;
2775                 }
2776 
2777                 std::istreambuf_iterator<char> first( is ), last;
2778                 std::string fc( first, last );
2779 
2780                 if( fc != captured_output.str() )
2781                 {
2782                     std::cerr << option << " '" << fn << "': output does not match; expected output:\n---\n" << fc << "---\n";
2783                     return 1;
2784                 }
2785 
2786                 std::cerr << option << " '" << fn << "': output matches.\n";
2787                 captured_output.str( "" );
2788             }
2789             else
2790             {
2791                 std::cerr << "'" << option << "': missing argument.\n";
2792                 return 1;
2793             }
2794         }
2795         else if( s_modules.count( option ) )
2796         {
2797             output_module_primary_report( option, html, track_sources, track_tests );
2798         }
2799         else if( s_header_map.count( option ) )
2800         {
2801             enable_secondary( secondary, track_sources, track_tests );
2802             output_header_report( option, html );
2803         }
2804         else
2805         {
2806             std::cerr << "'" << option << "': not an option, module or header.\n";
2807         }
2808     }
2809 
2810     if( html )
2811     {
2812         output_html_footer( html_footer );
2813     }
2814 }
2815