1 /**
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <algorithm>
17
18 #include "libpandabase/os/file.h"
19 #include "libpandabase/utils/pandargs.h"
20 #include "libpandabase/utils/logger.h"
21 #include "generated/link_options.h"
22
23 #include "linker.h"
24
25 namespace {
26
PrintHelp(const ark::PandArgParser & paParser)27 int PrintHelp(const ark::PandArgParser &paParser)
28 {
29 const auto a = paParser.GetErrorString();
30 if (!a.empty()) {
31 std::cerr << "Error: " << a << std::endl;
32 }
33
34 std::cerr << "Usage:" << std::endl;
35 std::cerr << "ark_link [OPTIONS] -- FILES..." << std::endl << std::endl;
36 std::cerr << "Supported options:" << std::endl << std::endl;
37 std::cerr << paParser.GetHelpString() << std::endl;
38 return 1;
39 }
40
MangleClass(std::string s)41 std::string MangleClass(std::string s)
42 {
43 s.insert(s.begin(), 'L');
44 s += ";";
45 return s;
46 }
47
SplitContentByLine(const std::string & input,std::vector<std::string> & lines)48 bool SplitContentByLine(const std::string &input, std::vector<std::string> &lines)
49 {
50 std::ifstream ifs;
51 std::string line;
52 ifs.open(input);
53 if (!ifs.is_open()) {
54 std::cerr << "Failed to open source list file '" << input << "' during input file collection." << std::endl
55 << "Please check if the file exists or the path is correct, "
56 << "and you have the necessary permissions to access it." << std::endl;
57 return false;
58 }
59
60 while (std::getline(ifs, line)) {
61 lines.push_back(line);
62 }
63 ifs.close();
64 return true;
65 }
66
CollectAbcFiles(const std::vector<std::string> & files,std::vector<std::string> & abcFiles)67 bool CollectAbcFiles(const std::vector<std::string> &files, std::vector<std::string> &abcFiles)
68 {
69 for (const auto &file : files) {
70 if (file.length() > 0 && file[0] == '@') {
71 auto inputAbs = ark::os::file::File::GetAbsolutePath(file.substr(1));
72 if (!inputAbs) {
73 std::cerr << "Failed to find file '" << file.substr(1) << "' during input file resolution" << std::endl
74 << "Please check if the file name is correct, the file exists at the specified path, "
75 << "and your project has the necessary permissions to access it." << std::endl;
76 return false;
77 }
78 auto fpath = inputAbs.Value();
79 std::vector<std::string> lines;
80 if (!SplitContentByLine(fpath, lines)) {
81 return false;
82 }
83
84 std::move(lines.begin(), lines.end(), std::back_inserter(abcFiles));
85 } else {
86 abcFiles.push_back(file);
87 }
88 }
89 return true;
90 }
91
ReadDependenciesFromConfig(const ark::static_linker::Options & options,ark::static_linker::Config & conf)92 bool ReadDependenciesFromConfig(const ark::static_linker::Options &options, ark::static_linker::Config &conf)
93 {
94 if (options.IsStripUnused() && options.GetStripUnusedSkiplist().empty()) {
95 // treat all classes as entry point
96 conf.allFileIsEntry = true;
97 return true;
98 }
99 if (!options.GetStripUnusedSkiplist().empty() && !options.IsStripUnused()) {
100 std::cerr << "Please set `strip-unused` if you want to strip abc." << std::endl;
101 return false;
102 }
103
104 auto inputs = options.GetStripUnusedSkiplist();
105 if (inputs.size() == 1) {
106 std::string fileListName = inputs[0];
107 auto dotPos = fileListName.rfind('.');
108 if (dotPos == std::string::npos || fileListName[0] != '@') {
109 return true;
110 }
111 auto suffix = fileListName.substr(dotPos + 1);
112 if (suffix != "txt") {
113 return true;
114 }
115
116 std::ifstream ifs;
117 std::string line;
118 auto input = fileListName.substr(1);
119 conf.entryNames.clear();
120 ifs.open(input);
121 if (!ifs.is_open()) {
122 std::cerr << "Failed to open config file '" << input << "'" << std::endl;
123 return false;
124 }
125
126 while (std::getline(ifs, line)) {
127 conf.entryNames.insert(line);
128 }
129 ifs.close();
130 }
131 return true;
132 }
133
134 } // namespace
135
main(int argc,const char * argv[])136 int main(int argc, const char *argv[])
137 {
138 ark::PandArgParser paParser;
139 ark::static_linker::Options options {*argv};
140 options.AddOptions(&paParser);
141 paParser.EnableRemainder();
142
143 if (!paParser.Parse(argc, argv)) {
144 return PrintHelp(paParser);
145 }
146
147 ark::Logger::InitializeStdLogging(
148 ark::Logger::LevelFromString(options.GetLogLevel()),
149 ark::Logger::ComponentMask().set(ark::Logger::Component::STATIC_LINKER).set(ark::Logger::Component::PANDAFILE));
150
151 const auto files = paParser.GetRemainder();
152 std::vector<std::string> abcFiles;
153 if (!CollectAbcFiles(files, abcFiles)) {
154 return 1;
155 }
156 if (abcFiles.empty()) {
157 std::cerr << "must have at least one file" << std::endl;
158 return PrintHelp(paParser);
159 }
160
161 if (options.GetOutput().empty()) {
162 auto const &fn = abcFiles[0];
163 options.SetOutput(fn.substr(0, fn.find_last_of('.')) + ".linked.abc");
164 }
165
166 auto conf = ark::static_linker::DefaultConfig();
167
168 conf.stripDebugInfo = options.IsStripDebugInfo();
169
170 auto classesVecToSet = [](const std::vector<std::string> &v, std::set<std::string> &s) {
171 s.clear();
172 std::transform(v.begin(), v.end(), std::inserter(s, s.begin()), MangleClass);
173 };
174
175 classesVecToSet(options.GetPartialClasses(), conf.partial);
176 classesVecToSet(options.GetRemainsPartialClasses(), conf.remainsPartial);
177 std::transform(options.GetStripUnusedSkiplist().begin(), options.GetStripUnusedSkiplist().end(),
178 std::inserter(conf.entryNames, conf.entryNames.begin()), [](const std::string &s) { return s; });
179
180 if (!ReadDependenciesFromConfig(options, conf)) {
181 return PrintHelp(paParser);
182 }
183
184 auto res = ark::static_linker::Link(conf, options.GetOutput(), abcFiles);
185
186 size_t i = 0;
187 for (const auto &s : res.errors) {
188 std::cerr << "# " << ++i << "\n";
189 std::cerr << s << std::endl;
190 }
191
192 if (options.IsShowStats()) {
193 std::cout << "stats:\n" << res.stats << std::endl;
194 }
195
196 const auto wasError = !res.errors.empty();
197 return static_cast<int>(wasError);
198 }
199