Skip to content

Commit f86f4a6

Browse files
committed
Testsuite: Add an altrun for CCG
This altrun allows the testsuite to build test executables through CCG, for a native target. The altrun inlcudes: - a pre_testsuite.py script that prepares the coverage runtime, the support library and the Ada runtime; - a cgprbuild.py script that is meant to replace gprbuild. It invokes CCG through gprbuild, then collects all the generated C files and compiles them to generate an executable; - various other files used by the two above scripts as build helpers. Note that CCG does not support compiling projects that have C listed as a project language, the binder will thus not execute and no main will be generated. As such, it is best not to run the full testsuite with this altrun but only a subset of tests which only contains Ada sources, such as Qualif/Ada.
1 parent 6864cab commit f86f4a6

File tree

10 files changed

+425
-29
lines changed

10 files changed

+425
-29
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ lib
2424
/testsuite/suite.cgpr
2525
/testsuite/support
2626
/testsuite/tmp
27+
/testsuite/altrun/ccg_native/ccg_gnatcov_rts
28+
/testsuite/altrun/ccg_native/adainclude
2729
gnatcov-loc.xml
2830
gnatinspect.db
2931
README.html
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
c XFAIL Runtime check bug in CCG, see eng/toolchain/gnat-llvm#73
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
c DEAD ASM inlining not supported by CCG
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
c DEAD ASM inlining not supported by CCG

testsuite/SUITE/control.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,23 +241,29 @@ def __init__(self, runtime_name=None):
241241
self.has_kernel_runtime = False
242242
self.has_light_runtime = False
243243
self.has_exception_propagation = True
244+
self.discrs = []
244245

245246
if not self.runtime_name:
246247
self.has_full_runtime = True
248+
self.discrs = ["RTS_FULL"]
247249
elif 'embedded' in self.runtime_name:
248250
self.has_ravenscar_runtime = True
251+
self.discrs = ["RTS_RAVENSCAR", "RTS_EMBEDDED"]
249252
elif 'light-tasking' in self.runtime_name:
250253
self.has_ravenscar_runtime = True
251254
self.has_exception_propagation = False
255+
self.discrs = ["RTS_RAVENSCAR", "RTS_LIGHT_TASKING"]
252256
elif self.runtime_name.startswith('zfp'):
253257
self.has_light_runtime = True
254258
self.has_exception_propagation = False
259+
self.discrs = ["RTS_ZFP"]
255260
elif (
256261
self.runtime_name == "light"
257262
or self.runtime_name.startswith('light-')
258263
):
259264
self.has_light_runtime = True
260265
self.has_exception_propagation = False
266+
self.discrs = ["RTS_ZFP"]
261267
elif self.runtime_name == 'kernel':
262268
self.has_kernel_runtime = True
263269
else:
@@ -276,12 +282,19 @@ def gnatcov_rts_project(self):
276282
else 'gnatcov_rts'
277283
)
278284

279-
def runtime_info(runtime=None):
280-
if runtime is None:
281-
runtime = env.main_options.RTS
285+
def _runtime_info(runtime, target):
286+
if target == "c":
287+
assert not runtime
288+
# CCG's runtime has no name, but it is for our purposes equivalent
289+
# to a light runtime.
290+
runtime = "light"
282291
return RuntimeInfo(runtime)
283292

284293

294+
def runtime_info():
295+
return _runtime_info(env.main_options.RTS, env.target.platform)
296+
297+
285298
# Target specificities. We don't have many variants but need to match each
286299
# against a few possible triplets.
287300

