1414 TOOL_PATH += ".exe"
1515TOOL_PATH = os .path .abspath (TOOL_PATH )
1616
17+ SHARED_BODY_DIR = "src/ebmcodegen/generated"
18+
19+
20+ def write_if_changed (path : str , content : bytes ) -> bool :
21+ """Write content to path only when it differs; returns True when written.
22+
23+ Keeping mtimes stable matters here: ninja rebuilds every ebm2<lang> target
24+ whose (wrapper) main.cpp or the shared body changed.
25+ """
26+ if os .path .exists (path ):
27+ with open (path , "rb" ) as f :
28+ if f .read () == content :
29+ return False
30+ with open (path , "wb" ) as f :
31+ f .write (content )
32+ return True
33+
34+
35+ _shared_bodies_done : set = set ()
36+
37+
38+ def ensure_shared_bodies (mode : str ):
39+ """Generate the language-agnostic shared bodies included by per-language wrappers."""
40+ if mode in _shared_bodies_done :
41+ return
42+ _shared_bodies_done .add (mode )
43+ isInterpreter = mode == "interpret" or mode == "interpret-class"
44+ family = "interpret" if isInterpreter else "codegen"
45+ DEFAULT_VISITOR_LOCATION = f"ebmcodegen/default_{ family } _visitor/visitor/"
46+ os .makedirs (SHARED_BODY_DIR , exist_ok = True )
47+ for kind in ("header" , "source" ):
48+ content = execute (
49+ [
50+ TOOL_PATH ,
51+ "--mode" ,
52+ f"{ mode } -{ kind } -body" ,
53+ "--default-visitor-impl-dir" ,
54+ DEFAULT_VISITOR_LOCATION ,
55+ ],
56+ None ,
57+ )
58+ path = os .path .join (SHARED_BODY_DIR , f"class_{ family } _{ kind } .inc" )
59+ if write_if_changed (path , content ):
60+ print (f"Generated shared body: { path } " )
61+ else :
62+ print (f"Shared body is up to date: { path } " )
63+
1764
1865def do_default_dummy_header (lang_name : str , mode : str ):
1966 isInterpreter = mode == "interpret" or mode == "interpret-class"
@@ -74,30 +121,26 @@ def do_setup(lang_name: str, mode: str, file_extension: str):
74121
75122 CMAKE = execute ([TOOL_PATH , "--mode" , "cmake" , "--lang" , lang_name ], None )
76123
77- DEFAULT_VISITOR_LOCATION = f"ebmcodegen/default_{ "interpret" if isInterpreter else "codegen" } _visitor/visitor/"
78-
79124 if isClassBased :
125+ # per-language files are thin wrappers over the shared bodies in src/ebmcodegen/generated/
126+ ensure_shared_bodies (mode )
80127 CODE_GENERATOR_HEADER = execute (
81128 [
82129 TOOL_PATH ,
83130 "--mode" ,
84- mode + "-header" ,
131+ mode + "-header-wrapper " ,
85132 "--lang" ,
86133 lang_name ,
87- "--default-visitor-impl-dir" ,
88- DEFAULT_VISITOR_LOCATION ,
89134 ],
90135 None ,
91136 )
92137 CODE_GENERATOR = execute (
93138 [
94139 TOOL_PATH ,
95140 "--mode" ,
96- mode + "-source" ,
141+ mode + "-source-wrapper " ,
97142 "--lang" ,
98143 lang_name ,
99- "--default-visitor-impl-dir" ,
100- DEFAULT_VISITOR_LOCATION ,
101144 ],
102145 None ,
103146 )
@@ -109,13 +152,10 @@ def do_setup(lang_name: str, mode: str, file_extension: str):
109152 VISITOR_DIR = os .path .join (OUTPUT_DIR , "visitor" )
110153 os .makedirs (VISITOR_DIR , exist_ok = True )
111154
112- with open (os .path .join (OUTPUT_DIR , "CMakeLists.txt" ), "wb" ) as f :
113- f .write (CMAKE )
114- with open (os .path .join (OUTPUT_DIR , "main.cpp" ), "wb" ) as f :
115- f .write (CODE_GENERATOR )
155+ write_if_changed (os .path .join (OUTPUT_DIR , "CMakeLists.txt" ), CMAKE )
156+ write_if_changed (os .path .join (OUTPUT_DIR , "main.cpp" ), CODE_GENERATOR )
116157 if CODE_GENERATOR_HEADER is not None :
117- with open (os .path .join (OUTPUT_DIR , "codegen.hpp" ), "wb" ) as f :
118- f .write (CODE_GENERATOR_HEADER )
158+ write_if_changed (os .path .join (OUTPUT_DIR , "codegen.hpp" ), CODE_GENERATOR_HEADER )
119159
120160 # add FILE_EXTENSIONS(ext) to Flags.hpp using script/ebmtemplate.py
121161 flags_path = os .path .join (VISITOR_DIR , "Flags.hpp" )
0 commit comments