1- # /// script
2- # dependencies = ["requests"]
3- # ///
4-
51"""
6- Command line executable allowing to update NinjaUrls.cmake , documentation
2+ Command line executable allowing to update upstream sources , documentation
73and tests given a Ninja version.
84"""
95from __future__ import annotations
106
117import argparse
128import contextlib
13- import hashlib
149import os
1510import re
16- import tempfile
11+ import shutil
12+ import subprocess
1713import textwrap
14+ from pathlib import Path
1815
19- from requests import request
20-
21- ROOT_DIR = os .path .join (os .path .dirname (__file__ ), ".." )
22-
23- REQ_BUFFER_SIZE = 65536 # Chunk size when iterating a download body
16+ ROOT_DIR = Path (__file__ ).parent .parent .resolve (strict = True )
2417
2518
2619@contextlib .contextmanager
@@ -31,133 +24,57 @@ def _log(txt, verbose=True):
3124 if verbose :
3225 print (f"{ txt } - done" )
3326
34-
35- def _download_file (download_url , filename ):
36- response = request (
37- method = 'GET' ,
38- url = download_url ,
39- allow_redirects = False ,
40- headers = {'Accept' : 'application/octet-stream' },
41- stream = True )
42- while response .status_code == 302 :
43- response = request (
44- 'GET' , response .headers ['Location' ], allow_redirects = False ,
45- stream = True
46- )
47- with open (filename , 'w+b' ) as f :
48- for chunk in response .iter_content (chunk_size = REQ_BUFFER_SIZE ):
49- f .write (chunk )
50-
51- return filename
52-
53-
54- def _hash_sum (filepath , algorithm = "sha256" , block_size = 2 ** 20 ):
55- hasher = hashlib .new (algorithm )
56- with open (filepath , mode = "rb" ) as fd :
57- while True :
58- data = fd .read (block_size )
59- if not data :
60- break
61- hasher .update (data )
62-
63- return hasher .hexdigest ()
64-
65-
66- def _download_and_compute_sha256 (url , filename ):
67- filepath = os .path .join (tempfile .gettempdir (), filename )
68- with _log (f"Downloading { url } " ):
69- _download_file (url , filepath )
70- sha256 = _hash_sum (filepath , algorithm = "sha256" )
71- return url , sha256
72-
73-
74- def get_ninja_archive_urls_and_sha256s (upstream_repository , version , verbose = False ):
75- tag_name = f"v{ version } "
76- files_base_url = f"https://github.com/{ upstream_repository } /archive/{ tag_name } "
77-
78- with _log (f"Collecting URLs and SHA256s from '{ files_base_url } '" ):
79-
80- # Get SHA256s and URLs
81- urls = {
82- "unix_source" : _download_and_compute_sha256 (files_base_url + ".tar.gz" , tag_name + ".tar.gz" ),
83- "win_source" : _download_and_compute_sha256 (files_base_url + ".zip" , tag_name + ".zip" ),
84- }
85-
86- if verbose :
87- for identifier , (url , sha256 ) in urls .items ():
88- print (f"[{ identifier } ]\n { url } \n { sha256 } \n " )
89-
90- return urls
91-
92-
93- def generate_cmake_variables (urls_and_sha256s ):
94- template_inputs = {}
95-
96- # Get SHA256s and URLs
97- for var_prefix , urls_and_sha256s_values in urls_and_sha256s .items ():
98- template_inputs [f"{ var_prefix } _url" ] = urls_and_sha256s_values [0 ]
99- template_inputs [f"{ var_prefix } _sha256" ] = urls_and_sha256s_values [1 ]
100-
101- return textwrap .dedent (
102- """
103- #-----------------------------------------------------------------------------
104- # Ninja sources
105- set(unix_source_url "{unix_source_url}")
106- set(unix_source_sha256 "{unix_source_sha256}")
107-
108- set(windows_source_url "{win_source_url}")
109- set(windows_source_sha256 "{win_source_sha256}")
110- """
111- ).format (** template_inputs )
112-
113-
114- def update_cmake_urls_script (upstream_repository , version ):
115- content = generate_cmake_variables (get_ninja_archive_urls_and_sha256s (upstream_repository , version ))
116- cmake_urls_filename = "NinjaUrls.cmake"
117- cmake_urls_filepath = os .path .join (ROOT_DIR , cmake_urls_filename )
118-
119- msg = f"Updating '{ cmake_urls_filename } ' with Ninja version { version } "
120- with _log (msg ), open (cmake_urls_filepath , "w" ) as cmake_file :
121- cmake_file .write (content )
122-
123-
124- def _update_file (filepath , regex , replacement , verbose = True ):
27+ @contextlib .contextmanager
28+ def chdir (path : Path ):
29+ origin = Path ().absolute ()
30+ os .chdir (path )
31+ try :
32+ yield
33+ finally :
34+ os .chdir (origin )
35+
36+
37+ def update_submodule (upstream_repository , version ):
38+ with chdir (ROOT_DIR ):
39+ subprocess .run (["git" , "submodule" , "deinit" , "-f" , "ninja-upstream" ], check = True )
40+ subprocess .run (["git" , "rm" , "-f" , "ninja-upstream" ], check = True )
41+ shutil .rmtree (ROOT_DIR / ".git/modules/ninja-upstream" )
42+ subprocess .run (["git" , "submodule" , "add" , f"https://github.com/{ upstream_repository } .git" , "ninja-upstream" ], check = True )
43+ subprocess .run (["git" , "submodule" , "update" , "--init" , "--recursive" , "ninja-upstream" ], check = True )
44+ with chdir (ROOT_DIR / "ninja-upstream" ):
45+ subprocess .run (["git" , "fetch" , "--tags" ], check = True )
46+ subprocess .run (["git" , "checkout" , f"v{ version } " ], check = True )
47+
48+
49+ def _update_file (filepath : Path , regex , replacement , verbose = True ):
12550 msg = f"Updating { os .path .relpath (filepath , ROOT_DIR )} "
12651 with _log (msg , verbose = verbose ):
12752 pattern = re .compile (regex )
128- with open (filepath ) as doc_file :
53+ with filepath . open () as doc_file :
12954 lines = doc_file .readlines ()
13055 updated_content = []
13156 for line in lines :
13257 updated_content .append (re .sub (pattern , replacement , line ))
133- with open (filepath , "w" ) as doc_file :
58+ with filepath . open ("w" ) as doc_file :
13459 doc_file .writelines (updated_content )
13560
13661
13762def update_docs (upstream_repository , version ):
13863 pattern = re .compile (r"ninja \d+.\d+.\d+(\.[\w\-]+)*" )
13964 replacement = f"ninja { version } "
140- _update_file (
141- os .path .join (ROOT_DIR , "README.rst" ),
142- pattern , replacement )
65+ _update_file (ROOT_DIR / "README.rst" , pattern , replacement )
14366
14467 pattern = re .compile (r"(?<=v)\d+.\d+.\d+(?:\.[\w\-]+)*(?=(?:\.zip|\.tar\.gz|\/))" )
14568 replacement = version
146- _update_file (
147- os .path .join (ROOT_DIR , "docs/update_ninja_version.rst" ),
148- pattern , replacement )
69+ _update_file (ROOT_DIR / "docs/update_ninja_version.rst" , pattern , replacement )
14970
15071 pattern = re .compile (r"(?<!v)\d+.\d+.\d+(?:\.[\w\-]+)*" )
15172 replacement = version
152- _update_file (
153- os .path .join (ROOT_DIR , "docs/update_ninja_version.rst" ),
154- pattern , replacement , verbose = False )
73+ _update_file (ROOT_DIR / "docs/update_ninja_version.rst" , pattern , replacement , verbose = False )
15574
15675 pattern = re .compile (r"github\.com\/[\w\-_]+\/[\w\-_]+(?=\/(?:release|archive))" )
15776 replacement = "github.com/" + upstream_repository
158- _update_file (
159- os .path .join (ROOT_DIR , "docs/update_ninja_version.rst" ),
160- pattern , replacement , verbose = False )
77+ _update_file (ROOT_DIR / "docs/update_ninja_version.rst" , pattern , replacement , verbose = False )
16178
16279
16380def update_tests (version ):
@@ -170,8 +87,7 @@ def update_tests(version):
17087
17188 pattern = re .compile (r'expected_version = "\d+.\d+.\d+(\.[\w\-]+)*"' )
17289 replacement = f'expected_version = "{ version } "'
173- _update_file (os .path .join (
174- ROOT_DIR , "tests/test_ninja.py" ), pattern , replacement )
90+ _update_file (ROOT_DIR / "tests/test_ninja.py" , pattern , replacement )
17591
17692
17793def main ():
@@ -189,34 +105,27 @@ def main():
189105 default = "Kitware/ninja" ,
190106 help = "Ninja upstream repository" ,
191107 )
192- parser .add_argument (
193- "--collect-only" ,
194- action = "store_true" ,
195- help = "If specified, only display the archive URLs and associated hashsums" ,
196- )
197108 parser .add_argument (
198109 "--quiet" ,
199110 action = "store_true" ,
200111 help = "Hide the output" ,
201112 )
202113 args = parser .parse_args ()
203- if args .collect_only :
204- get_ninja_archive_urls_and_sha256s (args .upstream_repository , args .ninja_version , verbose = True )
205- else :
206- update_cmake_urls_script (args .upstream_repository , args .ninja_version )
207- update_docs (args .upstream_repository , args .ninja_version )
208- update_tests (args .ninja_version )
209-
210- if not args .quiet :
211- msg = """\
212- Complete! Now run:
213-
214- git switch -c update-to-ninja-{release}
215- git add -u NinjaUrls.cmake docs/index.rst README.rst tests/test_ninja.py docs/update_ninja_version.rst
216- git commit -m "Update to Ninja {release}"
217- gh pr create --fill --body "Created by update_ninja_version.py"
218- """
219- print (textwrap .dedent (msg .format (release = args .ninja_version )))
114+
115+ update_submodule (args .upstream_repository , args .ninja_version )
116+ update_docs (args .upstream_repository , args .ninja_version )
117+ update_tests (args .ninja_version )
118+
119+ if not args .quiet :
120+ msg = """\
121+ Complete! Now run:
122+
123+ git switch -c update-to-ninja-{release}
124+ git add -u ninja-upstream docs/index.rst README.rst tests/test_ninja.py docs/update_ninja_version.rst
125+ git commit -m "Update to Ninja {release}"
126+ gh pr create --fill --body "Created by update_ninja_version.py"
127+ """
128+ print (textwrap .dedent (msg .format (release = args .ninja_version )))
220129
221130
222131if __name__ == "__main__" :
0 commit comments