certora-cli-beta-mirror 7.30.1__py3-none-any.whl → 8.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +11 -2
  2. certora_cli/CertoraProver/certoraBuild.py +68 -62
  3. certora_cli/CertoraProver/certoraBuildCacheManager.py +17 -16
  4. certora_cli/CertoraProver/certoraBuildRust.py +33 -21
  5. certora_cli/{Shared/rustProverCommon.py → CertoraProver/certoraBuildSui.py} +24 -18
  6. certora_cli/CertoraProver/certoraCloudIO.py +42 -33
  7. certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +62 -51
  8. certora_cli/CertoraProver/certoraCollectRunMetadata.py +20 -5
  9. certora_cli/CertoraProver/certoraConfigIO.py +17 -14
  10. certora_cli/CertoraProver/certoraContext.py +62 -10
  11. certora_cli/CertoraProver/certoraContextAttributes.py +132 -203
  12. certora_cli/CertoraProver/certoraContextValidator.py +108 -101
  13. certora_cli/CertoraProver/certoraParseBuildScript.py +4 -3
  14. certora_cli/CertoraProver/certoraVerifyGenerator.py +9 -4
  15. certora_cli/CertoraProver/splitRules.py +2 -0
  16. certora_cli/CertoraProver/storageExtension.py +0 -35
  17. certora_cli/Mutate/mutateApp.py +16 -10
  18. certora_cli/Mutate/mutateAttributes.py +11 -0
  19. certora_cli/Shared/certoraAttrUtil.py +11 -5
  20. certora_cli/Shared/certoraUtils.py +50 -47
  21. certora_cli/Shared/certoraValidateFuncs.py +29 -15
  22. certora_cli/Shared/proverCommon.py +6 -2
  23. certora_cli/certoraCVLFormatter.py +76 -0
  24. certora_cli/certoraConcord.py +39 -0
  25. certora_cli/certoraRun.py +53 -91
  26. certora_cli/certoraSolanaProver.py +1 -1
  27. certora_cli/certoraSorobanProver.py +1 -1
  28. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/METADATA +4 -3
  29. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/RECORD +36 -33
  30. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/entry_points.txt +1 -0
  31. certora_jars/ASTExtraction.jar +0 -0
  32. certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -1
  33. certora_jars/Typechecker.jar +0 -0
  34. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/LICENSE +0 -0
  35. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/WHEEL +0 -0
  36. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/top_level.txt +0 -0
@@ -43,7 +43,6 @@ from tqdm import tqdm
43
43
 
44
44
  import logging
45
45
 
46
-
47
46
  cloud_logger = logging.getLogger("cloud")
48
47
 
49
48
  MAX_FILE_SIZE = 25 * 1024 * 1024
@@ -74,11 +73,12 @@ class EcoEnum(Util.NoValEnum):
74
73
  EVM = Util.auto()
75
74
  SOROBAN = Util.auto()
76
75
  SOLANA = Util.auto()
76
+ SUI = Util.auto()
77
77
 
78
78
  class ProductEnum(Util.NoValEnum):
79
79
  PROVER = Util.auto()
80
80
  RANGER = Util.auto()
81
- SOPHY = Util.auto()
81
+ CONCORD = Util.auto()
82
82
 
83
83
 
84
84
  class TimeError(Exception):
