Skip to content

Commit 7fb7810

Browse files
committed
0.2.0: the argument vector is passed unaltered, and continuous integration runs
This implementation prepended the path to the argument vector, as the companion implementation did, and for the same reason: they were written by one author from one reading of a specification that did not settle the point. Clause 7.6 of openkal 0.4 settles it, and this is that change, together with an assertion that observes the vector a started program received rather than only its status. That both implementations made the same choice is not evidence that the choice was right. It is evidence that a second implementation by the same author tests less than a second implementation by another, which is the limit of what these two can establish between them. Continuous integration here had never passed a step it appeared to run. The workflow was copied from the companion implementation and ran an example in a directory this repository does not contain, asserting three lines from a program that is not here. The failure had been reported as an inability to start a shell in a working directory, which reads as an infrastructure fault rather than as a missing example. It is replaced by the portable program, which is taken from the specification at the version the manifest names. That program is the only cross-implementation evidence available to this repository: no continuous integration for the companion implementation runs on this platform, and none for this one runs on that. The assertion that the suites ran named two of the five present, and is now derived from the files. The surface comparison is now --complete.
1 parent 334ddf9 commit 7fb7810

4 files changed

Lines changed: 103 additions & 21 deletions

File tree

.github/workflows/ci.yml

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,30 +35,76 @@ jobs:
3535
mcpp --version
3636
mcpp self config --mirror GLOBAL
3737
38+
# The version of the specification this implementation is written against
39+
# is stated once, in the manifest, and read from there. The companion
40+
# implementation stated it a second time in this file and the two drifted.
41+
- name: Read the specification version from the manifest
42+
run: |
43+
v="$(sed -n 's/^openkal[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' mcpp.toml | head -1)"
44+
test -n "$v" || { echo "the manifest does not name openkal" >&2; exit 1; }
45+
echo "OPENKAL_VERSION=$v" >> "$GITHUB_ENV"
46+
echo "written against openkal $v"
47+
3848
- name: Conformance
3949
run: |
4050
mcpp test 2>&1 | tee conformance.log
41-
# A suite that discovered nothing reports success. Both cases are
42-
# asserted to have run.
43-
grep -q 'conformance_stream ... ok' conformance.log
44-
grep -q 'conformance_memory ... ok' conformance.log
51+
52+
# A suite that discovered nothing reports success, so every suite is
53+
# asserted to have run. The list is derived from the files present
54+
# rather than written out here: a hand-written list names the suites
55+
# that existed when it was written, and a suite added afterwards
56+
# escapes the assertion silently. This assertion named two of five.
57+
missing=0
58+
for f in tests/*.cpp; do
59+
name="$(basename "$f" .cpp)"
60+
if ! grep -q "^$name \.\.\. ok" conformance.log; then
61+
echo "suite did not run, or did not pass: $name" >&2
62+
missing=1
63+
fi
64+
done
65+
test "$missing" -eq 0
66+
67+
- name: The specification, at the version the manifest names
68+
uses: actions/checkout@v4
69+
with:
70+
repository: mcpplibs/openkal
71+
ref: ${{ env.OPENKAL_VERSION }}
72+
path: .spec
4573

4674
- name: The exported surface matches the specification
47-
env:
48-
OPENKAL_VERSION: 0.3.0
4975
run: |
50-
# Clause 9.3. The list is fetched from the specification repository at
51-
# the version this implementation is written against, so that the
52-
# comparison has one source rather than a copy that can drift.
53-
base="https://raw.githubusercontent.com/mcpplibs/openkal/$OPENKAL_VERSION"
54-
curl -fsSL --retry 3 --retry-all-errors -o SURFACE.txt "$base/SURFACE.txt"
55-
curl -fsSL --retry 3 --retry-all-errors -o check-surface.sh "$base/tools/check-surface.sh"
56-
bash check-surface.sh --complete SURFACE.txt $(find target -name '*.o' | tr '\n' ' ')
76+
# Clause 9.3. The list and the checker come from the specification
77+
# itself rather than from a copy kept here, so that the comparison has
78+
# one source. --complete because this implementation claims every
79+
# interface: a name it fails to export is a failure, not an interface
80+
# it declines to provide.
81+
bash .spec/tools/check-surface.sh --complete .spec/SURFACE.txt \
82+
$(find target -name '*.o' | tr '\n' ' ')
5783
58-
- name: The example runs
59-
working-directory: examples/hello
84+
# The program is taken from the specification rather than copied here. A
85+
# copy in each implementation is a copy that can diverge, and the value of
86+
# the program is precisely that every implementation runs the same one.
87+
#
88+
# The manifest is written here because naming the implementation is the
89+
# manifest's job and not the program's, which is the arrangement the
90+
# program exists to demonstrate.
91+
- name: The portable program runs
6092
run: |
93+
sed -i 's|^openkal = ".*"$|openkal = { path = ".spec" }|' mcpp.toml
94+
cat > .spec/examples/portable/mcpp.toml <<'TOML'
95+
[package]
96+
name = "portable"
97+
version = "0.1.0"
98+
99+
[dependencies]
100+
openkal = { path = "../.." }
101+
openkal-macos = { path = "../../.." }
102+
TOML
103+
cd .spec/examples/portable
61104
mcpp run 2>&1 | tee run.log
62-
grep -q 'openkal: hello' run.log
63-
grep -q 'openkal: allocation succeeded' run.log
64-
grep -q 'openkal: vectored writes unavailable' run.log
105+
# Both directions: that the program reported, and that nothing it
106+
# observed failed to hold. Asserting only the first would pass for a
107+
# program that printed its failures.
108+
grep -q 'openkal: the portable program, above eight interfaces' run.log
109+
grep -q 'openkal: observations that did not hold: 0' run.log
110+
! grep -q 'NOT HELD' run.log

mcpp.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
[package]
22
namespace = "mcpplibs"
33
name = "openkal-macos"
4-
version = "0.1.0"
4+
version = "0.2.0"
55
description = "An implementation of openkal for macOS. Its purpose is as much to test the specification as to be used: a specification satisfied only by the system it was written against has not been shown to be portable."
66
license = "Apache-2.0"
77
authors = ["mcpplibs"]
88
repo = "https://github.com/mcpplibs/openkal-macos"
99

1010
[dependencies]
11-
openkal = "0.3.0"
11+
openkal = "0.4.0"

src/process.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,10 @@ int kal_process_spawn(kal_dir base,
6969
if (!p.ok) return kal_err_invalid;
7070

7171
vector args, envs;
72-
args.add(p.buf, path_len); // the started program's own name
72+
// The vector is passed unaltered. Clause 7.6: argv[0] is the name the
73+
// started program observes as its own, and it is the caller's to choose —
74+
// the started program reads it through kal_env_arg(0), so a caller that did
75+
// not supply it could not predict what the program would read.
7376
for (kal_uintptr i = 0; i < argc; ++i) args.add(argv[i], argv_lens[i]);
7477
for (kal_uintptr i = 0; i < envc; ++i) envs.add(envp[i], envp_lens[i]);
7578
if (!args.ok || !envs.ok) return kal_err_no_memory;

tests/conformance_process_task.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,39 @@ int main() {
8585
check(status != 0, "a non-zero status is reported as such");
8686
kal_process_close(q);
8787
}
88+
89+
// Clause 7.6: the vector is passed unaltered, and argv[0] is the name
90+
// the started program observes as its own.
91+
//
92+
// The programs started above ignore their arguments, so they cannot
93+
// distinguish an implementation that passes the vector from one that
94+
// prepends the path — which is how that defect survived a suite that
95+
// started programs and read their statuses. A shell does distinguish
96+
// them: `sh -c <script>` takes $0 from its own argv[0] when no further
97+
// argument is given, so the script observes the name the caller chose.
98+
// An implementation that prepended the path would give the shell an
99+
// extra argument, which it would read as a script file to open, and the
100+
// status would be non-zero.
101+
const char* sh_paths[] = { "bin/sh", "usr/bin/sh" };
102+
const kal_uintptr sh_lens[] = { 6, 10 };
103+
const char* script = "test \"$0\" = openkal-observed-argv0";
104+
kal_uintptr script_len = 0; while (script[script_len]) ++script_len;
105+
106+
kal_process r{};
107+
const char* rargv[] = { "openkal-observed-argv0", "-c", script };
108+
const kal_uintptr rlens[] = { 22, 2, script_len };
109+
int rrc = kal_err_invalid;
110+
for (int i = 0; i < 2 && rrc != kal_ok; ++i)
111+
rrc = kal_process_spawn(slash, sh_paths[i], sh_lens[i], rargv, rlens, 3,
112+
nullptr, nullptr, 0, nullptr, &r);
113+
check(rrc == kal_ok, "a shell is started");
114+
if (rrc == kal_ok) {
115+
int status = -1, terminated = -1;
116+
kal_process_wait(r, &status, &terminated);
117+
check(status == 0 && terminated == 0,
118+
"the started program observes the argument vector the caller supplied, unaltered");
119+
kal_process_close(r);
120+
}
88121
}
89122

90123
// A name that ascends is refused here as it is in the file system.

0 commit comments

Comments
 (0)