228 boost::program_options::options_description desc(
"This program validates OKS git repository for commit by pre-receive hook");
230 std::vector<std::string> created, updated, deleted;
231 bool circular_dependency_between_includes_is_error =
true;
232 bool verbose =
false;
234 std::size_t pipeline_size = 4;
238 std::vector<std::string> app_types_list;
239 std::vector<std::string> segments_list;
242 (
"add,a", boost::program_options::value<std::vector<std::string> >(&created)->multitoken(),
"list of new OKS files and directories to be added to the repository")
243 (
"update,u", boost::program_options::value<std::vector<std::string> >(&updated)->multitoken(),
"list of new OKS files and directories to be updated in the repository")
244 (
"remove,r", boost::program_options::value<std::vector<std::string> >(&deleted)->multitoken(),
"list of new OKS files and directories to be removed from the repository")
245 (
"permissive-circular-dependencies-between-includes,C",
"downgrade severity of detected circular dependencies between includes from errors to warnings")
246 (
"user,U", boost::program_options::value<std::string>(&user),
"user id")
247 (
"threads-number,t", boost::program_options::value<std::size_t>(&pipeline_size)->default_value(pipeline_size),
"number of threads used by validation pipeline")
248 (
"verbose,v",
"Print debug information")
249 (
"help,h",
"Print help message");
251 boost::program_options::variables_map vm;
252 boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
254 if (vm.count(
"help"))
256 std::cout << desc << std::endl;
260 if (vm.count(
"permissive-circular-dependencies-between-includes"))
261 circular_dependency_between_includes_is_error =
false;
263 if (vm.count(
"verbose"))
266 boost::program_options::notify(vm);
269 catch (std::exception& ex)
271 std::cerr <<
"Command line parsing errors occurred:\n" << ex.what() << std::endl;
285 std::cerr <<
"There is no OKS repository set (check TDAQ_DB_REPOSITORY)" << std::endl;
291 auto start_usage = std::chrono::steady_clock::now();
295 std::set<std::string> directories;
299 std::map<std::string, std::set<std::string>> file_explicit_includes;
301 for (
auto& p : std::filesystem::recursive_directory_iterator(
"."))
302 if (std::filesystem::is_directory(p))
303 directories.insert(p.path().native().substr(2));
304 else if (std::filesystem::is_regular_file(p) && p.path().native().find(
"./.git") != 0 && p.path().native().find(
"./admin") != 0 && p.path().native().find(
"./README.md") != 0)
305 kernel.
get_includes(p.path().native(), file_explicit_includes[p.path().native().substr(2)],
true);
308 log_timestamp(
Debug) <<
"scan " << file_explicit_includes.size() <<
" repository files in " << std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-start_usage).count() / 1000. <<
" ms\n";
311 auto start_usage2 = std::chrono::steady_clock::now();
313 std::set<std::string> all_includes;
316 for (
const auto& f : file_explicit_includes)
318 for (
const auto& i : f.second)
320 if(file_explicit_includes.find(i) != file_explicit_includes.end())
322 all_includes.insert(i);
326 std::cerr <<
"Cannot find file \"" << i <<
"\" included by \"" << f.first <<
"\"" << std::endl;
333 log_timestamp(
Debug) <<
"check existence of includes in " << std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-start_usage2).count() / 1000. <<
" ms\n";
336 auto start_usage3 = std::chrono::steady_clock::now();
339 std::map<std::string, std::set<std::string>> file_all_includes;
341 for(
auto& x : file_explicit_includes)
344 define_includes(x.first, x.second, file_all_includes, file_explicit_includes, cd_fuse);
347 auto stop_usage3 = std::chrono::steady_clock::now();
350 log_timestamp(
Debug) <<
"calculated inclusion graph in " << std::chrono::duration_cast<std::chrono::microseconds>(stop_usage3-start_usage3).count() / 1000. <<
" ms\n";
352 log_timestamp() <<
"process " << file_explicit_includes.size() <<
" repository files and their includes in " << std::chrono::duration_cast<std::chrono::microseconds>(stop_usage3-start_usage).count() / 1000. <<
" ms" << std::endl;
359 if (circular_dependency_between_includes_is_error ==
true)
365 std::ostringstream text;
366 text <<
"ALL INCLUDES:\n";
368 for (
const auto& x : file_all_includes)
370 text <<
"FILE \"" << x.first <<
"\" has " << x.second.size() <<
" includes:\n";
372 for (
const auto& y : x.second)
373 text <<
" - \"" << y <<
"\"\n";
382 auto ignore_files = [](std::string& x)
384 static const std::string readme_file(
"README.md");
385 return x != readme_file;
389 std::set<std::string> modified;
390 std::copy_if(created.begin(), created.end(), std::inserter(modified, modified.end()), ignore_files);
391 std::copy_if(updated.begin(), updated.end(), std::inserter(modified, modified.end()), ignore_files);
394 for (
const auto& x : modified)
397 std::copy_if(deleted.begin(), deleted.end(), std::inserter(modified, modified.end()), ignore_files);
399 for (
const auto& f : file_explicit_includes)
400 if (all_includes.find(f.first) == all_includes.end())
402 if (modified.empty() ==
false)
404 if (modified.find(f.first) == modified.end())
406 const auto& file_includes = file_all_includes[f.first];
410 for (
const auto& x : modified)
411 if (file_includes.find(x) != file_includes.end())
414 TLOG_DEBUG(1) <<
"file \"" << f.first <<
"\" contains modified include \"" << x <<
'\"';
420 TLOG_DEBUG(1) <<
"skip file \"" << f.first <<
'\"';
426 TLOG_DEBUG(1) <<
"list of modified files contains file \"" << f.first <<
'\"';
430 if (modified.find(f.first) == modified.end())
447 catch (std::exception & e)