Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2025 ChilliBits. All rights reserved. | ||
2 | |||
3 | #include "ExternalLinkerInterface.h" | ||
4 | #include "util/Timer.h" | ||
5 | |||
6 | #include <iostream> | ||
7 | |||
8 | #include <exception/CompilerError.h> | ||
9 | #include <exception/LinkerError.h> | ||
10 | #include <util/FileUtil.h> | ||
11 | |||
12 | namespace spice::compiler { | ||
13 | |||
14 | 187 | void ExternalLinkerInterface::prepare() { | |
15 | // Set target to linker | ||
16 |
2/4✓ Branch 0 (2→3) taken 187 times.
✗ Branch 1 (2→50) not taken.
✓ Branch 2 (3→4) taken 187 times.
✗ Branch 3 (3→48) not taken.
|
187 | addLinkerFlag("--target=" + cliOptions.targetTriple); |
17 | |||
18 | // Static linking | ||
19 |
1/2✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→13) taken 187 times.
|
187 | if (cliOptions.staticLinking) |
20 | ✗ | addLinkerFlag("-static"); | |
21 | |||
22 | // Stripping symbols | ||
23 |
2/2✓ Branch 0 (13→14) taken 185 times.
✓ Branch 1 (13→21) taken 2 times.
|
187 | if (!cliOptions.generateDebugInfo) |
24 |
2/4✓ Branch 0 (16→17) taken 185 times.
✗ Branch 1 (16→59) not taken.
✓ Branch 2 (17→18) taken 185 times.
✗ Branch 3 (17→57) not taken.
|
370 | addLinkerFlag("-Wl,-s"); |
25 | |||
26 | // Web Assembly | ||
27 |
3/6✓ Branch 0 (22→23) taken 187 times.
✗ Branch 1 (22→25) not taken.
✗ Branch 2 (24→25) not taken.
✓ Branch 3 (24→26) taken 187 times.
✗ Branch 4 (27→28) not taken.
✓ Branch 5 (27→47) taken 187 times.
|
187 | if (cliOptions.targetArch == TARGET_WASM32 || cliOptions.targetArch == TARGET_WASM64) { |
28 | ✗ | addLinkerFlag("-nostdlib"); | |
29 | ✗ | addLinkerFlag("-Wl,--no-entry"); | |
30 | ✗ | addLinkerFlag("-Wl,--export-all"); | |
31 | } | ||
32 | 187 | } | |
33 | |||
34 | /** | ||
35 | * Start the linking process | ||
36 | */ | ||
37 | 187 | void ExternalLinkerInterface::link() const { | |
38 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 187 times.
|
187 | assert(!outputPath.empty()); |
39 | |||
40 | // Build the linker command | ||
41 |
1/2✓ Branch 0 (5→6) taken 187 times.
✗ Branch 1 (5→104) not taken.
|
187 | std::stringstream linkerCommandBuilder; |
42 |
1/2✓ Branch 0 (6→7) taken 187 times.
✗ Branch 1 (6→102) not taken.
|
187 | const auto [linkerInvokerName, linkerInvokerPath] = FileUtil::findLinkerInvoker(); |
43 |
1/2✓ Branch 0 (7→8) taken 187 times.
✗ Branch 1 (7→100) not taken.
|
187 | linkerCommandBuilder << linkerInvokerPath; |
44 |
1/2✓ Branch 0 (8→9) taken 187 times.
✗ Branch 1 (8→100) not taken.
|
187 | const auto [linkerName, linkerPath] = FileUtil::findLinker(cliOptions); |
45 |
2/4✓ Branch 0 (9→10) taken 187 times.
✗ Branch 1 (9→98) not taken.
✓ Branch 2 (10→11) taken 187 times.
✗ Branch 3 (10→98) not taken.
|
187 | linkerCommandBuilder << " -fuse-ld=" << linkerPath; |
46 | // Append linker flags | ||
47 |
2/2✓ Branch 0 (18→13) taken 798 times.
✓ Branch 1 (18→19) taken 187 times.
|
985 | for (const std::string &linkerFlag : linkerFlags) |
48 |
2/4✓ Branch 0 (14→15) taken 798 times.
✗ Branch 1 (14→77) not taken.
✓ Branch 2 (15→16) taken 798 times.
✗ Branch 3 (15→77) not taken.
|
798 | linkerCommandBuilder << " " << linkerFlag; |
49 | // Append output path | ||
50 |
3/6✓ Branch 0 (19→20) taken 187 times.
✗ Branch 1 (19→98) not taken.
✓ Branch 2 (20→21) taken 187 times.
✗ Branch 3 (20→80) not taken.
✓ Branch 4 (21→22) taken 187 times.
✗ Branch 5 (21→78) not taken.
|
187 | linkerCommandBuilder << " -o " << outputPath.string(); |
51 | // Append object files | ||
52 |
2/2✓ Branch 0 (30→25) taken 717 times.
✓ Branch 1 (30→31) taken 187 times.
|
904 | for (const std::string &objectFilePath : objectFilePaths) |
53 |
2/4✓ Branch 0 (26→27) taken 717 times.
✗ Branch 1 (26→81) not taken.
✓ Branch 2 (27→28) taken 717 times.
✗ Branch 3 (27→81) not taken.
|
717 | linkerCommandBuilder << " " << objectFilePath; |
54 | |||
55 | // Print status message | ||
56 |
1/2✗ Branch 0 (31→32) not taken.
✓ Branch 1 (31→43) taken 187 times.
|
187 | if (cliOptions.printDebugOutput) { |
57 | − | std::cout << "\nLinking with: " << linkerInvokerName << " (invoker) / " << linkerName << " (linker)"; // GCOV_EXCL_LINE | |
58 | − | std::cout << "\nEmitting executable to path: " << outputPath.string() << "\n"; // GCOV_EXCL_LINE | |
59 | } | ||
60 | |||
61 | // Call the linker | ||
62 |
1/2✓ Branch 0 (43→44) taken 187 times.
✗ Branch 1 (43→98) not taken.
|
187 | Timer timer; |
63 |
1/2✓ Branch 0 (44→45) taken 187 times.
✗ Branch 1 (44→98) not taken.
|
187 | timer.start(); |
64 |
1/2✓ Branch 0 (45→46) taken 187 times.
✗ Branch 1 (45→98) not taken.
|
187 | const std::string linkerCommand = linkerCommandBuilder.str(); |
65 |
1/2✓ Branch 0 (46→47) taken 187 times.
✗ Branch 1 (46→96) not taken.
|
187 | const auto [output, exitCode] = FileUtil::exec(linkerCommand); |
66 |
1/2✓ Branch 0 (47→48) taken 187 times.
✗ Branch 1 (47→94) not taken.
|
187 | timer.stop(); |
67 | |||
68 | // Check for linker error | ||
69 | − | if (exitCode != 0) // GCOV_EXCL_LINE | |
70 | − | throw LinkerError(LINKER_ERROR, "Linker exited with non-zero exit code"); // GCOV_EXCL_LINE | |
71 | |||
72 | // Print linker result if appropriate | ||
73 | − | if (cliOptions.printDebugOutput && !output.empty()) // GCOV_EXCL_LINE | |
74 | − | std::cout << "Linking result: " << output << "\n\n"; // GCOV_EXCL_LINE | |
75 | |||
76 | // Print link time | ||
77 | − | if (cliOptions.printDebugOutput) // GCOV_EXCL_LINE | |
78 | − | std::cout << "Total link time: " << timer.getDurationMilliseconds() << " ms\n\n"; // GCOV_EXCL_LINE | |
79 | 187 | } | |
80 | |||
81 | /** | ||
82 | * Add another object file to be linked when calling 'link()' | ||
83 | * | ||
84 | * @param objectFilePath Path to the object file | ||
85 | */ | ||
86 | 753 | void ExternalLinkerInterface::addObjectFilePath(const std::string &objectFilePath) { objectFilePaths.push_back(objectFilePath); } | |
87 | |||
88 | /** | ||
89 | * Add another linker flag for the call to the linker executable | ||
90 | * | ||
91 | * @param flag Linker flag | ||
92 | */ | ||
93 | 799 | void ExternalLinkerInterface::addLinkerFlag(const std::string &flag) { linkerFlags.push_back(flag); } | |
94 | |||
95 | /** | ||
96 | * Add another source file to compile and link in (C or C++) | ||
97 | * | ||
98 | * @param additionalSource Additional source file | ||
99 | */ | ||
100 | 2 | void ExternalLinkerInterface::addAdditionalSourcePath(std::filesystem::path additionalSource) { | |
101 | // Check if the file exists | ||
102 | − | if (!exists(additionalSource)) { // GCOV_EXCL_LINE | |
103 | − | const std::string msg = "The additional source file '" + additionalSource.string() + "' does not exist"; // GCOV_EXCL_LINE | |
104 | − | throw CompilerError(IO_ERROR, msg); // GCOV_EXCL_LINE | |
105 | − | } // GCOV_EXCL_LINE | |
106 | |||
107 | // Add the file to the linker | ||
108 | 2 | additionalSource.make_preferred(); | |
109 |
2/4✓ Branch 0 (13→14) taken 2 times.
✗ Branch 1 (13→31) not taken.
✓ Branch 2 (14→15) taken 2 times.
✗ Branch 3 (14→29) not taken.
|
2 | addObjectFilePath(additionalSource.string()); |
110 | 2 | } | |
111 | |||
112 | } // namespace spice::compiler | ||
113 |