33"""
44
55import os
6+ from copy import copy
67from pathlib import Path
78from typing import Any , Dict , List , Optional , Union
89
910from cmdstanpy .utils import get_logger
1011
1112STANC_OPTS = [
1213 'O' ,
13- 'allow_undefined' ,
14+ 'O0' ,
15+ 'O1' ,
16+ 'Oexperimental' ,
17+ 'allow-undefined' ,
1418 'use-opencl' ,
1519 'warn-uninitialized' ,
16- 'include_paths ' ,
20+ 'include-paths ' ,
1721 'name' ,
1822 'warn-pedantic' ,
1923]
2024
25+ STANC_DEPRECATED_OPTS = {
26+ 'allow_undefined' : 'allow-undefined' ,
27+ 'include_paths' : 'include-paths' ,
28+ }
29+
2130STANC_IGNORE_OPTS = [
2231 'debug-lex' ,
2332 'debug-parse' ,
@@ -121,41 +130,69 @@ def validate_stanc_opts(self) -> None:
121130 return
122131 ignore = []
123132 paths = None
133+ has_o_flag = False
134+
135+ for deprecated , replacement in STANC_DEPRECATED_OPTS .items ():
136+ if deprecated in self ._stanc_options :
137+ if replacement :
138+ get_logger ().warning (
139+ 'compiler option "%s" is deprecated, use "%s" instead' ,
140+ deprecated ,
141+ replacement ,
142+ )
143+ self ._stanc_options [replacement ] = copy (
144+ self ._stanc_options [deprecated ]
145+ )
146+ del self ._stanc_options [deprecated ]
147+ else :
148+ get_logger ().warning (
149+ 'compiler option "%s" is deprecated and '
150+ 'should not be used' ,
151+ deprecated ,
152+ )
124153 for key , val in self ._stanc_options .items ():
125154 if key in STANC_IGNORE_OPTS :
126155 get_logger ().info ('ignoring compiler option: %s' , key )
127156 ignore .append (key )
128157 elif key not in STANC_OPTS :
129158 raise ValueError (f'unknown stanc compiler option: { key } ' )
130- elif key == 'include_paths ' :
159+ elif key == 'include-paths ' :
131160 paths = val
132161 if isinstance (val , str ):
133162 paths = val .split (',' )
134163 elif not isinstance (val , list ):
135164 raise ValueError (
136- 'Invalid include_paths , expecting list or '
165+ 'Invalid include-paths , expecting list or '
137166 f'string, found type: { type (val )} .'
138167 )
139168 elif key == 'use-opencl' :
140169 if self ._cpp_options is None :
141170 self ._cpp_options = {'STAN_OPENCL' : 'TRUE' }
142171 else :
143172 self ._cpp_options ['STAN_OPENCL' ] = 'TRUE'
173+ elif key .startswith ('O' ):
174+ if has_o_flag :
175+ get_logger ().warning (
176+ 'More than one of (O, O1, O2, Oexperimental)'
177+ 'optimizations passed. Only the last one will'
178+ 'be used'
179+ )
180+ else :
181+ has_o_flag = True
144182
145183 for opt in ignore :
146184 del self ._stanc_options [opt ]
147185 if paths is not None :
148- self ._stanc_options ['include_paths' ] = paths
149- bad_paths = [
150- dir
151- for dir in self ._stanc_options ['include_paths' ]
152- if not os .path .exists (dir )
153- ]
186+ bad_paths = [dir for dir in paths if not os .path .exists (dir )]
154187 if any (bad_paths ):
155188 raise ValueError (
156189 'invalid include paths: {}' .format (', ' .join (bad_paths ))
157190 )
158191
192+ self ._stanc_options ['include-paths' ] = [
193+ os .path .abspath (os .path .expanduser (path )) for path in paths
194+ ]
195+
159196 def validate_cpp_opts (self ) -> None :
160197 """
161198 Check cpp compiler args.
@@ -190,8 +227,8 @@ def validate_user_header(self) -> None:
190227 raise ValueError (
191228 f"Header file must end in .hpp, got { self ._user_header } "
192229 )
193- if "allow_undefined " not in self ._stanc_options :
194- self ._stanc_options ["allow_undefined " ] = True
230+ if "allow-undefined " not in self ._stanc_options :
231+ self ._stanc_options ["allow-undefined " ] = True
195232 # set full path
196233 self ._user_header = os .path .abspath (self ._user_header )
197234
@@ -218,7 +255,7 @@ def add(self, new_opts: "CompilerOptions") -> None: # noqa: disable=Q000
218255 self ._stanc_options = new_opts .stanc_options
219256 else :
220257 for key , val in new_opts .stanc_options .items ():
221- if key == 'include_paths ' :
258+ if key == 'include-paths ' :
222259 self .add_include_path (str (val ))
223260 else :
224261 self ._stanc_options [key ] = val
@@ -230,30 +267,35 @@ def add(self, new_opts: "CompilerOptions") -> None: # noqa: disable=Q000
230267
231268 def add_include_path (self , path : str ) -> None :
232269 """Adds include path to existing set of compiler options."""
233- if 'include_paths' not in self ._stanc_options :
234- self ._stanc_options ['include_paths' ] = [path ]
235- elif path not in self ._stanc_options ['include_paths' ]:
236- self ._stanc_options ['include_paths' ].append (path )
270+ path = os .path .abspath (os .path .expanduser (path ))
271+ if 'include-paths' not in self ._stanc_options :
272+ self ._stanc_options ['include-paths' ] = [path ]
273+ elif path not in self ._stanc_options ['include-paths' ]:
274+ self ._stanc_options ['include-paths' ].append (path )
237275
238- def compose (self ) -> List [str ]:
239- """Format makefile options as list of strings."""
276+ def compose_stanc (self ) -> List [str ]:
240277 opts = []
241278 if self ._stanc_options is not None and len (self ._stanc_options ) > 0 :
242279 for key , val in self ._stanc_options .items ():
243- if key == 'include_paths ' :
280+ if key == 'include-paths ' :
244281 opts .append (
245- 'STANCFLAGS+=--include_paths ='
282+ '--include-paths ='
246283 + ',' .join (
247284 (
248285 Path (p ).as_posix ()
249- for p in self ._stanc_options ['include_paths ' ]
286+ for p in self ._stanc_options ['include-paths ' ]
250287 )
251288 )
252289 )
253290 elif key == 'name' :
254- opts .append (f'STANCFLAGS+= --name={ val } ' )
291+ opts .append (f'--name={ val } ' )
255292 else :
256- opts .append (f'STANCFLAGS+=--{ key } ' )
293+ opts .append (f'--{ key } ' )
294+ return opts
295+
296+ def compose (self ) -> List [str ]:
297+ """Format makefile options as list of strings."""
298+ opts = ['STANCFLAGS+=' + flag for flag in self .compose_stanc ()]
257299 if self ._cpp_options is not None and len (self ._cpp_options ) > 0 :
258300 for key , val in self ._cpp_options .items ():
259301 opts .append (f'{ key } ={ val } ' )
0 commit comments