@@ -587,7 +587,7 @@ class CloudVerification:
587
587
  """
588
588
 
589
589
  auth_data = {
590
- "process": self.context.process, "runName": self.runName,
590
+ "runName": self.runName,
591
591
  "run_source": self.context.run_source,
592
592
  "group_id": self.context.group_id,
593
593
  "branch": self.context.prover_version if self.context.prover_version else '',
@@ -624,6 +624,13 @@ class CloudVerification:
624
624
  rust_jar_settings.append(paths_in_source_dir(self.context.solana_inlining))
625
625
 
626
626
  auth_data["jarSettings"] = rust_jar_settings + jar_settings
627
+
628
+ elif Attrs.is_sui_app():
629
+ sui_jar_settings = ['-movePath', Path(self.context.move_path).name]
630
+ auth_data["jarSettings"] = sui_jar_settings + jar_settings
631
+
632
+ elif Attrs.is_concord_app():
633
+ auth_data["jarSettings"] = jar_settings + ["-equivalenceCheck", "true"]
627
634
  else:
628
635
  auth_data["jarSettings"] = jar_settings
629
636
 
@@ -645,13 +652,15 @@ class CloudVerification:
645
652
  auth_data["ecosystem"] = EcoEnum.SOLANA.name
646
653
  elif Attrs.is_soroban_app():
647
654
  auth_data["ecosystem"] = EcoEnum.SOROBAN.name
655
+ elif Attrs.is_sui_app():
656
+ auth_data["ecosystem"] = EcoEnum.SUI.name
648
657
  else:
649
658
  auth_data["ecosystem"] = EcoEnum.EVM.name
650
659
 
651
660
  if Attrs.is_ranger_app():
652
661
  auth_data["product"] = ProductEnum.RANGER.name
653
- elif Attrs.is_sophy_app():
654
- auth_data["product"] = ProductEnum.SOPHY.name
662
+ elif Attrs.is_concord_app():
663
+ auth_data["product"] = ProductEnum.CONCORD.name
655
664
  else:
656
665
  auth_data["product"] = ProductEnum.PROVER.name
657
666
 
@@ -686,12 +695,12 @@ class CloudVerification:
686
695
 
687
696
  def print_output_links(self) -> None:
688
697
  print(f"Manage your jobs at {self.get_domain()}")
689
- print(f"Follow your job and see verification results at {self.reportUrl}", flush=True)
698
+ print(f"Follow your job and see verification results at {self.url_print_format()}", flush=True)
690
699
 
691
700
  def print_verification_summary(self) -> None:
692
701
  report_exists = self.check_file_exists(params={"filename": "index.html", "certoraKey": self.context.key})
693
702
  if report_exists:
694
- print(f"Job is completed! View the results at {self.reportUrl}")
703
+ print(f"Job is completed! View the results at {self.url_print_format()}")
695
704
  print("Finished verification request", flush=True)
696
705
 
697
706
  def __send_verification_request(self, cl_args: str) -> bool:
@@ -760,42 +769,30 @@ class CloudVerification:
760
769
  Util.get_configuration_layout_data_file(),
761
770
  Util.get_build_dir() / Path(self.context.files[0]).name]
762
771
 
772
+ if Util.get_debug_log_file().exists():
773
+ files_list.append(Util.get_debug_log_file())
774
+
763
775
  if Util.get_certora_sources_dir().exists():
764
776
  files_list.append(Util.get_certora_sources_dir())
765
777
 
766
778
  if hasattr(self.context, 'build_script') and self.context.build_script:
767
- result = compress_files(self.logZipFilePath, Util.get_debug_log_file(),
768
- short_output=Ctx.is_minimal_cli_output(self.context))
769
-
770
- if not result:
771
- return False
772
- files_list.append(self.logZipFilePath)
773
-
774
- # Create a .RustExecution file to classify zipInput as a rust source code
775
- rust_execution_file = Util.get_build_dir() / ".RustExecution"
776
- rust_execution_file.touch(exist_ok=True)
777
- files_list.append(rust_execution_file)
778
-
779
779
  if attr_file := getattr(self.context, 'rust_logs_stdout', None):
780
780
  files_list.append(Util.get_build_dir() / Path(attr_file).name)
781
781
  if attr_file := getattr(self.context, 'rust_logs_stderr', None):
782
782
  files_list.append(Util.get_build_dir() / Path(attr_file).name)
783
783
 
784
- result = compress_files(self.ZipFilePath, *files_list,
785
- short_output=Ctx.is_minimal_cli_output(self.context))
784
+ result = compress_files(self.ZipFilePath, *files_list,
785
+ short_output=Ctx.is_minimal_cli_output(self.context))
786
+ elif Attrs.is_sui_app():
787
+ files_list = [Util.get_certora_metadata_file(),
788
+ Util.get_configuration_layout_data_file(),
789
+ Util.get_build_dir() / Path(self.context.move_path).name]
786
790
 
787
- elif Attrs.is_solana_app() or Attrs.is_soroban_app():
788
- for file in self.context.files:
789
- files_list.append(Path(file))
791
+ if Util.get_certora_sources_dir().exists():
792
+ files_list.append(Util.get_certora_sources_dir())
790
793
 
791
- result = compress_files(self.ZipFilePath, *files_list,
792
- short_output=Ctx.is_minimal_cli_output(self.context))
793
- else:
794
- raise Util.CertoraUserInputError(
795
- 'When running rust application, context should either have attribute "rust_executables" '
796
- 'provided by build_script execution, '
797
- 'or either is_solana_app(), is_soroban_app() should be set to True'
798
- )
794
+ result = compress_files(self.ZipFilePath, *files_list,
795
+ short_output=Ctx.is_minimal_cli_output(self.context))
799
796
 
800
797
  else:
801
798
  # Zip the log file first separately and again with the rest of the files, so it will not be decompressed
@@ -873,7 +870,7 @@ class CloudVerification:
873
870
  self.runName, self.reportUrl, self.msg, self.get_domain(), str(self.userId), self.anonymousKey)
874
871
 
875
872
  # Generate a json file for the VS Code extension with the relevant url
876
- self.vscode_extension_info_writer.add_field("verification_report_url", self.reportUrl)
873
+ self.vscode_extension_info_writer.add_field("verification_report_url", self.url_print_format())
877
874
  self.vscode_extension_info_writer.write_file()
878
875
 
879
876
  if not wait_for_results or wait_for_results == str(Vf.WaitForResultOptions.NONE):
@@ -953,6 +950,8 @@ class CloudVerification:
953
950
  for i in range(self.verification_request_retries):
954
951
  try:
955
952
  response = requests.post(verify_url, json=auth_data, headers=headers, timeout=60)
953
+ cloud_logger.debug(f"\n\n=================\n\nresponse - Status Code: {response.status_code}\n\n"
954
+ f"Headers:\n {response.headers}\n\nText:\n {response.text}\n\n=================\n\n")
956
955
  if response is None:
957
956
  continue
958
957
  status = response.status_code
@@ -1160,6 +1159,12 @@ class CloudVerification:
1160
1159
  Util.remove_file(self.ZipFilePath)
1161
1160
  Util.remove_file(self.logZipFilePath)
1162
1161
 
1162
+ def url_print_format(self) -> str:
1163
+ if self.reportUrl and self.context.url_visibility == str(Vf.UrlVisibilityOptions.PRIVATE):
1164
+ return self.privatize_url()
1165
+ else:
1166
+ return self.reportUrl
1167
+
1163
1168
  @lru_cache(maxsize=1, typed=False)
1164
1169
  def get_domain(self) -> str:
1165
1170
  """
