hoshi-lang dev
Yet another programming language
Loading...
Searching...
No Matches
ccObjectLinker.cpp
Go to the documentation of this file.
1//
2// Created by XIaokang00010 on 2025/7/31.
3//
4
5#include "ccObjectLinker.h"
6#include "compiler/ir/IR.h"
7#include "share/def.hpp"
8#include <cstring>
9#include <sstream>
10#include <filesystem>
11#include <string>
12
13namespace yoi {
14
15 ccObjectLinker::ccObjectLinker(const yoi::vec<yoi::wstr> &objectPaths, const std::shared_ptr<IRBuildConfig> &config)
16 : ObjectLinker(objectPaths, config) {
17 this->setLinkerPath(L"");
18 }
19
20 bool ccObjectLinker::commandExists(const std::string& commandName) {
21 const char* path_env = std::getenv("PATH");
22 if (!path_env) {
23 return false; // PATH not set
24 }
25
26 std::string path_str(path_env);
27 std::istringstream iss(path_str);
28 std::string path_segment;
29
30#ifdef _WIN32
31 const char path_delimiter = ';';
32 const std::string executable_extension = ".exe";
33#else
34 const char path_delimiter = ':';
35 const std::string executable_extension;
36#endif
37
38 // iterate through each segment in PATH
39 while (std::getline(iss, path_segment, path_delimiter)) {
40 std::filesystem::path full_path = std::filesystem::path(path_segment) / (commandName + executable_extension);
41
42 // check if the file exists and is a regular file (i.e., not a directory)
43 if (std::filesystem::exists(full_path) && std::filesystem::is_regular_file(full_path)) {
44 return true;
45 }
46 }
47 return false;
48 }
49
50
52 if (commandExists("cc")) {
53 setLinkerPath(L"cc");
54 } else if (commandExists("gcc")) {
55 setLinkerPath(L"gcc");
56 } else if (commandExists("clang")) {
57 setLinkerPath(L"clang");
58 }
59 else {
60 throw std::runtime_error("ccObjectLinker: 'cc', 'gcc', or 'clang' not found in system PATH. Cannot setup linker.");
61 }
62 return *this;
63 }
64
66 if (this->getLinkerPath().empty()) {
67 throw std::runtime_error("ccObjectLinker: Linker path not set. Call searchAndSetupLinker() first.");
68 }
69 if (this->getObjectPaths().empty()) {
70 throw std::runtime_error("ccObjectLinker: Object file path not set.");
71 }
72
73 std::string command = "\"" + yoi::wstring2string(this->getLinkerPath()) + "\"";
74
75 // add additional linking files
76 if (strcmp(YOI_PLATFORM, "darwin") != 0)
77 // if the platform is not darwin, we need to add -Wl,--start-group and -Wl,--end-group to link as groups
78 command += " -Wl,--start-group";
79 for (const auto &file : this->getConfig()->additionalLinkingFiles) {
80 // link as groups
81 command += " \"" + yoi::wstring2string(file) + "\"";
82 }
83
84 for (const auto &objectPath : this->getObjectPaths()) {
85 command += " \"";
86 command += yoi::wstring2string(objectPath) + "\"";
87 }
88
89 if (strcmp(YOI_PLATFORM, "darwin") != 0)
90 command += " -Wl,--end-group";
91
92 command += " -o \"";
93 command += yoi::wstring2string(outputPath) + "\"";
94
95 // add Elysia runtime path and library
96 if (!this->getElysiaRuntimePath().empty()) {
97 // ensure the elysiaRuntimePath exists as a directory
98 std::filesystem::path elysia_path_fs(this->getElysiaRuntimePath());
99 if (!std::filesystem::exists(elysia_path_fs) || !std::filesystem::is_directory(elysia_path_fs)) {
100 std::string error_msg = "ccObjectLinker: Elysia runtime path does not exist or is not a directory: " +
102 throw std::runtime_error(error_msg);
103 }
104
105 command += " -L\""; // add library search path
106 command += yoi::wstring2string(this->getElysiaRuntimePath()) + "\"";
107 command += " -lelysia_runtime";
108 }
109
110 if (this->getConfig()->buildType == IRBuildConfig::BuildType::library) {
111 command += " -shared"; // build a shared library
112 }
113
114 // synchronize the release/debug
115 if (this->getConfig()->buildMode == IRBuildConfig::BuildMode::debug) {
116 command += " -g";
117 }
118#ifdef _WIN32
119 command += " -mconsole"; // fuck argc, argv
120 replace_all(command, std::string("\""), std::string("\\\""));
121 command = "powershell.exe -Command \"&" + command + "\""; // fuck win32 command line
122#endif
123 // add additional linker options
124 for (const auto &option : this->getConfig()->additionalLinkerOptions) {
125 command += " " + yoi::wstring2string(option);
126 }
127
128 int result = std::system(command.c_str());
129 if (result != 0) {
130 std::string error_msg = "ccObjectLinker: Linker command failed with exit code " + std::to_string(result) + ". Command: " + command;
131 throw std::runtime_error(error_msg);
132 }
133 return *this;
134 }
135
136} // namespace yoi
std::shared_ptr< IRBuildConfig > getConfig() const
ObjectLinker & setLinkerPath(const yoi::wstr &linkerPath)
yoi::wstr getLinkerPath() const
yoi::vec< yoi::wstr > getObjectPaths() const
yoi::wstr getElysiaRuntimePath() const
bool commandExists(const std::string &command)
ccObjectLinker(const yoi::vec< yoi::wstr > &objectPaths, const std::shared_ptr< IRBuildConfig > &config)
ObjectLinker & link(const yoi::wstr &outputPath) override
ObjectLinker & searchAndSetupLinker() override
#define YOI_PLATFORM
Definition def.hpp:31
std::string wstring2string(const std::wstring &v)
Definition def.cpp:230
void replace_all(string_t &str, const string_t &from, const string_t &to)
Definition def.hpp:379
std::vector< t > vec
Definition def.hpp:56
std::wstring wstr
Definition def.hpp:51