forked from ethereum/solidity
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_helpers.py
157 lines (129 loc) · 5.89 KB
/
test_helpers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env python3
# ------------------------------------------------------------------------------
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2023 solidity contributors.
# ------------------------------------------------------------------------------
import os
import re
import subprocess
import sys
from argparse import ArgumentParser
from enum import Enum
from pathlib import Path
from typing import List
from typing import Set
# Our scripts/ is not a proper Python package so we need to modify PYTHONPATH to import from it
# pragma pylint: disable=import-error,wrong-import-position
PROJECT_ROOT = Path(__file__).parents[2]
sys.path.insert(0, f"{PROJECT_ROOT}/scripts/common")
from git_helpers import git_commit_hash
SOLC_FULL_VERSION_REGEX = re.compile(r"^[a-zA-Z: ]*(.*)$")
SOLC_SHORT_VERSION_REGEX = re.compile(r"^([0-9.]+).*\+|\-$")
class SettingsPreset(Enum):
LEGACY_NO_OPTIMIZE = 'legacy-no-optimize'
IR_NO_OPTIMIZE = 'ir-no-optimize'
LEGACY_OPTIMIZE_EVM_ONLY = 'legacy-optimize-evm-only'
IR_OPTIMIZE_EVM_ONLY = 'ir-optimize-evm-only'
LEGACY_OPTIMIZE_EVM_YUL = 'legacy-optimize-evm+yul'
IR_OPTIMIZE_EVM_YUL = 'ir-optimize-evm+yul'
def compiler_settings(evm_version: str, via_ir: bool = False, optimizer: bool = False, yul: bool = False) -> dict:
return {
"optimizer": {"enabled": optimizer, "details": {"yul": yul}},
"evmVersion": evm_version,
"viaIR": via_ir,
}
def settings_from_preset(preset: SettingsPreset, evm_version: str) -> dict:
return {
SettingsPreset.LEGACY_NO_OPTIMIZE: compiler_settings(evm_version),
SettingsPreset.IR_NO_OPTIMIZE: compiler_settings(evm_version, via_ir=True),
SettingsPreset.LEGACY_OPTIMIZE_EVM_ONLY: compiler_settings(evm_version, optimizer=True),
SettingsPreset.IR_OPTIMIZE_EVM_ONLY: compiler_settings(evm_version, via_ir=True, optimizer=True),
SettingsPreset.LEGACY_OPTIMIZE_EVM_YUL: compiler_settings(evm_version, optimizer=True, yul=True),
SettingsPreset.IR_OPTIMIZE_EVM_YUL: compiler_settings(evm_version, via_ir=True, optimizer=True, yul=True),
}[preset]
def parse_custom_presets(presets: List[str]) -> Set[SettingsPreset]:
return {SettingsPreset(p) for p in presets}
def parse_command_line(description: str, args: List[str]):
arg_parser = ArgumentParser(description)
arg_parser.add_argument(
"solc_binary_type",
metavar="solc-binary-type",
type=str,
default="native",
choices=["native", "solcjs"],
help="""Solidity compiler binary type""",
)
arg_parser.add_argument(
"solc_binary_path",
metavar="solc-binary-path",
type=Path,
help="""Path to solc binary""",
)
arg_parser.add_argument(
"selected_presets",
metavar="selected-presets",
help="""List of compiler settings presets""",
nargs='*',
)
return arg_parser.parse_args(args)
def download_project(test_dir: Path, repo_url: str, ref: str = "<latest-release>"):
print(f"Cloning {repo_url}...")
# Clone the repo ignoring all blobs until needed by git.
# This allows access to commit history but with a fast initial clone
subprocess.run(["git", "clone", "--filter", "blob:none", repo_url, test_dir.resolve()], check=True)
if not test_dir.exists():
raise RuntimeError("Failed to clone the project.")
os.chdir(test_dir)
# If the ref is '<latest-release>' try to use the latest tag as ref
# NOTE: Sadly this will not work with monorepos and may not always
# return the latest tag.
if ref == "<latest-release>":
tags = subprocess.check_output(
["git", "tag", "--sort", "-v:refname"],
encoding="ascii"
).strip().split('\n')
if len(tags) == 0:
raise RuntimeError("Failed to retrieve latest release tag.")
ref = tags[0]
print(f"Using ref: {ref}")
subprocess.run(["git", "checkout", ref], check=True)
if (test_dir / ".gitmodules").exists():
subprocess.run(["git", "submodule", "update", "--init"], check=True)
print(f"Current commit hash: {git_commit_hash()}")
def parse_solc_version(solc_version_string: str) -> str:
solc_version_match = re.search(SOLC_FULL_VERSION_REGEX, solc_version_string)
if solc_version_match is None:
raise RuntimeError(f"Solc version could not be found in: {solc_version_string}.")
return solc_version_match.group(1)
def get_solc_short_version(solc_full_version: str) -> str:
solc_short_version_match = re.search(SOLC_SHORT_VERSION_REGEX, solc_full_version)
if solc_short_version_match is None:
raise RuntimeError(f"Error extracting short version string from: {solc_full_version}.")
return solc_short_version_match.group(1)
def store_benchmark_report(self):
raise NotImplementedError()
def replace_version_pragmas(test_dir: Path):
"""
Replace fixed-version pragmas (part of Consensys best practice).
Include all directories to also cover node dependencies.
"""
print("Replacing fixed-version pragmas...")
for source in test_dir.glob("**/*.sol"):
content = source.read_text(encoding="utf-8")
content = re.sub(r"pragma solidity [^;]+;", r"pragma solidity >=0.0;", content)
with open(source, "w", encoding="utf-8") as f:
f.write(content)