Skip to content

Commit 335933a

Browse files
authored
Merge pull request github#13071 from github/redsun82/swift-xcode-failure-diag
Swift: add autobuild failure diagnostics
2 parents bcdd839 + c677c04 commit 335933a

25 files changed

+638
-61
lines changed

swift/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ A log file is produced for each run under `CODEQL_EXTRACTOR_SWIFT_LOG_DIR` (the
5252
You can use the environment variable `CODEQL_EXTRACTOR_SWIFT_LOG_LEVELS` to configure levels for
5353
loggers and outputs. This must have the form of a comma separated `spec:min_level` list, where
5454
`spec` is either a glob pattern (made up of alphanumeric, `/`, `*` and `.` characters) for
55-
matching logger names or one of `out:bin`, `out:text` or `out:console`, and `min_level` is one
55+
matching logger names or one of `out:binary`, `out:text`, `out:console` or `out:diagnostics`, and `min_level` is one
5656
of `trace`, `debug`, `info`, `warning`, `error`, `critical` or `no_logs` to turn logs completely off.
5757

5858
Current output default levels are no binary logs, `info` logs or higher in the text file and `warning` logs or higher on

swift/extractor/infra/SwiftDispatcher.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <swift/AST/SourceFile.h>
66
#include <swift/Basic/SourceManager.h>
77
#include <swift/Parse/Token.h>
8+
#include "absl/strings/str_cat.h"
89

910
#include "swift/extractor/trap/TrapDomain.h"
1011
#include "swift/extractor/infra/SwiftTagTraits.h"
@@ -83,7 +84,7 @@ class SwiftDispatcher {
8384
valid = false;
8485
}
8586
LOG_ERROR("{} has undefined field {}{}, {}", entry.NAME, field,
86-
index >= 0 ? ('[' + std::to_string(index) + ']') : "", action);
87+
index >= 0 ? absl::StrCat("[", index, "]") : "", action);
8788
}
8889
});
8990
if (valid) {

swift/extractor/infra/SwiftMangledName.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "swift/extractor/infra/SwiftMangledName.h"
2+
#include "absl/strings/str_cat.h"
23

34
namespace codeql {
45

@@ -8,13 +9,11 @@ void appendPart(std::string& out, const std::string& prefix) {
89
}
910

1011
void appendPart(std::string& out, UntypedTrapLabel label) {
11-
out += '{';
12-
out += label.str();
13-
out += '}';
12+
absl::StrAppend(&out, "{", label.str(), "}");
1413
}
1514

1615
void appendPart(std::string& out, unsigned index) {
17-
out += std::to_string(index);
16+
absl::StrAppend(&out, index);
1817
}
1918

2019
} // namespace

swift/extractor/main.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <swift/Basic/InitializeSwiftModules.h>
1111

1212
#include "absl/strings/str_join.h"
13+
#include "absl/strings/str_cat.h"
1314

1415
#include "swift/extractor/SwiftExtractor.h"
1516
#include "swift/extractor/infra/TargetDomains.h"
@@ -166,7 +167,7 @@ static void checkWhetherToRunUnderTool(int argc, char* const* argv) {
166167
// compilations, diagnostics, etc.
167168
codeql::TrapDomain invocationTrapDomain(codeql::SwiftExtractorState& state) {
168169
auto timestamp = std::chrono::system_clock::now().time_since_epoch().count();
169-
auto filename = std::to_string(timestamp) + '-' + std::to_string(getpid());
170+
auto filename = absl::StrCat(timestamp, "-", getpid());
170171
auto target = std::filesystem::path("invocations") / std::filesystem::path(filename);
171172
auto maybeDomain = codeql::createTargetTrapDomain(state, target, codeql::TrapType::invocation);
172173
CODEQL_ASSERT(maybeDomain, "Cannot create invocation trap file for {}", target);

swift/extractor/trap/TrapDomain.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <memory>
44
#include <sstream>
5+
#include "absl/strings/str_cat.h"
56

67
#include "swift/extractor/trap/TrapLabel.h"
78
#include "swift/extractor/infra/file/TargetFile.h"
@@ -27,7 +28,7 @@ class TrapDomain {
2728
e.forEachLabel([&e, this](const char* field, int index, auto& label) {
2829
if (!label.valid()) {
2930
LOG_ERROR("{} has undefined field {}{}", e.NAME, field,
30-
index >= 0 ? ('[' + std::to_string(index) + ']') : "");
31+
index >= 0 ? absl::StrCat("[", index, "]") : "");
3132
}
3233
});
3334
}
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,34 @@
11
"""
2-
recreation of internal `create_database_utils.py` to run the tests locally, with minimal
3-
and swift-specialized functionality
2+
Simplified version of internal `create_database_utils.py` used to run the tests locally, with
3+
minimal and swift-specialized functionality
4+
TODO unify integration testing code across the public and private repositories
45
"""
56
import subprocess
67
import pathlib
78
import sys
9+
import shutil
810

911

10-
def run_codeql_database_create(cmds, lang, keep_trap=True):
12+
def runSuccessfully(cmd):
13+
res = subprocess.run(cmd)
14+
if res.returncode:
15+
print("FAILED", file=sys.stderr)
16+
print(" ", *cmd, f"(exit code {res.returncode})", file=sys.stderr)
17+
sys.exit(res.returncode)
18+
19+
def runUnsuccessfully(cmd):
20+
res = subprocess.run(cmd)
21+
if res.returncode == 0:
22+
print("FAILED", file=sys.stderr)
23+
print(" ", *cmd, f"(exit code 0, expected to fail)", file=sys.stderr)
24+
sys.exit(1)
25+
26+
27+
def run_codeql_database_create(cmds, lang, keep_trap=True, db=None, runFunction=runSuccessfully):
28+
""" db parameter is here solely for compatibility with the internal test runner """
1129
assert lang == 'swift'
1230
codeql_root = pathlib.Path(__file__).parents[2]
31+
shutil.rmtree("db", ignore_errors=True)
1332
cmd = [
1433
"codeql", "database", "create",
1534
"-s", ".", "-l", "swift", "--internal-use-lua-tracing", f"--search-path={codeql_root}", "--no-cleanup",
@@ -19,8 +38,4 @@ def run_codeql_database_create(cmds, lang, keep_trap=True):
1938
for c in cmds:
2039
cmd += ["-c", c]
2140
cmd.append("db")
22-
res = subprocess.run(cmd)
23-
if res.returncode:
24-
print("FAILED", file=sys.stderr)
25-
print(" ", *cmd, file=sys.stderr)
26-
sys.exit(res.returncode)
41+
runFunction(cmd)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""
2+
Simplified POSIX only version of internal `diagnostics_test_utils.py` used to run the tests locally
3+
TODO unify integration testing code across the public and private repositories
4+
"""
5+
6+
import json
7+
import pathlib
8+
import subprocess
9+
import os
10+
import difflib
11+
import sys
12+
13+
14+
def _normalize_actual(test_dir, database_dir):
15+
proc = subprocess.run(['codeql', 'database', 'export-diagnostics', '--format', 'raw', '--', database_dir],
16+
stdout=subprocess.PIPE, universal_newlines=True, check=True, text=True)
17+
data = proc.stdout.replace(str(test_dir.absolute()), "<test-root-directory>")
18+
data = json.loads(data)
19+
data[:] = [e for e in data if not e["source"]["id"].startswith("cli/")]
20+
for e in data:
21+
e.pop("timestamp")
22+
return _normalize_json(data)
23+
24+
25+
def _normalize_expected(test_dir):
26+
with open(test_dir / "diagnostics.expected") as expected:
27+
text = expected.read()
28+
return _normalize_json(_load_concatenated_json(text))
29+
30+
31+
def _load_concatenated_json(text):
32+
text = text.lstrip()
33+
entries = []
34+
decoder = json.JSONDecoder()
35+
while text:
36+
obj, index = decoder.raw_decode(text)
37+
entries.append(obj)
38+
text = text[index:].lstrip()
39+
return entries
40+
41+
42+
def _normalize_json(data):
43+
entries = [json.dumps(e, sort_keys=True, indent=2) for e in data]
44+
entries.sort()
45+
entries.append("")
46+
return "\n".join(entries)
47+
48+
49+
def check_diagnostics(test_dir=".", test_db="db"):
50+
test_dir = pathlib.Path(test_dir)
51+
test_db = pathlib.Path(test_db)
52+
actual = _normalize_actual(test_dir, test_db)
53+
if os.environ.get("CODEQL_INTEGRATION_TEST_LEARN") == "true":
54+
with open(test_dir / "diagnostics.expected", "w") as expected:
55+
expected.write(actual)
56+
return
57+
expected = _normalize_expected(test_dir)
58+
if actual != expected:
59+
with open(test_dir / "diagnostics.actual", "w") as actual_out:
60+
actual_out.write(actual)
61+
actual = actual.splitlines(keepends=True)
62+
expected = expected.splitlines(keepends=True)
63+
print("".join(difflib.unified_diff(actual, expected, fromfile="diagnostics.actual", tofile="diagnostics.expected")), file=sys.stderr)
64+
sys.exit(1)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"helpLinks": [
3+
"https://docs.github.com/en/enterprise-server/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/customizing-code-scanning",
4+
"https://docs.github.com/en/enterprise-server/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages#adding-build-steps-for-a-compiled-language"
5+
],
6+
"plaintextMessage": "The detected build command failed (tried /usr/bin/xcodebuild build -project <test-root-directory>/hello-failure.xcodeproj -target hello-failure CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO).\n\nSet up a manual build command.",
7+
"severity": "error",
8+
"source": {
9+
"extractorName": "swift",
10+
"id": "swift/autobuilder/build-command-failed",
11+
"name": "Detected build command failed"
12+
},
13+
"visibility": {
14+
"cliSummaryTable": true,
15+
"statusPage": true,
16+
"telemetry": true
17+
}
18+
}

0 commit comments

Comments
 (0)