testsuite/SUITE/tutils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ def run_cov_program(executable, out=None, env=None, exec_args=None,
841841
inp = None
842842

843843
# If we are in a cross configuration, run the program using GNATemulator
844-
if thistest.options.target:
844+
if thistest.options.target and thistest.env.target.platform != "c":
845845
kernel = thistest.options.kernel
846846
board = thistest.options.board or thistest.env.target.machine
847847
args.append('{}-gnatemu'.format(thistest.env.target.triplet))
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import json
5+
import os
6+
import pathlib
7+
8+
from e3.os.process import Run
9+
from e3.platform import Platform
10+
11+
12+
def parse_scenario(arg: str):
13+
"""
14+
Helper to parse -XVAR[=VAL] command line options.
15+
If VAL is not specified in the argument, consider this an ill formed
16+
argument and ignore it.
17+
"""
18+
return arg.split("=", maxsplit=1) if "=" in arg else None
19+
20+
21+
def get_attr(obj, attr_name, idx=0):
22+
"""
23+
Get the value of the attribute named attr_name, at index idx if relevant.
24+
If no such attribute exists, returns None.
25+
"""
26+
attrs = obj["projects"][0]["attributes"]
27+
for attr_ob in attrs:
28+
if attr_ob["name"] == attr_name:
29+
if attr_ob["kind"] == "single":
30+
return attr_ob["value"]
31+
else:
32+
return attr_ob["values"][idx]
33+
return None
34+
35+
36+
def run(cmd, what):
37+
"""
38+
Run the command represented by cmd in a subprocess. If the exit status is
39+
not success and ignore_failure is True, print f"{what} failed" followed by
40+
the command output on the standard output and exit with an error.
41+
Otherwise, return the output of the command execution.
42+
"""
43+
p = Run(cmd)
44+
if p.status != 0:
45+
print(f"{what} failed:")
46+
print(p.command_line_image())
47+
print(p.out)
48+
exit(1)
49+
return p.out
50+
51+
52+
def main():
53+
parser = argparse.ArgumentParser()
54+
parser.add_argument("-P", dest="project")
55+
parser.add_argument("--config", dest="config")
56+
parser.add_argument("--target", dest="target")
57+
parser.add_argument("--RTS", dest="rts")
58+
parser.add_argument("--src-subdirs", dest="src_subdirs")
59+
parser.add_argument("--implicit-with", dest="implicit_with")
60+
parser.add_argument(
61+
"-X", dest="scenario", action="append", type=parse_scenario,
62+
)
63+
parser.add_argument("-v", dest="verbose", action="store_true")
64+
65+
args, extra = parser.parse_known_args()
66+
67+
altrun_dir = os.path.dirname(os.path.realpath(__file__))
68+
69+
# Substitute the "--implicit-with=gnatcov_rts.gpr" to point it toward our
70+
# own custom RTS.
71+
ccg_gnatcov_rts = os.path.join(
72+
altrun_dir, "ccg_gnatcov_rts", "gnatcov_rts.gpr",
73+
)
74+
custom_rts_dir = None
75+
if args.implicit_with and "gnatcov_rts" in args.implicit_with:
76+
custom_rts_dir = os.path.dirname(ccg_gnatcov_rts)
77+
args.implicit_with = ccg_gnatcov_rts
78+
79+
# Build common args for all gpr<tools> commands
80+
common_args = ["--target=c"]
81+
common_args += ["-P", args.project]
82+
if args.src_subdirs:
83+
common_args += [f"--src-subdirs={args.src_subdirs}"]
84+
if args.implicit_with:
85+
common_args += [f"--implicit-with={args.implicit_with}"]
86+
for key, val in args.scenario:
87+
common_args += [f"-X{key}={val}"]
88+
89+
# Add specific scenario for our custom version of gnatcov rts:
90+
common_args.append("-XGNATCOV_RTS_RTS_PROFILE=embedded")
91+
common_args.append("-XGNATCOV_RTS_FOR_CCG=true")
92+
93+
if "-cargs" in extra:
94+
cargs_index = extra.index("-cargs")
95+
extra.insert(cargs_index + 1, f"-mtriple={Platform.get().triplet}")
96+
else:
97+
extra += ["-cargs", f"-mtriple={Platform.get().triplet}"]
98+
99+
# Generate C files
100+
gprbuild_args = ["gprbuild"] + common_args + extra
101+
run(gprbuild_args, "gprbuild invocation")
102+
103+
# Gather information about the project structure, we need the name of the
104+
# main (to generate an executable of the same name), the list of object
105+
# directories, the root directory of the project (as all other directories
106+
# are relatives to this), and the directory in which to place the
107+
# executable.
108+
gprinspect_args = (
109+
["gprinspect"]
110+
+ common_args
111+
+ ["--attributes", "--display=json-compact"]
112+
)
113+
prj_txt = run(gprinspect_args, "gprinspect invocation")
114+
prj = json.loads(prj_txt)
115+
116+
# Assume single main per project file
117+
main_name = os.path.basename(get_attr(prj, "Main")).split(".")[0]
118+
prj_dir = get_attr(prj, "Project_Dir")
119+
exec_dir = os.path.join(prj_dir, get_attr(prj, "Exec_Dir"))
120+
obj_dir = os.path.join(prj_dir, get_attr(prj, "Object_Dir"))
121+
122+
# Get the list of all the generated C files that need to be compiled. gprls
123+
# will return a list of ".o" files, so replace the extension to ".c"
124+
gprls_args = ["gprls"] + common_args + ["-o", "-U"]
125+
126+
c_files = run(gprls_args, "gprls invocation").splitlines()
127+
c_files = [f for f in c_files if f[-2:] == ".c"]
128+
# Add the generated binder file in order to be able to link the executable
129+
c_files.append(os.path.join(obj_dir, "b__" + main_name + ".c"))
130+
131+
# Hack: given the characteristics passed to the runtime (light runtime),
132+
# it will always add a "last_chance_dumper" unit which defines the symbol
133+
# __lch_enter. This symbol is also defined in the Libsupport unit, as part
134+
# of the Libsupport library. This normally is a static library, and thus
135+
# the object for __lch_enter should not be pulled during the link, but with
136+
# ccg we don't build any libraries. as such we must exclude libsupport.c
137+
# from the gcc invocation to avoid having multiple definitions of
138+
# __lch_enter.
139+
c_files = [
140+
filename
141+
for filename in c_files
142+
if os.path.basename(filename) != "libsupport.c"
143+
]
144+
145+
gcc_args = ["gcc"]
146+
147+
if "-g" in extra:
148+
gcc_args += ["-g"]
149+
150+
gcc_args += c_files
151+
152+
# Add the list of native C files from gnatcov_rts: the project was modified
153+
# to not include C as a language (to avoid gprbuild issues), but these are
154+
# still required for a successful build.
155+
if custom_rts_dir:
156+
rts_dir = pathlib.Path(custom_rts_dir)
157+
gcc_args += [
158+
str(pth.absolute()) for pth in rts_dir.glob("gnatcov_rts_c*")
159+
]
160+
161+
# Add the runtime library in the mix. The pre-testsuite script compile the
162+
# library and does not bother to create a separate adalib directory for the
163+
# compilation results
164+
gcc_args += [os.path.join(altrun_dir, "adainclude", "libgnat.a")]
165+
166+
# Compile the executable
167+
gcc_args += [
168+
"-o",
169+
os.path.join(exec_dir, main_name + Platform.get().os.exeext),
170+
]
171+
run(gcc_args, "gcc invocation")
172+
173+
174+
if __name__ == "__main__":
175+
main()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
project Libgnat is
2+
3+
for Source_Dirs use ("adainclude");
4+
for Object_Dir use "adainclude";
5+
6+
end Libgnat;

0 commit comments

Comments
 (0)