Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2024 ChilliBits. All rights reserved. | ||
2 | |||
3 | #include "FileUtil.h" | ||
4 | |||
5 | #include <array> | ||
6 | #include <filesystem> | ||
7 | #include <iostream> | ||
8 | |||
9 | #include <exception/CompilerError.h> | ||
10 | #include <exception/LinkerError.h> | ||
11 | #include <util/CommonUtil.h> | ||
12 | |||
13 | namespace spice::compiler { | ||
14 | |||
15 | /** | ||
16 | * Creates a file and writes fileContent to it. | ||
17 | * | ||
18 | * @param filePath File path | ||
19 | * @param fileContent String to write into the file | ||
20 | */ | ||
21 | ✗ | void FileUtil::writeToFile(const std::filesystem::path &filePath, const std::string &fileContent) { | |
22 | ✗ | std::ofstream file(filePath); | |
23 | ✗ | if (!file) | |
24 | ✗ | throw CompilerError(IO_ERROR, "Failed to open file: " + filePath.string()); | |
25 | ✗ | file << fileContent; | |
26 | ✗ | file.flush(); | |
27 | ✗ | file.close(); | |
28 | ✗ | } | |
29 | |||
30 | /** | ||
31 | * Retrieve the contents of a file as a string | ||
32 | * | ||
33 | * @param filePath File path | ||
34 | * @return File contents as a string | ||
35 | */ | ||
36 | 561 | std::string FileUtil::getFileContent(const std::filesystem::path &filePath) { | |
37 |
1/2✓ Branch 1 taken 561 times.
✗ Branch 2 not taken.
|
561 | std::ifstream file(filePath); |
38 |
2/4✓ Branch 1 taken 561 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 561 times.
|
561 | if (!file) |
39 | ✗ | throw CompilerError(IO_ERROR, "Failed to open file: " + filePath.string()); | |
40 |
1/2✓ Branch 1 taken 561 times.
✗ Branch 2 not taken.
|
561 | std::stringstream stringStream; |
41 |
1/2✓ Branch 2 taken 561 times.
✗ Branch 3 not taken.
|
561 | stringStream << file.rdbuf(); |
42 |
1/2✓ Branch 1 taken 561 times.
✗ Branch 2 not taken.
|
561 | file.close(); |
43 |
1/2✓ Branch 1 taken 561 times.
✗ Branch 2 not taken.
|
1122 | return stringStream.str(); |
44 | 561 | } | |
45 | |||
46 | /** | ||
47 | * Retrieve the number of lines of a file | ||
48 | * | ||
49 | * @param filePath File path | ||
50 | * @return Number of lines | ||
51 | */ | ||
52 | ✗ | size_t FileUtil::getLineCount(const std::filesystem::path &filePath) { | |
53 | ✗ | std::ifstream file(filePath); | |
54 | ✗ | if (!file) | |
55 | ✗ | throw CompilerError(IO_ERROR, "Failed to open file: " + filePath.string()); | |
56 | ✗ | size_t lineCount = 0; | |
57 | ✗ | std::string line; | |
58 | ✗ | while (std::getline(file, line)) | |
59 | ✗ | lineCount++; | |
60 | ✗ | file.close(); | |
61 | ✗ | return lineCount; | |
62 | ✗ | } | |
63 | |||
64 | /** | ||
65 | * Execute external command. Used to execute compiled binaries | ||
66 | * | ||
67 | * @param command Command to execute | ||
68 | * @return Result struct | ||
69 | */ | ||
70 | 356 | ExecResult FileUtil::exec(const std::string &command) { | |
71 | #ifdef _WIN32 | ||
72 | const std::string quotedCommand = "\"" + command + "\""; | ||
73 | FILE *pipe = _popen(quotedCommand.c_str(), "r"); | ||
74 | #else | ||
75 |
1/2✓ Branch 2 taken 356 times.
✗ Branch 3 not taken.
|
356 | FILE *pipe = popen(command.c_str(), "r"); |
76 | #endif | ||
77 | |||
78 | − | if (!pipe) // GCOV_EXCL_LINE | |
79 | − | throw CompilerError(IO_ERROR, "Failed to execute command: " + command); // GCOV_EXCL_LINE | |
80 | |||
81 | 356 | std::array<char, 128> buffer{}; | |
82 |
1/2✓ Branch 1 taken 356 times.
✗ Branch 2 not taken.
|
356 | std::stringstream result; |
83 |
3/4✓ Branch 1 taken 6531 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6175 times.
✓ Branch 4 taken 356 times.
|
13418 | while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) |
84 |
1/2✓ Branch 1 taken 6175 times.
✗ Branch 2 not taken.
|
6175 | result << buffer.data(); |
85 | |||
86 | #ifdef _WIN32 | ||
87 | const int exitCode = _pclose(pipe); | ||
88 | #else | ||
89 |
1/2✓ Branch 1 taken 356 times.
✗ Branch 2 not taken.
|
356 | const int exitCode = pclose(pipe) / 256; |
90 | #endif | ||
91 | 356 | return {result.str(), exitCode}; | |
92 |
1/2✓ Branch 1 taken 356 times.
✗ Branch 2 not taken.
|
356 | } |
93 | |||
94 | /** | ||
95 | * Checks if a certain command is available on the computer | ||
96 | * | ||
97 | * @param cmd Command to search for | ||
98 | * @return Present or not | ||
99 | */ | ||
100 | ✗ | bool FileUtil::isCommandAvailable(const std::string &cmd) { | |
101 | #if OS_WINDOWS | ||
102 | const std::string checkCmd = "where " + cmd + " > nul 2>&1"; | ||
103 | #elif OS_UNIX | ||
104 | ✗ | const std::string checkCmd = "which " + cmd + " > /dev/null 2>&1"; | |
105 | #else | ||
106 | #error "Unsupported platform" | ||
107 | #endif | ||
108 | ✗ | return std::system(checkCmd.c_str()) == 0; | |
109 | ✗ | } | |
110 | |||
111 | /** | ||
112 | * Checks if Graphviz is installed on the system | ||
113 | * | ||
114 | * @return Present or not | ||
115 | */ | ||
116 | ✗ | bool FileUtil::isGraphvizInstalled() { return std::system("dot -V") == 0; } | |
117 | |||
118 | /** | ||
119 | * Search for a supported linker invoker on the system and return the executable name or path. | ||
120 | * This function may throw a LinkerError if no linker invoker is found. | ||
121 | * | ||
122 | * @return Name of path to the linker invoker executable | ||
123 | */ | ||
124 | 178 | std::string FileUtil::findLinkerInvoker() { | |
125 | #ifdef OS_UNIX | ||
126 |
2/4✓ Branch 3 taken 178 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 178 times.
✗ Branch 6 not taken.
|
356 | for (const std::string linkerInvokerName : {"clang", "gcc"}) |
127 |
2/4✓ Branch 3 taken 178 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 178 times.
✗ Branch 6 not taken.
|
356 | for (const std::string path : {"/usr/bin/", "/usr/local/bin/", "/bin/"}) |
128 |
4/8✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 178 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 178 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 178 times.
✗ Branch 12 not taken.
|
178 | if (std::filesystem::exists(path + linkerInvokerName)) |
129 |
3/6✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 178 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 178 times.
|
534 | return path + linkerInvokerName; |
130 | #elif OS_WINDOWS | ||
131 | for (const std::string linkerInvokerName : {"clang", "gcc"}) | ||
132 | if (isCommandAvailable(linkerInvokerName + " -v")) | ||
133 | return linkerInvokerName; | ||
134 | #endif | ||
135 | − | const auto msg = "No supported linker invoker was found on the system. Supported are: clang and gcc"; // LCOV_EXCL_LINE | |
136 | − | throw LinkerError(LINKER_NOT_FOUND, msg); // LCOV_EXCL_LINE | |
137 | } | ||
138 | |||
139 | /** | ||
140 | * Search for a supported linker on the system and return the executable name or path. | ||
141 | * This function may throw a LinkerError if no linker is found. | ||
142 | * | ||
143 | * @return Name of path to the linker executable | ||
144 | */ | ||
145 | 178 | std::string FileUtil::findLinker() { | |
146 | #ifdef OS_UNIX | ||
147 |
2/4✓ Branch 3 taken 178 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 178 times.
✗ Branch 6 not taken.
|
356 | for (const std::string linkerName : {"mold", "ld.lld", "lld-link", "ld64.ddl", "gold", "ld"}) |
148 |
2/4✓ Branch 3 taken 356 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 356 times.
✗ Branch 6 not taken.
|
712 | for (const std::string path : {"/usr/bin/", "/usr/local/bin/", "/bin/"}) |
149 |
5/8✓ Branch 1 taken 356 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 356 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 356 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 178 times.
✓ Branch 12 taken 178 times.
|
356 | if (std::filesystem::exists(path + linkerName)) |
150 |
4/6✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 178 times.
✓ Branch 5 taken 178 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 178 times.
|
712 | return path + linkerName; |
151 | #elif OS_WINDOWS | ||
152 | for (const std::string linkerName : {"lld", "ld"}) | ||
153 | if (isCommandAvailable(linkerName + " -v")) | ||
154 | return linkerName; | ||
155 | #endif | ||
156 | − | const auto msg = "No supported linker was found on the system. Supported are: mold, lld, gold and ld"; // LCOV_EXCL_LINE | |
157 | − | throw LinkerError(LINKER_NOT_FOUND, msg); // LCOV_EXCL_LINE | |
158 | } | ||
159 | |||
160 | /** | ||
161 | * Retrieve the dir, where the standard library lives. | ||
162 | * Returns an empty string if the std was not found. | ||
163 | * | ||
164 | * @return Std directory | ||
165 | */ | ||
166 | 600 | std::filesystem::path FileUtil::getStdDir() { | |
167 | #ifdef OS_UNIX | ||
168 |
3/6✓ Branch 1 taken 600 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 600 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 600 times.
|
600 | if (exists(std::filesystem::path("/usr/lib/spice/std/"))) |
169 | ✗ | return {"/usr/lib/spice/std/"}; | |
170 | #endif | ||
171 |
1/2✓ Branch 1 taken 600 times.
✗ Branch 2 not taken.
|
600 | if (std::getenv("SPICE_STD_DIR")) |
172 |
3/6✓ Branch 2 taken 600 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 600 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 600 times.
✗ Branch 8 not taken.
|
600 | if (const std::filesystem::path stdPath(std::getenv("SPICE_STD_DIR")); exists(stdPath)) |
173 |
2/4✓ Branch 1 taken 600 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 600 times.
|
600 | return stdPath; |
174 | − | return ""; // GCOV_EXCL_LINE | |
175 | } | ||
176 | |||
177 | /** | ||
178 | * Retrieve the dir, where the bootstrap compiler lives. | ||
179 | * Returns an empty string if the bootstrap compiler was not found. | ||
180 | * | ||
181 | * @return | ||
182 | */ | ||
183 | 11 | std::filesystem::path FileUtil::getBootstrapDir() { | |
184 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | if (std::getenv("SPICE_BOOTSTRAP_DIR")) { |
185 |
3/6✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 8 not taken.
|
11 | if (const std::filesystem::path stdPath(std::getenv("SPICE_BOOTSTRAP_DIR")); exists(stdPath)) |
186 |
2/4✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 11 times.
|
11 | return stdPath; |
187 | } | ||
188 | − | return ""; // GCOV_EXCL_LINE | |
189 | } | ||
190 | |||
191 | /** | ||
192 | * Retrieve the dir, where output binaries should go when installing them | ||
193 | * | ||
194 | * @return Installation directory | ||
195 | */ | ||
196 | 2 | std::filesystem::path FileUtil::getSpiceBinDir() { | |
197 | #if OS_WINDOWS | ||
198 | return std::filesystem::path(std::getenv("USERPROFILE")) / "spice" / "bin"; | ||
199 | #elif OS_UNIX | ||
200 | 2 | return "/usr/local/bin/"; | |
201 | #else | ||
202 | #error "Unsupported platform" | ||
203 | #endif | ||
204 | } | ||
205 | |||
206 | } // namespace spice::compiler | ||
207 |