1- """Artifact build oracle for the Eurosys '25 EGWALKER artifact .
1+ """Artifact build oracle for EGWALKER (EuroSys '25) .
22
33Validates:
4- - Required repository working directories exist .
4+ - Repository working directory exists .
55 - Build commands execute successfully (captures stdout/stderr/return code).
66"""
77
1313from pathlib import Path
1414
1515from evaluator .oracle_artifact_build_primitives import (
16- BuildCommandRequirement ,
17- BuildRequirement ,
18- OracleArtifactBuildBase ,
16+ BuildCommandRequirement ,
17+ OracleArtifactBuildBase ,
1918)
20- from evaluator .utils import EntryConfig
19+ from evaluator .utils import EntryConfig , BaseRequirement
2120
2221
23- @dataclass (frozen = True , slots = True , kw_only = True )
22+ @dataclass (frozen = True , slots = True , kw_only = True )
2423class BuildTarget :
25- """Declarative description of one build command to run."""
24+ """Declarative description of one build command to run.
2625
27- name : str
28- command : Sequence [str ]
29- cwd_relative : Path | None = None
30- optional : bool = False
31- timeout_seconds : float = 60.0
32- env_overrides : Mapping [str , str ] = field (default_factory = dict )
26+ Kept intentionally thin: the base primitive (BuildCommandRequirement) performs
27+ the authoritative validation and normalization.
28+ """
3329
34- def __post_init__ (self ) -> None :
35- if not self .name :
36- raise ValueError ("BuildTarget.name must be non-empty" )
37- if not self .command :
38- raise ValueError (f"{ self .name } : command must be non-empty" )
39- if self .timeout_seconds <= 0 :
40- raise ValueError (f"{ self .name } : timeout_seconds must be > 0" )
30+ name : str
31+ cmd : Sequence [str ]
32+ relative_workdir : Path | None = None
33+ optional : bool = False
34+ timeout_seconds : float = 60.0
35+ env_overrides : Mapping [str , str ] = field (default_factory = dict )
4136
42- # Normalize for downstream requirements.
43- if self . cwd_relative is not None and not isinstance ( self .cwd_relative , Path ) :
44- object . __setattr__ ( self , "cwd_relative" , Path ( self . cwd_relative ) )
37+ def __post_init__ ( self ) -> None :
38+ if not self .name :
39+ raise ValueError ( "BuildTarget.name must be non-empty" )
4540
46- # Freeze command to avoid accidental mutation.
47- object .__setattr__ (self , "command" , tuple (self .command ))
41+ object .__setattr__ (self , "cmd" , tuple (self .cmd ))
42+
43+ if self .relative_workdir is not None and not isinstance (
44+ self .relative_workdir , Path
45+ ):
46+ object .__setattr__ (self , "relative_workdir" , Path (self .relative_workdir ))
4847
4948
5049class OracleArtifactBuild (OracleArtifactBuildBase ):
51- """The artifact build oracle for artifact-core.
52-
53- Defaults:
54- * Runs build commands in the repo keyed by config.name.
55- * EntryConfig.repository_paths must contain an entry for config.name.
56- """
57-
58- _DEFAULT_TARGET_SPECS : tuple [tuple [str , tuple [str , ...], float ], ...] = (
59- (
60- "artifact-core: make tools" ,
61- (
62- "make" ,
63- "-j8" ,
64- "tools/diamond-types/target/release/dt" ,
65- "tools/crdt-converter/target/release/crdt-converter" ,
66- "tools/diamond-types/target/release/paper-stats" ,
67- "tools/paper-benchmarks/target/memusage/paper-benchmarks" ,
68- "tools/paper-benchmarks/target/release/paper-benchmarks" ,
69- "tools/ot-bench/target/memusage/ot-bench" ,
70- "tools/ot-bench/target/release/ot-bench" ,
71- ),
72- 60.0 ,
73- ),
74- )
75-
76- def __init__ (
77- self ,
78- * ,
79- config : EntryConfig ,
80- logger : logging .Logger ,
81- targets : Sequence [BuildTarget ] | None = None ,
82- ) -> None :
83- super ().__init__ (logger = logger )
84- self ._config = config
85-
86- if targets is None :
87- targets = self ._make_default_targets ()
88- self ._targets = tuple (targets )
89-
90- names = [t .name for t in self ._targets ]
91- if len (names ) != len (set (names )):
92- raise ValueError (f"Duplicate build target names: { names !r} " )
93-
94- def _make_default_targets (self ) -> tuple [BuildTarget , ...]:
95- """Creates default targets (stored in the EntryConfig object)."""
96- return tuple (
97- BuildTarget (name = name , command = command , timeout_seconds = timeout_seconds )
98- for (name , command , timeout_seconds ) in self ._DEFAULT_TARGET_SPECS
50+ """The artifact build oracle for artifact-core.
51+
52+ Defaults:
53+ * Runs build commands in the repo keyed by config.name.
54+ * EntryConfig.repository_paths is expected to contain an entry for config.name.
55+ """
56+
57+ _DEFAULT_TARGET_SPECS : tuple [tuple [str , tuple [str , ...], float ], ...] = (
58+ (
59+ "artifact-core: make tools" ,
60+ (
61+ "make" ,
62+ "-j8" ,
63+ "tools/diamond-types/target/release/dt" ,
64+ "tools/crdt-converter/target/release/crdt-converter" ,
65+ "tools/diamond-types/target/release/paper-stats" ,
66+ "tools/paper-benchmarks/target/memusage/paper-benchmarks" ,
67+ "tools/paper-benchmarks/target/release/paper-benchmarks" ,
68+ "tools/ot-bench/target/memusage/ot-bench" ,
69+ "tools/ot-bench/target/release/ot-bench" ,
70+ ),
71+ 300.0 ,
72+ ),
9973 )
10074
101- def requirements (self ) -> Sequence [BuildRequirement ]:
102- """Returns an ordered list of build requirements to validate."""
103- return tuple (
104- BuildCommandRequirement (
105- name = target .name ,
106- optional = target .optional ,
107- cwd = self ._config .repository_paths [self ._config .name ],
108- command = target .command ,
109- cwd_relative = target .cwd_relative ,
110- timeout_seconds = target .timeout_seconds ,
111- env_overrides = target .env_overrides ,
112- )
113- for target in self ._targets
114- )
75+ def __init__ (
76+ self ,
77+ * ,
78+ config : EntryConfig ,
79+ logger : logging .Logger ,
80+ targets : Sequence [BuildTarget ] | None = None ,
81+ ) -> None :
82+ super ().__init__ (logger = logger )
83+ self ._config = config
84+
85+ if targets is None :
86+ targets = self ._make_default_targets ()
87+ self ._targets = tuple (targets )
88+
89+ names = [t .name for t in self ._targets ]
90+ if len (names ) != len (set (names )):
91+ raise ValueError (f"Duplicate build target names: { names !r} " )
92+
93+ def _make_default_targets (self ) -> tuple [BuildTarget , ...]:
94+ return tuple (
95+ BuildTarget (name = name , cmd = cmd , timeout_seconds = timeout_seconds )
96+ for (name , cmd , timeout_seconds ) in self ._DEFAULT_TARGET_SPECS
97+ )
98+
99+ def requirements (self ) -> Sequence [BaseRequirement ]:
100+ """Returns an ordered list of build requirements to validate."""
101+ repo_root = self ._config .repository_paths .get (self ._config .name )
102+
103+ if repo_root is None :
104+ return (
105+ BuildCommandRequirement (
106+ name = f"config: missing repository_paths entry for { self ._config .name !r} " ,
107+ optional = False ,
108+ cwd = Path (self ._config .home_dir ) / "__MISSING_REPOSITORY_PATH__" ,
109+ cmd = ("true" ,),
110+ timeout_seconds = 1.0 ,
111+ ),
112+ )
113+
114+ return tuple (
115+ BuildCommandRequirement (
116+ name = target .name ,
117+ optional = target .optional ,
118+ cwd = repo_root ,
119+ cmd = target .cmd ,
120+ relative_workdir = target .relative_workdir ,
121+ timeout_seconds = target .timeout_seconds ,
122+ env_overrides = target .env_overrides ,
123+ )
124+ for target in self ._targets
125+ )
0 commit comments