GCC Code Coverage Report


Directory: ../
File: src/util/FileUtil.cpp
Date: 2024-12-24 01:17:15
Exec Total Coverage
Lines: 38 64 59.4%
Functions: 7 11 63.6%
Branches: 54 152 35.5%

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