2121#include < cstdlib>
2222#include < filesystem>
2323#include < format>
24- #include < fstream>
2524#include < string>
2625#include < unordered_set>
2726#include < vector>
@@ -309,54 +308,91 @@ void WriteJson(const fs::path &path, llvm::json::Object object) {
309308 os << llvm::json::Value (std::move (object));
310309}
311310
312- std::vector<std::string> ReadCXXFlags (const fs::path &rule_dir) {
313- std::vector<std::string> flags;
314- std::ifstream file (rule_dir / " cxxflags" );
315- for (std::string flag; std::getline (file, flag);) {
316- if (!flag.empty ()) {
317- flags.push_back (flag);
318- }
311+ llvm::json::Object ReadJson (const fs::path &path) {
312+ auto buffer = llvm::MemoryBuffer::getFile (path.string ());
313+ if (!buffer) {
314+ llvm::errs () << " ERROR: cannot read " << path.string () << ' \n ' ;
315+ std::exit (EXIT_FAILURE );
316+ }
317+ auto parsed = llvm::json::parse ((*buffer)->getBuffer ());
318+ if (!parsed || !parsed->getAsObject ()) {
319+ llvm::errs () << " ERROR: cannot parse " << path.string () << ' \n ' ;
320+ std::exit (EXIT_FAILURE );
321+ }
322+ return std::move (*parsed->getAsObject ());
323+ }
324+
325+ unsigned SinceStandard (const fs::path &src_path) {
326+ auto stem_string = src_path.stem ().string ();
327+ llvm::StringRef stem = stem_string;
328+ unsigned standard = 11 ;
329+ if (stem.consume_front (" src_cxx" ) && stem.getAsInteger (10 , standard)) {
330+ llvm::errs () << " ERROR: unexpected rule file " << src_path.string () << ' \n ' ;
331+ std::exit (EXIT_FAILURE );
319332 }
320- return flags ;
333+ return standard ;
321334}
322335
323- void IndexRules (const fs::path &rules_dir, const fs::path &index_dir,
324- const std::vector<std::string> &excluded,
325- const std::vector<std::string> &common_headers) {
326- std::vector<fs::path> rule_dirs;
327- for (const auto &entry : fs::directory_iterator (rules_dir)) {
328- if (entry.is_directory () &&
329- !llvm::is_contained (excluded, entry.path ().filename ().string ())) {
330- rule_dirs.push_back (entry.path ());
336+ void MergeRules (llvm::json::Object &into, llvm::json::Object &&from) {
337+ for (auto &[key, entries] : from) {
338+ for (auto &entry : *entries.getAsArray ()) {
339+ AddRule (into, key.str (), std::move (entry));
331340 }
332341 }
333- std::ranges::sort (rule_dirs);
334- fs::create_directories (index_dir);
335- for (const char *lang : {" c" , " cpp" }) {
336- std::string common;
337- if (lang == std::string (" cpp" )) {
338- for (const auto &header : common_headers) {
339- common += std::format (" #include \" {}\"\n " , header);
340- }
342+ }
343+
344+ void IndexDir (const fs::path &rule_dir, const fs::path &out) {
345+ auto dir_name = rule_dir.filename ().string ();
346+ llvm::json::Object c_rules;
347+ if (fs::exists (rule_dir / " src.c" )) {
348+ Index (rule_dir / " src.c" , /* is_c=*/ true , dir_name, {}, c_rules);
349+ }
350+ std::vector<fs::path> cxx_srcs;
351+ for (const auto &entry : fs::directory_iterator (rule_dir)) {
352+ auto name = entry.path ().filename ().string ();
353+ if (name == " src.c" || name == " src.cpp" ||
354+ (name.starts_with (" src_cxx" ) && name.ends_with (" .cpp" ))) {
355+ cxx_srcs.push_back (entry.path ());
341356 }
342- bool is_c = lang == std::string (" c" );
357+ }
358+ std::ranges::sort (cxx_srcs);
359+ llvm::json::Object cxx_rules;
360+ for (const auto &src_path : cxx_srcs) {
361+ auto standard = SinceStandard (src_path);
343362 llvm::json::Object rules;
344- for (const auto &rule_dir : rule_dirs) {
345- for (const char *name : {" src.c" , " src.cpp" }) {
346- auto src_path = rule_dir / name;
347- if (!fs::exists (src_path) || (is_c && src_path.extension () != " .c" )) {
348- continue ;
349- }
350- llvm::errs () << " Indexing " << src_path.string () << " as " << lang
351- << ' \n ' ;
352- Index (src_path, is_c, rule_dir.filename ().string (),
353- ReadCXXFlags (rule_dir), rules);
363+ Index (src_path, /* is_c=*/ false , dir_name,
364+ {std::format (" -std=c++{}" , standard)}, rules);
365+ for (auto &[key, entries] : rules) {
366+ for (auto &entry : *entries.getAsArray ()) {
367+ (*entry.getAsObject ())[" since" ] = standard;
354368 }
355369 }
356- WriteJson (index_dir / (std::string (lang) + " .json" ),
357- llvm::json::Object{{" common" , std::move (common)},
358- {" rules" , std::move (rules)}});
370+ MergeRules (cxx_rules, std::move (rules));
371+ }
372+ WriteJson (out, llvm::json::Object{{" c" , std::move (c_rules)},
373+ {" cpp" , std::move (cxx_rules)}});
374+ }
375+
376+ void MergeIndexes (const std::vector<std::string> &parts,
377+ const std::vector<std::string> &common_headers,
378+ const fs::path &index_dir) {
379+ llvm::json::Object c_rules;
380+ llvm::json::Object cxx_rules;
381+ for (const auto &part : parts) {
382+ auto json = ReadJson (part);
383+ MergeRules (c_rules, std::move (*json.getObject (" c" )));
384+ MergeRules (cxx_rules, std::move (*json.getObject (" cpp" )));
385+ }
386+ std::string common;
387+ for (const auto &header : common_headers) {
388+ common += std::format (" #include \" {}\"\n " , header);
359389 }
390+ fs::create_directories (index_dir);
391+ WriteJson (index_dir / " c.json" ,
392+ llvm::json::Object{{" common" , " " }, {" rules" , std::move (c_rules)}});
393+ WriteJson (index_dir / " cpp.json" ,
394+ llvm::json::Object{{" common" , std::move (common)},
395+ {" rules" , std::move (cxx_rules)}});
360396}
361397
362398} // namespace cpp2rust
@@ -365,23 +401,35 @@ namespace {
365401
366402llvm::cl::OptionCategory cat (" cpp-rule-indexer options" );
367403
368- llvm::cl::list<std::string>
369- Excluded (" exclude" , llvm::cl::desc(" Rule directory that is not indexed" ),
370- llvm::cl::value_desc (" rule-dir" ), llvm::cl::ZeroOrMore,
371- llvm::cl::cat (cat));
404+ llvm::cl::opt<std::string> Dir (" dir" , llvm::cl::desc(" Rule directory to index" ),
405+ llvm::cl::value_desc(" rule-dir" ),
406+ llvm::cl::cat(cat));
407+
408+ llvm::cl::list<std::string> Parts (llvm::cl::Positional,
409+ llvm::cl::desc (" <indexes to merge>" ),
410+ llvm::cl::ZeroOrMore, llvm::cl::cat(cat));
372411
373412llvm::cl::list<std::string> CommonHeaders (
374413 " common-header" , llvm::cl::desc(" Header the C++ rules depend on" ),
375414 llvm::cl::value_desc(" header" ), llvm::cl::ZeroOrMore, llvm::cl::cat(cat));
376415
416+ llvm::cl::opt<std::string>
417+ Out (" o" ,
418+ llvm::cl::desc (" Output part of a rule directory, or index directory" ),
419+ llvm::cl::value_desc(" path" ), llvm::cl::Required, llvm::cl::cat(cat));
420+
377421} // namespace
378422
379423int main (int argc, char *argv[]) {
380424 llvm::cl::HideUnrelatedOptions (cat);
381425 llvm::cl::ParseCommandLineOptions (argc, argv);
382426
383- cpp2rust::IndexRules (RULES_SRC_DIR , RULES_INDEX_DIR ,
384- {Excluded.begin (), Excluded.end ()},
385- {CommonHeaders.begin (), CommonHeaders.end ()});
427+ if (!Dir.empty ()) {
428+ cpp2rust::IndexDir (Dir.getValue (), Out.getValue ());
429+ } else {
430+ cpp2rust::MergeIndexes ({Parts.begin (), Parts.end ()},
431+ {CommonHeaders.begin (), CommonHeaders.end ()},
432+ Out.getValue ());
433+ }
386434 return EXIT_SUCCESS ;
387435}
0 commit comments