1- from os .path import dirname , abspath , join
1+ #!/usr/bin/env python3
2+ # # -*- coding: utf-8 -*-
3+
4+ import argparse
25from glob import glob
6+ import json
7+ import pathlib
8+ import os
9+ from os .path import dirname , abspath , join
310import re
11+ import signal
412import sys
513import pycurl
614
15+ # failed projects, global to print on early abort with Ctrl+C
16+ failed_projects : dict [str , list [str ]] = dict ()
17+
18+
19+ def print_failed_projects ():
20+ if failed_projects :
21+ print ("failed projects:" )
22+ print (json .dumps (failed_projects , indent = 2 ))
23+ else :
24+ print ("all clear! No project with failing URL found" )
25+
26+
27+ def signal_handler (signal , frame ):
28+ # force exit as pycurl.error after KeyboardInterrupt is caught
29+ # and then the next url is checked
30+ print ("You pressed Ctrl+C!" )
31+ print_failed_projects ()
32+ sys .exit (1 )
33+
34+
735def getResponseStausCode (url ):
836 try :
937 c = pycurl .Curl ()
@@ -18,36 +46,91 @@ def getResponseStausCode(url):
1846 except pycurl .error :
1947 return 999
2048
21- hunterDir = dirname (dirname (abspath (__file__ )))
22- projectsDir = join (hunterDir , 'cmake' , 'projects' )
2349
24- project = ''
25- if len (sys .argv ) > 1 :
26- project = sys .argv [1 ]
50+ def main ():
51+ parser = argparse .ArgumentParser ()
52+ parser .add_argument (
53+ "projects" ,
54+ help = "project names to process, used for local debugging" ,
55+ nargs = "*" ,
56+ )
57+ parser .add_argument (
58+ "-o" ,
59+ "--output" ,
60+ help = "specify file to write failed projects and URLs to, default write to stdout" ,
61+ type = str ,
62+ default = "" ,
63+ )
64+ args = parser .parse_args ()
2765
28- projectsFiles = join (projectsDir , project , '**' , '*.cmake' )
66+ repo_root = pathlib .Path (__file__ ).parent .parent
67+ projects_dir = repo_root / "cmake" / "projects"
2968
30- checkedFile = join (hunterDir , 'maintenance' , 'checked.txt' )
31- try :
32- checkedStream = open (checkedFile , "r+" )
33- checked = checkedStream .readlines ()
34- except FileNotFoundError :
35- checkedStream = open (checkedFile , "w" )
36- checked = []
69+ projects = set ()
70+ if args .projects :
71+ if "all" in args .projects :
72+ print ("project 'all' specified, checking all projects" )
73+ project_hunter_files = projects_dir / "*" / "hunter.cmake"
74+ for hunter_file in glob (project_hunter_files .as_posix (), recursive = False ):
75+ project = pathlib .Path (hunter_file ).parent .name
76+ projects .add (project )
77+ else :
78+ for project in args .projects :
79+ if (projects_dir / project ).is_dir ():
80+ projects .add (project )
81+ else :
82+ raise RuntimeError (
83+ f"provided project doesn't exist: { project } : expected dir: { projects_dir / project } "
84+ )
85+ else :
86+ try :
87+ with open (os .environ .get ("HOME" ) + "/files.json" ) as json_files :
88+ files = json .load (json_files )
89+ except IOError :
90+ raise RuntimeError ("Can't read changed files from files.json" )
3791
38- projects = dict ()
92+ p = re .compile ("cmake/projects/([^/]+)" )
93+ for file in files :
94+ if p .match (file ):
95+ project = p .match (file ).group (1 )
96+ if (projects_dir / project ).is_dir ():
97+ projects .add (project )
3998
40- for projectFile in glob ( projectsFiles , recursive = True ):
41- with open ( projectFile , "r" ) as file :
42- content = file . read ( )
99+ # override signal handler to make it possible to hard exit at Ctrl+C
100+ # and print a status if we've found a failing URL or not yet
101+ signal . signal ( signal . SIGINT , signal_handler )
43102
44- entries = re .findall (r'hunter_add_version\s*\(\s*PACKAGE_NAME\s+"*(.*?)"*\s+VERSION\s+"*(.*?)"*\s+URL\s+"*(.*?)"*\s+SHA1\s+"*(.*?)"*\s+.*?\)' , content , re .MULTILINE | re .DOTALL )
45- if len (entries ):
103+ for project in sorted (projects ):
104+ print ()
105+ print (f"checking project: { project } " )
106+ hunter_file = projects_dir / project / "hunter.cmake"
107+ if not hunter_file .is_file ():
108+ raise RuntimeError (f"hunter.cmake file not found: { hunter_file } " )
109+ with open (hunter_file , "r" , encoding = "utf-8" ) as file :
110+ content = file .read ()
111+
112+ entries = re .findall (
113+ r'hunter_add_version\s*\(\s*PACKAGE_NAME\s+"*(.*?)"*\s+VERSION\s+"*(.*?)"*\s+URL\s+"*(.*?)"*\s+SHA1\s+"*(.*?)"*\s+.*?\)' ,
114+ content ,
115+ re .MULTILINE | re .DOTALL ,
116+ )
117+ if len (entries ) == 0 :
118+ raise RuntimeError (
119+ f"no URLs found for project '{ project } ' in file: { hunter_file } "
120+ )
46121 for name , version , url , _ in entries :
47- if not any (url == x .rstrip ('\n ' ) for x in checked ):
48- statusCode = getResponseStausCode (url )
49- print (str (statusCode ) + ' ' + url )
50- if statusCode > 200 :
51- checkedStream .write (str (statusCode ) + ' ' + url + '\n ' )
52-
53- checkedStream .close ()
122+ status_code = getResponseStausCode (url )
123+ print (f"{ status_code } { url } " )
124+ if status_code > 200 :
125+ if project not in failed_projects :
126+ failed_projects [project ] = []
127+ failed_projects [project ].append (f"{ status_code } { url } " )
128+ print_failed_projects ()
129+ if len (failed_projects ) > 0 and args .output :
130+ with open (args .output , "w" , encoding = "utf-8" ) as file :
131+ json .dump (failed_projects , file , indent = 2 )
132+ return 0 if len (failed_projects ) == 0 else 1
133+
134+
135+ if __name__ == "__main__" :
136+ sys .exit (main ())
0 commit comments