hoshi-lang dev
Yet another programming language
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1#include "share/defines.h"
5#include <compiler/ir/IR.h>
11#include <iostream>
12#include <llvm/Support/raw_ostream.h>
13#include <share/def.hpp>
14#include <stdexcept>
15#include <string>
16#include <vector>
17#include <filesystem>
18
19
20namespace fs = std::filesystem;
21
22
24 std::wstring platform) {
26 if (platform == L"windows") return ".exe";
27 return "";
28 } else if (type == yoi::IRBuildConfig::BuildType::library) {
29 if (platform == L"windows") return ".dll";
30 if (platform == L"darwin") return ".dylib";
31 return ".so";
32 }
33 return "";
34}
35
36
37void printUsage(const char* programName) {
38 std::cerr << "hoshi-lang compiler\n";
39 std::cout << "Made with love by Jerry Chou (This project is licensed under the MIT license.)\n";
40 std::cerr << "Usage: " << programName << " [options] <input_file> ...\n"
41 << "Options:\n"
42 << " -o <path>, --output <path> Set output file path (e.g., build/my_app).\n"
43 << " If <path> is a directory (ends with / or \\), input filename is used.\n"
44 << " If not specified, derived from input_file in the current directory.\n"
45 << " --build-type <type> Specify build type (executable, static-lib, shared-lib). Default: executable\n"
46 << " --build-mode <mode> Specify build mode (debug, release). Default: debug\n"
47 << " --linker <linker> Specify object linker (cc, cl, none). Default: cc (cl on Windows platform)\n"
48 << " 'none' will generate .o file but skip final linking.\n"
49 << " --clean, --remove-intermediate Remove intermediate files (.yoi, .ll, .o) after compilation.\n"
50 << " Default: do not preserve intermediate files.\n"
51 << " -I <path>, --include <path> Add an include directory to search for header files and dynamic libraries.\n"
52 << " -D <k> <v>, --define <k> <v> Add a macro definition.\n"
53 << " -W <key>, --warning <key> Enable warning for a specific category.\n"
54 << " -S <key>, --suppress <key> Suppress warning for a specific category.\n"
55 << " -E <key>, --error <key> Treat error for a specific category as a warning.\n"
56 << " -C <path>, --project-cache <path> Set project cache directory.\n"
57 << " --preserve-intermediate Explicitly preserve intermediate files.\n"
58 << " --whereami, -w Print the path to the hoshi-lang installation directory.\n"
59 << " --build-number Print the build number of hoshi-lang.\n"
60 << " -h, --help Display this help message.\n";
61}
62
63int main(int argc, const char **argv) {
64
65
66 std::string inputFile;
67 std::string outputPathStr;
70 yoi::wstr projectCacheDir;
71 std::wstring targetPlatform = yoi::string2wstring(YOI_PLATFORM);
72 std::wstring targetArch = yoi::string2wstring(YOI_ARCH);
74 yoi::vec<yoi::wstr> includeDirs{L"", (std::filesystem::path(yoi::whereIsHoshiLang()) / ".." / "lib").wstring()};
77 bool preserveIntermediateFiles = false;
78
79 for (int i = 1; i < argc; ++i) {
80 std::string arg = argv[i];
81
82 if (arg == "-o" || arg == "--output") {
83 if (i + 1 < argc) {
84 outputPathStr = argv[++i];
85 } else {
86 std::cerr << "Error: " << arg << " requires a path argument.\n";
87 printUsage(argv[0]);
88 return 1;
89 }
90 } else if (arg == "--build-type") {
91 if (i + 1 < argc) {
92 std::string typeStr = argv[++i];
93 if (typeStr == "executable") buildType = yoi::IRBuildConfig::BuildType::executable;
94 else if (typeStr == "library") buildType = yoi::IRBuildConfig::BuildType::library;
95 else {
96 std::cerr << "Error: Invalid build type '" << typeStr << "'. Valid types: executable, library.\n";
97 printUsage(argv[0]);
98 return 1;
99 }
100 } else {
101 std::cerr << "Error: " << arg << " requires a type argument.\n";
102 printUsage(argv[0]);
103 return 1;
104 }
105 } else if (arg == "--build-mode") {
106 if (i + 1 < argc) {
107 std::string modeStr = argv[++i];
108 if (modeStr == "debug") buildMode = yoi::IRBuildConfig::BuildMode::debug;
109 else if (modeStr == "release") buildMode = yoi::IRBuildConfig::BuildMode::release;
110 else {
111 std::cerr << "Error: Invalid build mode '" << modeStr << "'. Valid modes: debug, release.\n";
112 printUsage(argv[0]);
113 return 1;
114 }
115 } else {
116 std::cerr << "Error: " << arg << " requires a mode argument.\n";
117 printUsage(argv[0]);
118 return 1;
119 }
120 } else if (arg == "--linker") {
121 if (i + 1 < argc) {
122 std::string linkerStr = argv[++i];
123 if (linkerStr == "cc") useObjectLinker = yoi::IRBuildConfig::UseObjectLinker::cc;
124 else if (linkerStr == "cl") useObjectLinker = yoi::IRBuildConfig::UseObjectLinker::cl;
125 else if (linkerStr == "none") useObjectLinker = yoi::IRBuildConfig::UseObjectLinker::none;
126 else {
127 std::cerr << "Error: Invalid linker type '" << linkerStr << "'. Valid types: cc, cl, none.\n";
128 printUsage(argv[0]);
129 return 1;
130 }
131 } else {
132 std::cerr << "Error: " << arg << " requires a linker argument.\n";
133 printUsage(argv[0]);
134 return 1;
135 }
136 } else if (arg == "--clean" || arg == "--remove-intermediate") {
137 preserveIntermediateFiles = false;
138 } else if (arg == "-C" || arg == "--project-cache") {
139 if (i + 1 < argc) {
140 projectCacheDir = yoi::string2wstring(argv[++i]);
141 } else {
142 std::cerr << "Error: " << arg << " requires a path argument.\n";
143 printUsage(argv[0]);
144 return 1;
145 }
146 } else if (arg == "-I" || arg == "--include") {
147 yoi::wstr includeDir = yoi::string2wstring(argv[++i]);
148 includeDirs.push_back(includeDir);
149 } else if (arg == "--preserve-intermediate") {
150 preserveIntermediateFiles = true;
151 } else if (arg == "--help" || arg == "-h") {
152 printUsage(argv[0]);
153 return 0;
154 } else if (arg == "--whereami" || arg == "-w") {
156 return 0;
157 } else if (arg == "--build-number") {
158 std::cout << HOSHI_LANG_VERSION << "\n";
159 return 0;
160 } else if (arg == "-D" || arg == "--define") {
161 if (i + 2 < argc) {
162 yoi::wstr key = yoi::string2wstring(argv[++i]);
163 yoi::wstr value = yoi::string2wstring(argv[++i]);
164 macroDefs.emplace_back(key, value);
165 } else {
166 std::cerr << "Error: " << arg << " requires two arguments.\n";
167 printUsage(argv[0]);
168 return 1;
169 }
170 } else if (arg == "-W" || arg == "--warning") {
171 if (i + 1 < argc) {
172 std::string key = argv[++i];
174 } else {
175 std::cerr << "Error: " << arg << " requires one arguments.\n";
176 printUsage(argv[0]);
177 return 1;
178 }
179 } else if (arg == "-S" || arg == "--suppress") {
180 if (i + 1 < argc) {
181 std::string key = argv[++i];
183 } else {
184 std::cerr << "Error: " << arg << " requires one arguments.\n";
185 printUsage(argv[0]);
186 return 1;
187 }
188 } else if (arg == "-E" || arg == "--error") {
189 if (i + 1 < argc) {
190 std::string key = argv[++i];
192 } else {
193 std::cerr << "Error: " << arg << " requires one arguments.\n";
194 printUsage(argv[0]);
195 return 1;
196 }
197 } else if (!arg.starts_with("-")) {
198 if (arg.ends_with(".hoshi") && inputFile.empty())
199 inputFile = arg;
200 else if (arg.ends_with(".hoshi") && !inputFile.empty()) {
201 std::cerr << "Warning: Multiple input files specified: " << inputFile << " and " << arg << "\n";
202 printUsage(argv[0]);
203 return 1;
204 }
205 else
206 additionalLinkingFiles.push_back(yoi::string2wstring(arg));
207 } else {
208 std::cerr << "Error: Unknown argument: " << arg << "\n";
209 printUsage(argv[0]);
210 return 1;
211 }
212 }
213
214
215 if (inputFile.empty()) {
216 std::cerr << "Error: No input file provided.\n";
217 printUsage(argv[0]);
218 return 1;
219 }
220 if (!fs::exists(inputFile)) {
221 std::cerr << "Error: Input file '" << inputFile << "' does not exist.\n";
222 return 1;
223 }
224 if (!fs::is_regular_file(inputFile)) {
225 std::cerr << "Error: Input file '" << inputFile << "' is not a regular file.\n";
226 return 1;
227 }
228
229
230 fs::path inputFilePath(inputFile);
231 fs::path outputDir;
232 fs::path outputBaseName;
233
234 if (outputPathStr.empty()) {
235
236 outputDir = fs::current_path();
237 outputBaseName = inputFilePath.stem();
238 } else {
239 fs::path specifiedOutputPath(outputPathStr);
240 if (specifiedOutputPath.has_filename()) {
241
242 outputDir = specifiedOutputPath.parent_path();
243 outputBaseName = specifiedOutputPath.stem();
244 } else {
245 outputDir = specifiedOutputPath;
246 outputBaseName = inputFilePath.stem();
247 }
248 }
249
250
251 try {
252 if (!outputDir.empty() && !fs::exists(outputDir)) {
253 fs::create_directories(outputDir);
254 }
255 } catch (const fs::filesystem_error& e) {
256 std::cerr << "Error: Could not create output directory '" << outputDir.string() << "': " << e.what() << "\n";
257 return 1;
258 }
259
260
261 fs::path yoiIRFile = outputDir / (outputBaseName.string() + ".yoi");
262 fs::path llvmIRFile = outputDir / (outputBaseName.string() + ".ll");
263 fs::path objectFile = outputDir / (outputBaseName.string() + ".o");
264 fs::path finalOutput = outputDir / (outputBaseName.string() + getOutputExtension(buildType, targetPlatform));
265
266
267 std::vector<fs::path> intermediateFilesToClean;
268 if (!preserveIntermediateFiles) {
269 intermediateFilesToClean.push_back(yoiIRFile);
270 intermediateFilesToClean.push_back(llvmIRFile);
271 intermediateFilesToClean.push_back(objectFile);
272 }
273
274 int exitCode = 0;
275
276
277 std::shared_ptr<yoi::compilerContext> compilerCtx =
278 std::make_shared<yoi::compilerContext>();
279 try {
280 compilerCtx->initializeSharedObjects();
281
282
283 compilerCtx->setBuildConfig(yoi::IRBuildConfig::Builder()
284 .setBuildType(buildType)
285 .setBuildPlatform(yoi::string2wstring(YOI_PLATFORM))
286 .setBuildMode(buildMode)
287 .setBuildArch(yoi::string2wstring(YOI_ARCH))
288 .setUseObjectLinker(useObjectLinker)
289 .setPreserveIntermediateFiles(preserveIntermediateFiles)
290 .setImmediatelyClearupCache(projectCacheDir.empty())
291 .setSearchPaths(includeDirs)
292 .setBuildCachePath(projectCacheDir)
295 .setMarco(L"hoshi_feature_version", yoi::string2wstring(HOSHI_LANG_VERSION))
297 .setAdditionalLinkingFiles(additionalLinkingFiles)
298 .yield());
299
300 for (auto &macro : macroDefs) {
301 compilerCtx->getBuildConfig()->marcos[macro.first] = macro.second;
302 }
303
304 yoi::wstr input = yoi::string2wstring(inputFile);
305
306 auto entryModuleId = compilerCtx->compileModule(input);
307 compilerCtx->runOptimizer();
308
309 std::cout << "Linking Yoi IR modules...\n";
310 yoi::IRLinker linker;
311 auto objectIRFile = linker.link(compilerCtx, entryModuleId);
312 compilerCtx->setIRObjectFile(objectIRFile);
313 auto unifiedModule = objectIRFile->compiledModule;
314
315 auto yoiIRStr = unifiedModule->to_string();
316 std::error_code ec_yoi;
317 llvm::raw_fd_stream yoi_file(yoiIRFile.string(), ec_yoi);
318 if (ec_yoi) {
319 throw std::runtime_error("Could not open YOI IR output file '" + yoiIRFile.string() + "': " + ec_yoi.message());
320 }
321 yoi_file << yoi::wstring2string(yoiIRStr);
322 yoi_file.close();
323
324 yoi::LLVMCodegen llvmCodegen(compilerCtx, unifiedModule);
325 std::cout << "Generating target object files...\n";
326
327 yoi::vec<yoi::wstr> objectFileNames;
328
329 TIMER("Generating target object files", objectFileNames = llvmCodegen.generate());
330
331 if (preserveIntermediateFiles) {
332 // For debug verification, we print the IR of the main input module
333 llvmCodegen.dumpIR(L"builtin", llvmIRFile.string());
334 }
335
336 if (useObjectLinker != yoi::IRBuildConfig::UseObjectLinker::none) {
337 yoi::ObjectLinker *objectLinker = nullptr;
338 switch (useObjectLinker) {
340 objectLinker = new yoi::ccObjectLinker(objectFileNames, compilerCtx->getBuildConfig());
341 break;
342 }
344 objectLinker = new yoi::clObjectLinker(objectFileNames, compilerCtx->getBuildConfig());
345 break;
346 }
347 default:
348 break;
349 }
350
351 if (objectLinker) {
352 std::cout << "Linking final " << yoi::wstring2string(targetPlatform) << " "
353 << (buildType == yoi::IRBuildConfig::BuildType::executable ? "executable" : "library")
354 << "...\n";
355 objectLinker->searchAndSetupLinker();
357 objectLinker->link(yoi::string2wstring(finalOutput.string()));
358 delete objectLinker;
359 }
360 } else {
361 std::cout << "Object linking skipped (--linker none).\n";
362 }
363 std::cout << "Compilation successful!\n";
364
365 if (compilerCtx->getBuildConfig()->immediatelyClearupCache) {
366 std::error_code ec_remove;
367 std::filesystem::remove_all(compilerCtx->getBuildConfig()->buildCachePath, ec_remove);
368 if (ec_remove) {
369 std::cerr << "Warning: Could not remove cache file '" << yoi::wstring2string(compilerCtx->getBuildConfig()->buildCachePath) << "': " << ec_remove.message() << "\n";
370 }
371 }
372 } catch (const std::logic_error &e) {
373 std::cerr << "Error: " << e.what() << std::endl;
374 exitCode = 1;
375 } /* catch (const std::exception& e) {
376 std::cerr << "An unexpected error occurred: " << e.what() << std::endl;
377 exitCode = 1;
378 } */
379
380 if (!preserveIntermediateFiles && exitCode == 0) {
381 for (const auto& file : intermediateFilesToClean) {
382 std::error_code ec_remove;
383 fs::remove(file, ec_remove);
384 if (ec_remove) {
385 std::cerr << "Warning: Could not remove intermediate file '" << file.string() << "': " << ec_remove.message() << "\n";
386 }
387 }
388 } else if (preserveIntermediateFiles) {
389 std::cout << "Intermediate files preserved.\n";
390 }
391
392 return exitCode;
393}
std::shared_ptr< IRObjectFile > link(const std::shared_ptr< compilerContext > &context, indexT entryModuleId)
Links all compiled modules from the context into a single IRObjectFile.
Definition IRLinker.cpp:18
yoi::vec< yoi::wstr > generate()
void dumpIR(const yoi::wstr &modulePath, const std::string &filename)
static yoi::vec< yoi::wstr > defaultAdditionalLinkingFiles()
ObjectLinker & setElysiaRuntimePath(const yoi::wstr &elysiaRuntimePath)
virtual ObjectLinker & link(const yoi::wstr &outputPath)=0
virtual ObjectLinker & searchAndSetupLinker()=0
#define YOI_PLATFORM
Definition def.hpp:31
#define YOI_ARCH
Definition def.hpp:44
#define HOSHI_LANG_VERSION
Definition defines.h:4
#define HOSHI_LANG_GIT_COMMIT_HASH
Definition defines.h:5
#define TIMER(X, Y)
int main()
Definition loop.cpp:3
std::string getOutputExtension(yoi::IRBuildConfig::BuildType type, std::wstring platform)
Definition main.cpp:23
void printUsage(const char *programName)
Definition main.cpp:37
std::string wstring2string(const std::wstring &v)
Definition def.cpp:184
std::vector< t > vec
Definition def.hpp:53
std::wstring string2wstring(const std::string &v)
Definition def.cpp:178
yoi::wstr realpath(const std::wstring &path)
Definition def.cpp:190
std::wstring whereIsHoshiLang()
Definition def.cpp:200
std::wstring wstr
Definition def.hpp:48
std::map< std::string, ExceptionHandleType > exception_categories
Definition def.cpp:17
Builder & setBuildCachePath(const yoi::wstr &buildCachePath)
Definition IR.cpp:1613
std::shared_ptr< IRBuildConfig > yield()
Definition IR.cpp:905
Builder & setAdditionalLinkingFiles(const yoi::vec< yoi::wstr > &additionalLinkingFiles)
Definition IR.cpp:1546
Builder & setSearchPaths(const yoi::vec< yoi::wstr > &searchPaths)
Definition IR.cpp:1060
Builder & setMarco(const yoi::wstr &name, const yoi::wstr &value)
Definition IR.cpp:1476