Skip to content

Commit 9980d63

Browse files
windows + mac download (charlesnicholson#300)
* windows + mac download * fix download links, add type annotations, format * unzip * typo
1 parent 737c3eb commit 9980d63

2 files changed

Lines changed: 149 additions & 99 deletions

File tree

.github/workflows/presubmit.yml

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
- name: Pylint build.py
2626
run: . /work/venv/bin/activate && python -m pylint build.py tests/size_report.py
2727

28-
download:
28+
download-linux:
2929
runs-on: ubuntu-latest
3030

3131
container:
@@ -41,6 +41,34 @@ jobs:
4141
- name: Build
4242
run: ./b --download --paland -v
4343

44+
download-mac:
45+
runs-on: macos-latest
46+
47+
steps:
48+
- uses: actions/checkout@v4
49+
with:
50+
submodules: recursive
51+
- name: Build
52+
run: ./b --download --paland -v
53+
54+
download-win:
55+
runs-on: windows-latest
56+
57+
steps:
58+
- uses: actions/checkout@v4
59+
with:
60+
submodules: recursive
61+
- name: Set up Python 3.x
62+
uses: actions/setup-python@v5
63+
with:
64+
python-version: '3.x'
65+
- name: Build
66+
shell: cmd
67+
run: |
68+
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
69+
python.exe build.py --download --paland -v
70+
71+
4472
sanitizers:
4573
runs-on: ubuntu-latest
4674

@@ -199,7 +227,7 @@ jobs:
199227
python3 tests/size_report.py -p host
200228
201229
all-checks-pass:
202-
needs: [pylint, download, sanitizers, linux-x64, linux-arm64, macos, win, size-reports]
230+
needs: [pylint, download-linux, download-mac, download-win, sanitizers, linux-x64, linux-arm64, macos, win, size-reports]
203231
runs-on: ubuntu-latest
204232
steps:
205233
- run: echo Done

build.py

Lines changed: 119 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -11,175 +11,197 @@
1111
import urllib.request
1212
import zipfile
1313

14-
SCRIPT_PATH = pathlib.Path(__file__).resolve().parent
14+
_SCRIPT_PATH = pathlib.Path(__file__).resolve().parent
15+
_NINJA_URL = "https://github.com/ninja-build/ninja/releases/download/v1.12.1/{}"
16+
_CMAKE_VERSION = "4.0.1"
17+
_CMAKE_URL = (
18+
"https://github.com/Kitware/CMake/releases/download/"
19+
f"v{_CMAKE_VERSION}/cmake-{_CMAKE_VERSION}" + "-{}.{}"
20+
)
1521

16-
NINJA_URL = 'https://github.com/ninja-build/ninja/releases/download/v1.10.2/{}'
17-
CMAKE_URL = 'https://cmake.org/files/v3.22/{}'
1822

19-
20-
def parse_args():
23+
def _parse_args() -> argparse.Namespace:
2124
"""Parse command-line arguments."""
2225
parser = argparse.ArgumentParser()
2326
parser.add_argument(
24-
'--cfg',
25-
choices=[
26-
'Debug',
27-
'RelWithDebInfo',
28-
'Release'],
29-
default='Release',
30-
const='Release',
31-
nargs='?',
32-
help='CMake configuration')
27+
"--cfg",
28+
choices=["Debug", "RelWithDebInfo", "Release"],
29+
default="Release",
30+
const="Release",
31+
nargs="?",
32+
help="CMake configuration",
33+
)
3334
parser.add_argument(
34-
'--arch',
35+
"--arch",
3536
type=int,
3637
choices=(32, 64),
3738
default=64,
3839
const=64,
39-
nargs='?',
40-
help='Target architecture')
40+
nargs="?",
41+
help="Target architecture",
42+
)
4143
parser.add_argument(
42-
'--paland',
43-
help='Compile with Paland\'s printf conformance suite',
44-
action='store_true')
44+
"--paland",
45+
help="Compile with Paland's printf conformance suite",
46+
action="store_true",
47+
)
4548
parser.add_argument(
46-
'--download',
47-
help='Download CMake and Ninja, don\'t use local copies',
48-
action='store_true')
49-
parser.add_argument('--ubsan', action='store_true', help='Clang UB sanitizer')
50-
parser.add_argument('--asan', action='store_true', help='Clang addr sanitizer')
51-
parser.add_argument('-v', '--verbose', action='store_true', help='verbose')
49+
"--download",
50+
help="Download CMake and Ninja, don't use local copies",
51+
action="store_true",
52+
)
53+
parser.add_argument("--ubsan", action="store_true", help="Clang UB sanitizer")
54+
parser.add_argument("--asan", action="store_true", help="Clang addr sanitizer")
55+
parser.add_argument("-v", "--verbose", action="store_true", help="verbose")
5256
return parser.parse_args()
5357