@@ -1175,3 +1180,7 @@ class CloudVerification:
1175
1180
  domain = Util.SupportedServers.PRODUCTION.value
1176
1181
 
1177
1182
  return domain
1183
+
1184
+ @lru_cache(maxsize=1)
1185
+ def privatize_url(self) -> str:
1186
+ return self.reportUrl.split('?anonymousKey=')[0]
@@ -25,12 +25,13 @@ sys.path.insert(0, str(scripts_dir_path))
25
25
  import CertoraProver.certoraContextAttributes as Attrs
26
26
  from CertoraProver.certoraCollectRunMetadata import RunMetaData, MetadataEncoder
27
27
  import Shared.certoraUtils as Utils
28
+ from typing import List
28
29
 
29
30
 
30
31
  class MainSection(Enum):
31
32
  GENERAL = "GENERAL"
33
+ OPTIONS = "OPTIONS"
32
34
  SOLIDITY_COMPILER = "SOLIDITY_COMPILER"
33
- GIT = "GIT"
34
35
  NEW_SECTION = "NEW_SECTION"
35
36
 
36
37
 
@@ -47,13 +48,11 @@ class InnerContent:
47
48
  content: Any
48
49
  doc_link: str = ''
49
50
  tooltip: str = ''
50
- unsound: str = 'false'
51
+ unsound: bool = False
51
52
 
