-
Notifications
You must be signed in to change notification settings - Fork 83
/
Copy pathfoundry.py
executable file
·179 lines (139 loc) · 5.32 KB
/
foundry.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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
"""
Foundry platform
"""
import logging
import os
import subprocess
from pathlib import Path
from typing import TYPE_CHECKING, List, Optional, TypeVar
import json
from crytic_compile.platform.abstract_platform import AbstractPlatform, PlatformConfig
from crytic_compile.platform.types import Type
from crytic_compile.platform.hardhat import hardhat_like_parsing
from crytic_compile.utils.subprocess import run
# Handle cycle
if TYPE_CHECKING:
from crytic_compile import CryticCompile
T = TypeVar("T")
LOGGER = logging.getLogger("CryticCompile")
class Foundry(AbstractPlatform):
"""
Foundry platform
"""
NAME = "Foundry"
PROJECT_URL = "https://github.com/foundry-rs/foundry"
TYPE = Type.FOUNDRY
# pylint: disable=too-many-locals,too-many-statements,too-many-branches
def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None:
"""Compile
Args:
crytic_compile (CryticCompile): CryticCompile object to populate
**kwargs: optional arguments. Used: "foundry_ignore_compile", "foundry_out_directory"
"""
ignore_compile = kwargs.get("foundry_ignore_compile", False) or kwargs.get(
"ignore_compile", False
)
out_directory = kwargs.get("foundry_out_directory", "out")
if ignore_compile:
LOGGER.info(
"--ignore-compile used, if something goes wrong, consider removing the ignore compile flag"
)
if not ignore_compile:
compilation_command = [
"forge",
"build",
"--build-info",
]
compile_all = kwargs.get("foundry_compile_all", False)
if not compile_all:
foundry_config = self.config(self._target)
if foundry_config:
compilation_command += [
"--skip",
f"*/{foundry_config.tests_path}/**",
f"*/{foundry_config.scripts_path}/**",
"--force",
]
run(
compilation_command,
cwd=self._target,
)
build_directory = Path(
self._target,
out_directory,
"build-info",
)
hardhat_like_parsing(crytic_compile, self._target, build_directory, self._target)
def clean(self, **kwargs: str) -> None:
"""Clean compilation artifacts
Args:
**kwargs: optional arguments.
"""
ignore_compile = kwargs.get("foundry_ignore_compile", False) or kwargs.get(
"ignore_compile", False
)
if ignore_compile:
return
run(["forge", "clean"], cwd=self._target)
@staticmethod
def is_supported(target: str, **kwargs: str) -> bool:
"""Check if the target is a foundry project
Args:
target (str): path to the target
**kwargs: optional arguments. Used: "foundry_ignore"
Returns:
bool: True if the target is a foundry project
"""
if kwargs.get("foundry_ignore", False):
return False
return os.path.isfile(os.path.join(target, "foundry.toml"))
@staticmethod
def config(working_dir: str) -> Optional[PlatformConfig]:
"""Return configuration data that should be passed to solc, such as remappings.
Args:
working_dir (str): path to the working_dir
Returns:
Optional[PlatformConfig]: Platform configuration data such as optimization, remappings...
"""
result = PlatformConfig()
LOGGER.info("'forge config --json' running")
json_config = json.loads(
subprocess.run(
["forge", "config", "--json"], cwd=working_dir, stdout=subprocess.PIPE, check=True
).stdout
)
# Solc configurations
result.solc_version = json_config.get("solc")
result.via_ir = json_config.get("via_ir")
result.allow_paths = json_config.get("allow_paths")
result.offline = json_config.get("offline")
result.evm_version = json_config.get("evm_version")
result.optimizer = json_config.get("optimizer")
result.optimizer_runs = json_config.get("optimizer_runs")
result.remappings = json_config.get("remappings")
# Foundry project configurations
result.src_path = json_config.get("src")
result.tests_path = json_config.get("test")
result.libs_path = json_config.get("libs")
result.scripts_path = json_config.get("script")
return result
# pylint: disable=no-self-use
def is_dependency(self, path: str) -> bool:
"""Check if the path is a dependency
Args:
path (str): path to the target
Returns:
bool: True if the target is a dependency
"""
if path in self._cached_dependencies:
return self._cached_dependencies[path]
ret = "lib" in Path(path).parts or "node_modules" in Path(path).parts
self._cached_dependencies[path] = ret
return ret
# pylint: disable=no-self-use
def _guessed_tests(self) -> List[str]:
"""Guess the potential unit tests commands
Returns:
List[str]: The guessed unit tests commands
"""
return ["forge test"]