5458

55-
def download_file(url, local_path, verbose):
59+
def download_file(url: str, local_path: pathlib.Path, verbose: bool) -> None:
5660
"""Download a file from url to local_path."""
5761
if verbose:
58-
print(f'Downloading:\n Remote: {url}\n Local: {local_path}')
59-
with urllib.request.urlopen(url) as rsp, open(local_path, 'wb') as file:
62+
print(f"Downloading:\n Remote: {url}\n Local: {local_path}")
63+
with urllib.request.urlopen(url) as rsp, open(local_path, "wb") as file:
6064
shutil.copyfileobj(rsp, file)
6165

6266

63-
def get_cmake(download, verbose):
67+
def _get_cmake(download: bool, verbose: bool) -> pathlib.Path:
6468
"""Return the path to system CMake, or download and unpack a local copy."""
6569
if not download:
66-
cmake = shutil.which('cmake')
70+
cmake = shutil.which("cmake")
6771
if cmake:
68-
return cmake
72+
return pathlib.Path(cmake)
6973

7074
plat = {
71-
'darwin': 'macos-universal',
72-
'linux': 'linux-x86_64',
73-
'win32': 'win64-x86_64'}[
74-
sys.platform]
75-
cmake_prefix = f'cmake-3.22.2-{plat}'
76-
cmake_local_dir = SCRIPT_PATH / 'external' / 'cmake'
77-
cmake_file = f'{cmake_prefix}.tar.gz'
78-
cmake_local_tgz = cmake_local_dir / cmake_file
79-
cmake_local_exe = cmake_local_dir / cmake_prefix / \
80-
('CMake.app/Contents' if sys.platform == 'darwin' else '') / 'bin' / 'cmake'
75+
"darwin": "macos-universal",
76+
"linux": "linux-x86_64",
77+
"win32": "windows-x86_64",
78+
}[sys.platform]
79+
80+
suffix = "zip" if sys.platform == "win32" else "tar.gz"
81+
82+
cmake_prefix = f"cmake-{_CMAKE_VERSION}-{plat}"
83+
cmake_local_dir = _SCRIPT_PATH / "external/cmake"
84+
cmake_file = f"{cmake_prefix}.{suffix}"
85+
cmake_local_archive = cmake_local_dir / cmake_file
86+
cmake_local_exe = (
87+
cmake_local_dir
88+
/ cmake_prefix
89+
/ ("CMake.app/Contents" if sys.platform == "darwin" else "")
90+
/ "bin/cmake"
91+
)
8192

