10
10
from dataclasses import dataclass
11
11
from os import getenv
12
12
from pathlib import Path
13
+ from typing import TypedDict
13
14
14
15
15
16
REPO_ROOT = Path (__file__ ).parent .parent
16
17
GIT = ["git" , "-C" , REPO_ROOT ]
17
18
19
+ # Don't run exhaustive tests if these files change, even if they contaiin a function
20
+ # definition.
18
21
IGNORE_FILES = [
19
22
"src/math/support/" ,
20
23
"src/libm_helper.rs" ,
21
24
"src/math/arch/intrinsics.rs" ,
22
25
]
23
- """Don't run exhaustive tests if these files change, even if they contaiin a function
24
- definition."""
26
+
27
+ TYPES = ["f16" , "f32" , "f64" , "f128" ]
28
+
29
+
30
+ class FunctionDef (TypedDict ):
31
+ """Type for an entry in `function-definitions.json`"""
32
+
33
+ sources : list [str ]
34
+ type : str
25
35
26
36
27
37
@dataclass
28
38
class Context :
29
39
gh_ref : str | None
30
40
changed : list [Path ]
31
- defs : dict [str , dict [str , list [str ] | str ]]
32
- """A list of all available functions by name."""
41
+ defs : dict [str , FunctionDef ]
33
42
34
43
def __init__ (self ) -> None :
35
44
self .gh_ref = getenv ("GITHUB_REF" )
@@ -44,85 +53,71 @@ def __init__(self) -> None:
44
53
45
54
def _init_change_list (self ):
46
55
"""Create a list of files that have been changed. This uses GITHUB_REF if
47
- available, otherwise a diff between `HEAD` and `master`."""
56
+ available, otherwise a diff between `HEAD` and `master`.
57
+ """
48
58
49
59
# For pull requests, GitHub creates a ref `refs/pull/1234/merge` (1234 being
50
60
# the PR number), and sets this as `GITHUB_REF`.
51
61
ref = self .gh_ref
52
- if ref is not None :
53
- eprint (f"using ref `{ ref } `" )
54
- if "merge" not in ref :
55
- # If the ref is not for `merge` then we are not in PR CI
56
- eprint ("No diff available for ref" )
57
- return
58
-
59
- # The ref is for a dummy merge commit. We can extract the merge base by
60
- # inspecting all parents (`^@`).
61
- merge_sha = sp .check_output (
62
- GIT + ["show-ref" , "--hash" , ref ], text = True
63
- ).strip ()
64
- merge_log = sp .check_output (GIT + ["log" , "-1" , merge_sha ], text = True )
65
- eprint (f"Merge:\n { merge_log } \n " )
66
-
67
- parents = (
68
- sp .check_output (GIT + ["rev-parse" , f"{ merge_sha } ^@" ], text = True )
69
- .strip ()
70
- .splitlines ()
71
- )
72
- assert len (parents ) == 2 , f"expected two-parent merge but got:\n { parents } "
73
- base = parents [0 ]
74
- incoming = parents [1 ]
75
- else :
76
- # When running locally, allow providing a rev via `MERGE_BASE`. Otherwise
77
- # assume `HEAD -> MASTER`
78
- base = getenv ("MERGE_BASE" )
79
- if base is None :
80
- base = sp .check_output (
81
- GIT + ["merge-base" , "HEAD" , "master" ], text = True
82
- )
83
- incoming = "HEAD"
84
-
85
- base = base .strip ()
86
- incoming = incoming .strip ()
62
+ eprint (f"using ref `{ ref } `" )
63
+ if ref is None or "merge" not in ref :
64
+ # If the ref is not for `merge` then we are not in PR CI
65
+ eprint ("No diff available for ref" )
66
+ return
67
+
68
+ # The ref is for a dummy merge commit. We can extract the merge base by
69
+ # inspecting all parents (`^@`).
70
+ merge_sha = sp .check_output (
71
+ GIT + ["show-ref" , "--hash" , ref ], text = True
72
+ ).strip ()
73
+ merge_log = sp .check_output (GIT + ["log" , "-1" , merge_sha ], text = True )
74
+ eprint (f"Merge:\n { merge_log } \n " )
75
+
76
+ parents = (
77
+ sp .check_output (GIT + ["rev-parse" , f"{ merge_sha } ^@" ], text = True )
78
+ .strip ()
79
+ .splitlines ()
80
+ )
81
+ assert len (parents ) == 2 , f"expected two-parent merge but got:\n { parents } "
82
+ base = parents [0 ].strip ()
83
+ incoming = parents [1 ].strip ()
87
84
88
85
eprint (f"base: { base } , incoming: { incoming } " )
89
86
textlist = sp .check_output (
90
87
GIT + ["diff" , base , incoming , "--name-only" ], text = True
91
88
)
92
89
self .changed = [Path (p ) for p in textlist .splitlines ()]
93
90
91
+ @staticmethod
92
+ def _ignore_file (fname : str ) -> bool :
93
+ return any (fname .startswith (pfx ) for pfx in IGNORE_FILES )
94
+
94
95
def changed_routines (self ) -> dict [str , list [str ]]:
95
96
"""Create a list of routines for which one or more files have been updated,
96
97
separated by type.
97
98
"""
98
99
routines = set ()
99
100
for name , meta in self .defs .items ():
100
- def_list = meta ["sources" ]
101
- add = False
102
- for fname in def_list :
103
- # Skip if the file was not changed
104
- if Path (fname ) not in self .changed :
105
- continue
106
-
107
- # Don't update if changes to the file should be ignored
108
- if any (fname .startswith (pfx ) for pfx in IGNORE_FILES ):
109
- continue
101
+ # Don't update if changes to the file should be ignored
102
+ sources = (f for f in meta ["sources" ] if not self ._ignore_file (f ))
110
103
111
- add = True
104
+ # Select changed files
105
+ changed = [f for f in sources if Path (f ) in self .changed ]
112
106
113
- if add :
107
+ if len (changed ) > 0 :
108
+ eprint (f"changed files for { name } : { changed } " )
114
109
routines .add (name )
115
110
116
- ret = {}
111
+ ret = {ty : [] for ty in TYPES }
117
112
for r in sorted (routines ):
118
- ret . setdefault ( self .defs [r ]["type" ], []) .append (r )
113
+ ret [ self .defs [r ]["type" ]] .append (r )
119
114
120
115
return ret
121
116
122
117
def make_workflow_output (self ) -> str :
123
118
changed = self .changed_routines ()
124
119
ret = []
125
- for ty in [ "f16" , "f32" , "f64" , "f128" ] :
120
+ for ty in TYPES :
126
121
ty_changed = changed .get (ty , [])
127
122
item = {
128
123
"ty" : ty ,
0 commit comments