52
53
  def __post_init__(self) -> None:
53
54
  if isinstance(self.content, bool):
54
55
  self.content = 'true' if self.content else 'false'
55
- if isinstance(self.unsound, bool):
56
- self.unsound = 'true' if self.unsound else 'false'
57
56
 
58
57
 
59
58
  @dataclasses.dataclass
@@ -65,6 +64,7 @@ class CardContent:
65
64
 
66
65
  DOC_LINK_PREFIX = 'https://docs.certora.com/en/latest/docs/'
67
66
  GIT_ATTRIBUTES = ['origin', 'revision', 'branch', 'dirty']
67
+ ARG_LIST_ATTRIBUTES = ['prover_args', 'java_args']
68
68
 
69
69
 
70
70
  class AttributeJobConfigData:
@@ -73,13 +73,13 @@ class AttributeJobConfigData:
73
73
  This should be added to the AttributeDefinition and configured for every new attribute
74
74
  presented in the Rule report.
75
75
 
76
- Note: Attributes which do not contain specific information will be assigned as a Flag in the General main section!
76
+ Note: Attributes that do not contain specific information will be presented in the OPTIONS main section!
77
77
 
78
78
  arguments:
79
79
  - main_section : MainSection -- the main section inside the config tab
80
- default: MainSection.GENERAL
81
- - subsection : str -- the subsection within the main_section (e.g Flags)
82
- default: Flags
80
+ default: MainSection.OPTIONS
81
+ - subsection : str -- the subsection within the main_section
82
+ default: None - they will be presented inside the OPTIONS card
83
83
  - doc_link : Optional[str] -- a link to the Documentation page of this attribute (if exists)
84
84
  default: 'https://docs.certora.com/en/latest/docs/' + Solana/EVM path + #<attribute_name>
85
85
  - tooltip : Optional[str] -- a description of this attribute to present in the config tab
@@ -88,7 +88,7 @@ class AttributeJobConfigData:
88
88
  default: False