8293
if not cmake_local_exe.exists():
83-
if not cmake_local_tgz.exists():
94+
if not cmake_local_archive.exists():
8495
cmake_local_dir.mkdir(parents=True, exist_ok=True)
85-
download_file(
86-
CMAKE_URL.format(cmake_file),
87-
cmake_local_tgz,
88-
verbose)
89-
with tarfile.open(cmake_local_tgz, 'r') as tar:
90-
for member in tar.getmembers():
91-
member_path = pathlib.Path(cmake_local_dir / member.name).resolve()
92-
if not cmake_local_dir in member_path.parents:
93-
raise ValueError('Tar file contents move upwards past sandbox root')
94-
tar.extractall(path=cmake_local_dir)
96+
download_file(_CMAKE_URL.format(plat, suffix), cmake_local_archive, verbose)
97+
98+
match suffix:
99+
case "tar.gz":
100+
with tarfile.open(cmake_local_archive, "r") as tar:
101+
for member in tar.getmembers():
102+
member_path = pathlib.Path(
103+
cmake_local_dir / member.name
104+
).resolve()
105+
if cmake_local_dir not in member_path.parents:
106+
raise ValueError(
107+
"Tar file contents move upwards past sandbox root"
108+
)
109+
110+
tar.extractall(path=cmake_local_dir)
111+
112+
case "zip":
113+
with zipfile.ZipFile(cmake_local_archive, "r") as zip_file:
114+
zip_file.extractall(cmake_local_dir)
95115

96116
return cmake_local_exe
97117

98118

99-
def get_ninja(download, verbose):
119+
def _get_ninja(download: bool, verbose: bool) -> pathlib.Path:
100120
"""Return the path to system Ninja, or download and unpack a local copy."""
101121
if not download:
102-
ninja = shutil.which('ninja')
122+
ninja = shutil.which("ninja")
103123
if ninja:
104-
return ninja
124+
return pathlib.Path(ninja)
105125

106-
ninja_local_dir = SCRIPT_PATH / 'external' / 'ninja'
107-
plat = {'darwin': 'mac', 'linux': 'linux', 'win32': 'win'}[sys.platform]
108-
ninja_file = f'ninja-{plat}.zip'
126+
ninja_local_dir = _SCRIPT_PATH / "external/ninja"
127+
plat = {"darwin": "mac", "linux": "linux", "win32": "win"}[sys.platform]
128+
ninja_file = f"ninja-{plat}.zip"
109129
ninja_local_zip = ninja_local_dir / ninja_file
110-
ninja_local_exe = ninja_local_dir / f'ninja{".exe" if plat == "win" else ""}'
130+
ninja_local_exe = ninja_local_dir / f"ninja{'.exe' if plat == 'win' else ''}"
111131

112132
if not ninja_local_exe.exists():
113133
if not ninja_local_zip.exists():
114134
ninja_local_dir.mkdir(parents=True, exist_ok=True)
115-
download_file(
116-
NINJA_URL.format(ninja_file),
117-
ninja_local_zip,
118-
verbose)
119-
with zipfile.ZipFile(ninja_local_zip, 'r') as zip_file:
135+
download_file(_NINJA_URL.format(ninja_file), ninja_local_zip, verbose)
136+
137+
with zipfile.ZipFile(ninja_local_zip, "r") as zip_file:
120138
zip_file.extractall(ninja_local_dir)
121-
os.chmod(ninja_local_exe, os.stat(
122-
ninja_local_exe).st_mode | stat.S_IEXEC)
139+
140+
os.chmod(ninja_local_exe, os.stat(ninja_local_exe).st_mode | stat.S_IEXEC)
123141

124142
return ninja_local_exe
125143

126144

127-
def configure_cmake(cmake_exe, ninja, args):
145+
def _configure_cmake(
146+
cmake_exe: pathlib.Path, ninja: pathlib.Path, args: argparse.Namespace
147+
) -> bool:
128148
"""Prepare CMake for building nanoprintf tests under 'build/ninja/<cfg>'."""
129-
build_path = SCRIPT_PATH / 'build' / 'ninja' / args.cfg
130-
if (build_path / 'CMakeFiles').exists():
149+
build_path = _SCRIPT_PATH / "build/ninja" / args.cfg
150+
if (build_path / "CMakeFiles").exists():
131151
return True
132152

