-
Notifications
You must be signed in to change notification settings - Fork 0
/
gpp.py
125 lines (104 loc) · 4.55 KB
/
gpp.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
import stat
from typing import Iterable, Optional, Tuple
from pathlib import Path
from curricula.library import process
from curricula.library.files import delete_file, add_mode
from curricula.library.configurable import Configurable
from curricula_grade.grader.task import Error
from curricula_grade.resource import ExecutableFile, File
from curricula_grade.setup import SetupResult
from curricula_grade.common import Runnable
__all__ = (
"gpp_compile_object",
"gpp_compile_shared_object",
"GppObjectSetup",)
def gpp_compile_object(
source_path: Path,
*source_paths: Path,
destination_path: Path,
gpp_options: Iterable[str] = (),
timeout: float = None) -> Tuple[SetupResult, Optional[ExecutableFile]]:
"""Build a binary from a single C++ file with G++."""
destination_path.parent.mkdir(parents=True, exist_ok=True)
runtime = process.run(
"g++",
str(source_path),
*map(str, source_paths),
*gpp_options,
"-o", str(destination_path),
timeout=timeout)
error_description = None
error_traceback = None
if runtime.raised_exception:
error_description = f"error invoking compilation of {source_path.parts[-1]}: {runtime.exception.description}"
elif runtime.timed_out:
error_description = f"timed out while compiling {source_path.parts[-1]}"
elif runtime.code != 0:
if runtime.stderr:
error_description = "failed to compile"
error_traceback = runtime.stderr.decode(errors="replace")
else:
error_description = "nonzero status code during compilation"
elif not destination_path.exists():
error_description = f"build did not produce {destination_path.parts[-1]}"
# If the build failed
if error_description is not None:
error = Error(description=error_description, traceback=error_traceback)
return SetupResult(passing=False, details=dict(runtime=runtime.dump()), error=error), None
# Chmod
add_mode(destination_path, stat.S_IXOTH)
# Otherwise
return SetupResult(passing=True, details=dict(runtime=runtime.dump())), ExecutableFile(destination_path)
class GppObjectSetup(Configurable, Runnable):
"""Class-based wrapper for gpp_compile_object."""
source_paths: Iterable[Path]
destination_path: Path
gpp_options: Iterable[str]
timeout: float
executable_name: str
result_type = SetupResult
def __call__(self, resources: dict) -> SetupResult:
"""Run gpp_compile_object."""
result, executable = gpp_compile_object(
*self.resolve("source_paths", field_getter_resources=resources),
destination_path=self.resolve("destination_path", field_getter_resources=resources),
gpp_options=self.resolve("gpp_options", default=(), field_getter_resources=resources),
timeout=self.resolve("timeout", default=None, field_getter_resources=resources))
resources[self.resolve("executable_name", field_getter_resources=resources)] = executable
return result
def gpp_compile_shared_object(
harness_path: Path,
source_paths: Iterable[Path] = (),
include_paths: Iterable[Path] = (),
gpp_options: Iterable[str] = (),
object_path: Path = Path("harness.o"),
shared_object_path: Path = Path("harness.so"),
timeout: float = None) -> Tuple[SetupResult, Optional[File]]:
"""Build a shared object file.
The target path will be listed after an include flag so that
headers may be imported.
"""
runtime = process.run(
"g++", "-Wall", "-c", *gpp_options,
str(harness_path.absolute()),
*map(lambda path: f"{path.absolute()}", source_paths),
*map(lambda path: f"-I{path.absolute()}", include_paths),
"-o", str(object_path),
timeout=timeout)
if runtime.code != 0 or runtime.raised_exception is not None:
return (
SetupResult(
passing=False,
details=dict(runtime=runtime.dump()),
error=Error(description="compilation failed")),
None)
runtime = process.run("g++", "-shared", str(object_path), "-o", str(shared_object_path), timeout=timeout)
delete_file(object_path)
if runtime.code != 0 or runtime.raised_exception is not None:
return (
SetupResult(
passing=False,
details=dict(runtime=runtime.dump()),
error=Error(description="shared library build failed")),
None)
return SetupResult(passing=True), File(shared_object_path)