89
89
  """
90
90
 
91
- def __init__(self, main_section: MainSection = MainSection.GENERAL, subsection: str = '',
91
+ def __init__(self, main_section: MainSection = MainSection.OPTIONS, subsection: str = '',
92
92
  doc_link: Optional[str] = '', tooltip: Optional[str] = '', unsound: bool = False):
93
93
  self.main_section = main_section
94
94
  self.subsection = subsection
@@ -201,6 +201,40 @@ def create_or_get_card_content(output: list[CardContent], name: str) -> CardCont
201
201
  return main_section
202
202
 
203
203
 
204
+ def split_and_sort_arg_list_value(args_list: List[str]) -> List[str]:
205
+ """
206
+ Splits a unified CLI argument list of strings into a sorted list of flag+value groups.
207
+ This is useful mainly for --prover_args and --java_args.
208
+
209
+ For example:
210
+ "-depth 15 -adaptiveSolverConfig false" → ["-adaptiveSolverConfig false", "-depth 15"]
211
+
212
+ Assumes each flag starts with '-' and its value follows immediately, if exists.
213
+ Lines are sorted alphabetically.
214
+ """
215
+ unified_args = ''.join(args_list)
216
+
217
+ if not unified_args.strip():
218
+ return []
219
+
220
+ lines: List[str] = []
221
+ tokens = unified_args.split()
222
+ curr_line = ""
223
+
224
+ for token in tokens:
225
+ if token.startswith('-'):
226
+ if curr_line:
227
+ lines.append(curr_line)
228
+ curr_line = token
229
+ else:
230
+ curr_line += f" {token}"
231
+
232
+ if curr_line:
233
+ lines.append(curr_line)
234
+
235
+ return sorted(lines)
236
+
237
+
204
238
  def create_inner_content(name: str, content_type: ContentType, value: Any, doc_link: str,
205
239
  config_data: AttributeJobConfigData) -> InnerContent:
206
240
  return InnerContent(
@@ -209,7 +243,7 @@ def create_inner_content(name: str, content_type: ContentType, value: Any, doc_l
209
243
  content=value,
210
244
  doc_link=doc_link,
211
245
  tooltip=config_data.tooltip or '',
212
- unsound='true' if config_data.unsound else 'false'
246
+ unsound=config_data.unsound
213
247
  )
214
248
 
215
249
 
@@ -260,35 +294,23 @@ def collect_attribute_configs(metadata: dict) -> list[CardContent]:
260
294
  )
261
295
  continue
262
296
 
263
- # Find or create the subsection (if it exists)
297
+ # Find or create the subsection (if it doesn't exist)
264
298
  if isinstance(attr_value, list):
265
- current_section: Any = main_section
266
299
  content_type = ContentType.SIMPLE
300
+ if attr_name in ARG_LIST_ATTRIBUTES:
301
+ attr_value = split_and_sort_arg_list_value(attr_value)
267
302
 
268
303
  elif isinstance(attr_value, dict):
269
- current_section = main_section
270
304
  content_type = ContentType.COMPLEX
271
305
  attr_value = [
272
306
  create_inner_content(key, ContentType.FLAG, value, doc_link, config_data)
273
307
  for key, value in attr_value.items()
274
308
  ]
275
309
  else:
276
- # this attribute is a value attribute without a subsection, it will be placed in flags.
277
- subsection_key = config_data.subsection.lower() if config_data.subsection else 'flags'
278
- current_section = next((section for section in main_section.content
279
- if section.inner_title == subsection_key), None)
280
- if current_section is None:
281
- current_section = InnerContent(
282
- inner_title=subsection_key,
283
- content_type=ContentType.COMPLEX.value,
284
- content=[]
285
- )
286
- main_section.content.append(current_section)
287
-
288
310
  content_type = ContentType.FLAG
289
311
 
290
312
  # Update the current section with attribute details
291
- current_section.content.append(
313
+ main_section.content.append(
292
314
  create_inner_content(attr_name, content_type, attr_value, doc_link, config_data)
293
315
  )
294
316
 
@@ -300,37 +322,23 @@ def collect_run_config_from_metadata(attributes_configs: list[CardContent], meta
300
322
  Adding CLI and Git configuration from metadata
301
323
  """
302
324
 
303
- # Define a mapping of metadata attributes to their keys in general_section
304
- metadata_mappings = {
305
- 'CLI Version': metadata.get('CLI_version'),
306
- 'Verify': metadata.get('conf', {}).get('verify'),
307
- }
308
-
309
325
  general_section = create_or_get_card_content(attributes_configs, MainSection.GENERAL.value.lower())
310
326
 
311
- # Add metadata attributes dynamically if they exist
312
- for key, value in metadata_mappings.items():
313
- if value:
314
- general_section.content.append(InnerContent(
315
- inner_title=key,
316
- content_type=ContentType.FLAG.value,
317
- content=value,
318
- ))
327
+ if cli_version := metadata.get('CLI_version'):
328
+ general_section.content.append(InnerContent(
329
+ inner_title='CLI Version',
330
+ content_type=ContentType.FLAG.value,
331
+ content=cli_version,
332
+ ))
319
333
 
320
- # Adding GIT configuration from metadata only if attributes are found
321
- git_content = []
322
334
  for attr in GIT_ATTRIBUTES:
323
335
  if attr_value := metadata.get(attr):