133153
sys.stdout.flush()
134154
build_path.mkdir(parents=True)
135155

136-
cmake_args = [cmake_exe,
137-
SCRIPT_PATH,
138-
'-G',
139-
'Ninja',
140-
'-DCMAKE_EXPORT_COMPILE_COMMANDS=ON',
141-
f'-DCMAKE_MAKE_PROGRAM={ninja}',
142-
f'-DCMAKE_BUILD_TYPE={args.cfg}',
143-
f'-DNPF_PALAND={"ON" if args.paland else "OFF"}',
144-
f'-DNPF_32BIT={"ON" if args.arch == 32 else "OFF"}',
145-
f'-DNPF_CLANG_ASAN={"ON" if args.asan else "OFF"}',
146-
f'-DNPF_CLANG_UBSAN={"ON" if args.ubsan else "OFF"}']
156+
cmake_args = [
157+
cmake_exe,
158+
_SCRIPT_PATH,
159+
"-G",
160+
"Ninja",
161+
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
162+
f"-DCMAKE_MAKE_PROGRAM={ninja}",
163+
f"-DCMAKE_BUILD_TYPE={args.cfg}",
164+
f"-DNPF_PALAND={'ON' if args.paland else 'OFF'}",
165+
f"-DNPF_32BIT={'ON' if args.arch == 32 else 'OFF'}",
166+
f"-DNPF_CLANG_ASAN={'ON' if args.asan else 'OFF'}",
167+
f"-DNPF_CLANG_UBSAN={'ON' if args.ubsan else 'OFF'}",
168+
]
169+
147170
try:
148-
return subprocess.run(
149-
cmake_args,
150-
cwd=build_path,
151-
check=True).returncode == 0
171+
return subprocess.run(cmake_args, cwd=build_path, check=True).returncode == 0
152172
except subprocess.CalledProcessError as cpe:
153173
return cpe.returncode == 0
154174

155175

156-
def build_cmake(cmake_exe, args):
176+
def _build_cmake(cmake_exe: pathlib.Path, args: argparse.Namespace) -> bool:
157177
"""Run CMake in build mode to compile and run the nanoprintf test suite."""
158178
sys.stdout.flush()
159-
build_path = SCRIPT_PATH / 'build' / 'ninja' / args.cfg
160-
cmake_args = [cmake_exe, '--build', build_path] + \
161-
(['--', '-v'] if args.verbose else [])
179+
build_path = _SCRIPT_PATH / "build/ninja" / args.cfg
180+
cmake_args = [cmake_exe, "--build", build_path] + (
181+
["--", "-v"] if args.verbose else []
182+
)
183+
162184
try:
163185
return subprocess.run(cmake_args, check=True).returncode == 0
164186
except subprocess.CalledProcessError as cpe:
165187
return cpe.returncode == 0
166188

167189

168-
def main():
190+
def main() -> int:
169191
"""Parse args, find or get tools, configure CMake, build and run tests."""
170-
args = parse_args()
192+
args = _parse_args()
171193

172-
cmake = get_cmake(args.download, args.verbose)
194+
cmake = _get_cmake(args.download, args.verbose)
173195
if args.verbose:
174-
print(f'Found CMake at {cmake}')
196+
print(f"Found CMake at {cmake}")
175197

176-
ninja = get_ninja(args.download, args.verbose)
198+
ninja = _get_ninja(args.download, args.verbose)
177199
if args.verbose:
178-
print(f'Found ninja at {ninja}')
200+
print(f"Found ninja at {ninja}")
179201

180-
built_ok = configure_cmake(cmake, ninja, args) and build_cmake(cmake, args)
202+
built_ok = _configure_cmake(cmake, ninja, args) and _build_cmake(cmake, args)
181203
return int(not built_ok) # 0 is success
182204

183205

184-
if __name__ == '__main__':
206+
if __name__ == "__main__":
185207
sys.exit(main())

0 commit comments

Comments
 (0)