218 boost::program_options::options_description desc(
219 "This program validates OKS git repository for commit by pre-receive hook");
221 std::vector<std::string> created, updated, deleted;
222 bool circular_dependency_between_includes_is_error =
true;
223 bool verbose =
false;
225 std::size_t pipeline_size = 4;
228 std::vector<std::string> app_types_list;
229 std::vector<std::string> segments_list;
231 desc.add_options()(
"add,a",
232 boost::program_options::value<std::vector<std::string>>(&created)->multitoken(),
233 "list of new OKS files and directories to be added to the repository")(
235 boost::program_options::value<std::vector<std::string>>(&updated)->multitoken(),
236 "list of new OKS files and directories to be updated in the repository")(
238 boost::program_options::value<std::vector<std::string>>(&deleted)->multitoken(),
239 "list of new OKS files and directories to be removed from the repository")(
240 "permissive-circular-dependencies-between-includes,C",
241 "downgrade severity of detected circular dependencies between includes from errors to warnings")(
242 "user,U", boost::program_options::value<std::string>(&user),
"user id")(
244 boost::program_options::value<std::size_t>(&pipeline_size)->default_value(pipeline_size),
245 "number of threads used by validation pipeline")(
"verbose,v",
"Print debug information")(
"help,h",
246 "Print help message");
248 boost::program_options::variables_map vm;
249 boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
251 if (vm.count(
"help")) {
252 std::cout << desc << std::endl;
256 if (vm.count(
"permissive-circular-dependencies-between-includes"))
257 circular_dependency_between_includes_is_error =
false;
259 if (vm.count(
"verbose"))
262 boost::program_options::notify(vm);
264 }
catch (std::exception& ex) {
265 std::cerr <<
"Command line parsing errors occurred:\n" << ex.what() << std::endl;
276 std::cerr <<
"There is no OKS repository set (check TDAQ_DB_REPOSITORY)" << std::endl;
282 auto start_usage = std::chrono::steady_clock::now();
286 std::set<std::string> directories;
289 std::map<std::string, std::set<std::string>> file_explicit_includes;
291 for (
auto& p : std::filesystem::recursive_directory_iterator(
"."))
292 if (std::filesystem::is_directory(p))
293 directories.insert(p.path().native().substr(2));
294 else if (std::filesystem::is_regular_file(p) && p.path().native().find(
"./.git") != 0 &&
295 p.path().native().find(
"./admin") != 0 && p.path().native().find(
"./README.md") != 0)
296 kernel.
get_includes(p.path().native(), file_explicit_includes[p.path().native().substr(2)],
true);
299 log_timestamp(
Debug) <<
"scan " << file_explicit_includes.size() <<
" repository files in "
300 << std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() -
306 auto start_usage2 = std::chrono::steady_clock::now();
308 std::set<std::string> all_includes;
311 for (
const auto& f : file_explicit_includes) {
312 for (
const auto& i : f.second) {
313 if (file_explicit_includes.find(i) != file_explicit_includes.end()) {
314 all_includes.insert(i);
316 std::cerr <<
"Cannot find file \"" << i <<
"\" included by \"" << f.first <<
"\"" << std::endl;
324 << std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() -
330 auto start_usage3 = std::chrono::steady_clock::now();
333 std::map<std::string, std::set<std::string>> file_all_includes;
335 for (
auto& x : file_explicit_includes) {
337 define_includes(x.first, x.second, file_all_includes, file_explicit_includes, cd_fuse);
340 auto stop_usage3 = std::chrono::steady_clock::now();
344 <<
"calculated inclusion graph in "
345 << std::chrono::duration_cast<std::chrono::microseconds>(stop_usage3 - start_usage3).count() / 1000. <<
" ms\n";
347 log_timestamp() <<
"process " << file_explicit_includes.size() <<
" repository files and their includes in "
348 << std::chrono::duration_cast<std::chrono::microseconds>(stop_usage3 - start_usage).count() / 1000.
349 <<
" ms" << std::endl;
354 <<
" circular dependencies between includes of the repository files:"
357 if (circular_dependency_between_includes_is_error ==
true)
362 std::ostringstream text;
363 text <<
"ALL INCLUDES:\n";
365 for (
const auto& x : file_all_includes) {
366 text <<
"FILE \"" << x.first <<
"\" has " << x.second.size() <<
" includes:\n";
368 for (
const auto& y : x.second)
369 text <<
" - \"" << y <<
"\"\n";
378 auto ignore_files = [](std::string& x) {
379 static const std::string readme_file(
"README.md");
380 return x != readme_file;
384 std::set<std::string> modified;
385 std::copy_if(created.begin(), created.end(), std::inserter(modified, modified.end()), ignore_files);
386 std::copy_if(updated.begin(), updated.end(), std::inserter(modified, modified.end()), ignore_files);
389 for (
const auto& x : modified)
392 std::copy_if(deleted.begin(), deleted.end(), std::inserter(modified, modified.end()), ignore_files);
394 for (
const auto& f : file_explicit_includes)
395 if (all_includes.find(f.first) == all_includes.end()) {
396 if (modified.empty() ==
false) {
397 if (modified.find(f.first) == modified.end()) {
398 const auto& file_includes = file_all_includes[f.first];
402 for (
const auto& x : modified)
403 if (file_includes.find(x) != file_includes.end()) {
405 TLOG_DEBUG(1) <<
"file \"" << f.first <<
"\" contains modified include \"" << x <<
'\"';
409 if (found ==
false) {
410 TLOG_DEBUG(1) <<
"skip file \"" << f.first <<
'\"';
414 TLOG_DEBUG(1) <<
"list of modified files contains file \"" << f.first <<
'\"';
418 if (modified.find(f.first) == modified.end())
431 }
catch (std::exception& e) {