324
- git_content.append(InnerContent(
336
+ general_section.content.append(InnerContent(
325
337
  inner_title=attr,
326
338
  content_type=ContentType.FLAG.value,
327
339
  content=attr_value,
328
340
  ))
329
341
 
330
- if git_content:
331
- git_section = create_or_get_card_content(attributes_configs, MainSection.GIT.value.lower())
332
- git_section.content = git_content
333
-
334
342
  return attributes_configs
335
343
 
336
344
 
@@ -338,14 +346,17 @@ def sort_configuration_layout(data: list[CardContent]) -> list[CardContent]:
338
346
  """
339
347
  Sorts a configuration layout:
340
348
  - Top-level sorted by 'card_title'
341
- - Nested content sorted by 'inner_title', with 'Verify' first
349
+ - Nested content sorted by 'inner_title', with 'verify' first
342
350
  """
343
351
  priority = {
344
- "Verify": 0,
352
+ # Priorities for top-level cards
345
353
  "general": 0,
354
+ "files": 1,
355
+ "options": 2,
356
+ # Top level items inside their respective cards
357
+ "verify": 0,
346
358
  "solc": 0,
347
- "CLI Version": 1,
348
- "flags": 2
359
+ "CLI Version": 0
349
360
  }
350
361
 
351
362
  def inner_sort_key(item: Any) -> Any:
@@ -31,6 +31,7 @@ import CertoraProver.certoraContextAttributes as Attrs
31
31
 
32
32
  metadata_logger = logging.getLogger("metadata")
33
33
 
34
+
34
35
  def collect_args_with_jar_flags() -> List[str]:
35
36
  return_array = []
36
37
  for attr in Attrs.get_attribute_class().attribute_list():
@@ -70,10 +71,18 @@ class RunMetaData:
70
71
  dirty -- true iff the git repository has changes (git diff is not empty)
71
72
  main_spec -- the relative path to the main spec file that should be displayed by default at the web report
72
73
  conf_path -- the relative path form the cwd_relative to the configuration file
74
+ group_id -- optional identifier for grouping this run
75
+ java_version -- version of Java used during the run, if available
76
+ python_version -- version of Python running the process
77
+ certora_ci_client -- name of the CI client if available, derived from environment
78
+ timestamp -- UTC timestamp when the run metadata was generated
79
+ CLI_package_name -- name of the CLI package
80
+ CLI_version -- version of the CLI
81
+ jar_flag_info -- CLI attributes which are jarFlags
73
82
  """
74
83
  def __init__(self, raw_args: List[str], conf: Dict[str, Any], origin: str, revision: str,
75
84
  branch: str, cwd_relative: Path, dirty: bool, main_spec: Optional[str],
76
- conf_path: Optional[Path], group_id: Optional[str]):
85
+ conf_path: Optional[Path], group_id: Optional[str], java_version: str):
77
86
  self.raw_args = raw_args
78
87
  self.conf = conf
79
88
  self.origin = origin
@@ -85,6 +94,7 @@ class RunMetaData:
85
94
  self.conf_path = conf_path
86
95
  self.group_id = group_id
87
96
  self.python_version = ".".join(str(x) for x in sys.version_info[:3])
97
+ self.java_version = java_version
88
98
  self.certora_ci_client = Utils.get_certora_ci_name()
89
99
  self.timestamp = str(datetime.now(timezone.utc).timestamp())
90
100
  _, self.CLI_package_name, self.CLI_version = Utils.get_package_and_version()
@@ -105,6 +115,7 @@ class RunMetaData:
105
115
  f" conf_path: {self.conf_path}\n"
106
116
  f" group_id: {self.group_id}\n"
107
117
  f" python_version: {self.python_version}\n"
118
+ f" java_version: {self.java_version}\n"
108
119
  f" CertoraCI client: {self.certora_ci_client}\n"
109
120
  f" jar_flag_info: {self.jar_flag_info}\n"
110
121
  )
@@ -182,7 +193,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
182
193
  dirty=True,
183
194
  main_spec=None,
184
195
  conf_path=None,
185
- group_id=None)
196
+ group_id=None,
197
+ java_version=context.java_version)
186
198
 
187
199
  # collect information about current git snapshot
188
200
  cwd_abs = wd.absolute()
@@ -210,7 +222,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
210
222
  dirty=True,
211
223
  main_spec=get_main_spec(context),
212
224
  conf_path=conf_path,
213
- group_id=context.group_id)
225
+ group_id=context.group_id,
226
+ java_version=context.java_version)
214
227
 
215
228
  try:
216
229
  sha_out = subprocess.run(['git', 'rev-parse', 'HEAD'], cwd=wd,
@@ -245,7 +258,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
245
258
  dirty=dirty,
246
259
  main_spec=get_main_spec(context),
247
260
  conf_path=conf_path,
248
- group_id=context.group_id)
261
+ group_id=context.group_id,
262
+ java_version=context.java_version)
249
263
 
250
264
  metadata_logger.debug(f' collected data:\n{str(data)}')
251
265
 
@@ -263,4 +277,5 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
263
277
  dirty=True,
264
278
  main_spec=get_main_spec(context),
265
279
  conf_path=conf_path,
266
- group_id=context.group_id)
280
+ group_id=context.group_id,
281
+ java_version=context.java_version)
@@ -17,6 +17,7 @@ import json5
17
17
  import logging
18
18
  from pathlib import Path
19
19
  from typing import Dict, Any
20
+ from collections import OrderedDict
20
21
 
21
22
  import CertoraProver.certoraContext as Ctx
22
23
  import CertoraProver.certoraContextAttributes as Attrs
@@ -75,11 +76,11 @@ def read_from_conf_file(context: CertoraContext) -> None:
75
76
 
76
77
  try:
77
78
  with conf_file_path.open() as conf_file:
78
- context.conf_file_attr = json5.load(conf_file, allow_duplicate_keys=False)
79
+ context.conf_file_attr = json5.load(conf_file, allow_duplicate_keys=False, object_pairs_hook=OrderedDict)
79
80
  try:
80
81
  check_conf_content(context)
81
82
  except Util.CertoraUserInputError as e:
82
- raise Util.CertoraUserInputError(f"Error when reading {conf_file_path}: {str(e)}", e) from None
83
+ raise Util.CertoraUserInputError(f"Error when reading {conf_file_path}: {e}") from None
83
84
  context.conf_file = str(conf_file_path)
84
85
  except FileNotFoundError:
85
86
  raise Util.CertoraUserInputError(f"read_from_conf_file: {conf_file_path}: not found") from None
@@ -96,22 +97,24 @@ def handle_override_base_config(context: CertoraContext) -> None:
96
97
  """
97
98
 
98
99
  if context.override_base_config:
99
- with Path(context.override_base_config).open() as conf_file:
100
- try:
101
- override_base_config_attrs = json5.load(conf_file, allow_duplicate_keys=False)
100
+ try:
101
+ with Path(context.override_base_config).open() as conf_file:
102
+ override_base_config_attrs = json5.load(conf_file, allow_duplicate_keys=False,
103
+ object_pairs_hook=OrderedDict)
102
104
  context.conf_file_attr = {**override_base_config_attrs, **context.conf_file_attr}
103
105
 
104
106
  if 'override_base_config' in override_base_config_attrs:
105
107
  raise Util.CertoraUserInputError("base config cannot include 'override_base_config'")
106
- except Exception as e:
107
- raise Util.CertoraUserInputError(f"Cannot load base config: {context.override_base_config}\n{e}")
108
- for attr in override_base_config_attrs:
109
- if hasattr(context, attr):
110
- value = getattr(context, attr, False)
111
- if not value:
112
- setattr(context, attr, override_base_config_attrs.get(attr))
113
- else:
114
- raise Util.CertoraUserInputError(f"{attr} appears in the base conf file {context.override_base_config} but is not a known attribute.")
108
+ except Exception as e:
109
+ raise Util.CertoraUserInputError(f"Cannot load base config: {context.override_base_config}\n{e}")
110
+
111
+ for attr in override_base_config_attrs:
112
+ if hasattr(context, attr):
113
+ value = getattr(context, attr, False)
114
+ if not value:
115
+ setattr(context, attr, override_base_config_attrs.get(attr))
116
+ else:
117
+ raise Util.CertoraUserInputError(f"{attr} appears in the base conf file {context.override_base_config} but is not a known attribute.")
115
118
 
116
119
 
117
120
  def check_conf_content(context: CertoraContext) -> None: