• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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