xref: /llvm-project/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp (revision 2946cd701067404b99c39fb29dc9c74bd7193eb3)
1 //===- LibDriver.cpp - lib.exe-compatible driver --------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Defines an interface to a lib.exe-compatible driver that also understands
10 // bitcode files. Used by llvm-lib and lld-link /lib.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/BinaryFormat/Magic.h"
17 #include "llvm/Object/ArchiveWriter.h"
18 #include "llvm/Option/Arg.h"
19 #include "llvm/Option/ArgList.h"
20 #include "llvm/Option/Option.h"
21 #include "llvm/Support/CommandLine.h"
22 #include "llvm/Support/Path.h"
23 #include "llvm/Support/Process.h"
24 #include "llvm/Support/StringSaver.h"
25 #include "llvm/Support/raw_ostream.h"
26 
27 using namespace llvm;
28 
29 namespace {
30 
31 enum {
32   OPT_INVALID = 0,
33 #define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
34 #include "Options.inc"
35 #undef OPTION
36 };
37 
38 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
39 #include "Options.inc"
40 #undef PREFIX
41 
42 static const opt::OptTable::Info InfoTable[] = {
43 #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12)      \
44   {X1, X2, X10,         X11,         OPT_##ID, opt::Option::KIND##Class,       \
45    X9, X8, OPT_##GROUP, OPT_##ALIAS, X7,       X12},
46 #include "Options.inc"
47 #undef OPTION
48 };
49 
50 class LibOptTable : public opt::OptTable {
51 public:
52   LibOptTable() : OptTable(InfoTable, true) {}
53 };
54 
55 }
56 
57 static std::string getOutputPath(opt::InputArgList *Args,
58                                  const NewArchiveMember &FirstMember) {
59   if (auto *Arg = Args->getLastArg(OPT_out))
60     return Arg->getValue();
61   SmallString<128> Val = StringRef(FirstMember.Buf->getBufferIdentifier());
62   sys::path::replace_extension(Val, ".lib");
63   return Val.str();
64 }
65 
66 static std::vector<StringRef> getSearchPaths(opt::InputArgList *Args,
67                                              StringSaver &Saver) {
68   std::vector<StringRef> Ret;
69   // Add current directory as first item of the search path.
70   Ret.push_back("");
71 
72   // Add /libpath flags.
73   for (auto *Arg : Args->filtered(OPT_libpath))
74     Ret.push_back(Arg->getValue());
75 
76   // Add $LIB.
77   Optional<std::string> EnvOpt = sys::Process::GetEnv("LIB");
78   if (!EnvOpt.hasValue())
79     return Ret;
80   StringRef Env = Saver.save(*EnvOpt);
81   while (!Env.empty()) {
82     StringRef Path;
83     std::tie(Path, Env) = Env.split(';');
84     Ret.push_back(Path);
85   }
86   return Ret;
87 }
88 
89 static std::string findInputFile(StringRef File, ArrayRef<StringRef> Paths) {
90   for (StringRef Dir : Paths) {
91     SmallString<128> Path = Dir;
92     sys::path::append(Path, File);
93     if (sys::fs::exists(Path))
94       return Path.str().str();
95   }
96   return "";
97 }
98 
99 int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {
100   BumpPtrAllocator Alloc;
101   StringSaver Saver(Alloc);
102 
103   // Parse command line arguments.
104   SmallVector<const char *, 20> NewArgs(ArgsArr.begin(), ArgsArr.end());
105   cl::ExpandResponseFiles(Saver, cl::TokenizeWindowsCommandLine, NewArgs);
106   ArgsArr = NewArgs;
107 
108   LibOptTable Table;
109   unsigned MissingIndex;
110   unsigned MissingCount;
111   opt::InputArgList Args =
112       Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount);
113   if (MissingCount) {
114     llvm::errs() << "missing arg value for \""
115                  << Args.getArgString(MissingIndex) << "\", expected "
116                  << MissingCount
117                  << (MissingCount == 1 ? " argument.\n" : " arguments.\n");
118     return 1;
119   }
120   for (auto *Arg : Args.filtered(OPT_UNKNOWN))
121     llvm::errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n";
122 
123   // Handle /help
124   if (Args.hasArg(OPT_help)) {
125     Table.PrintHelp(outs(), "llvm-lib [options] file...", "LLVM Lib");
126     return 0;
127   }
128 
129   // If no input files, silently do nothing to match lib.exe.
130   if (!Args.hasArgNoClaim(OPT_INPUT))
131     return 0;
132 
133   std::vector<StringRef> SearchPaths = getSearchPaths(&Args, Saver);
134 
135   // Create a NewArchiveMember for each input file.
136   std::vector<NewArchiveMember> Members;
137   for (auto *Arg : Args.filtered(OPT_INPUT)) {
138     std::string Path = findInputFile(Arg->getValue(), SearchPaths);
139     if (Path.empty()) {
140       llvm::errs() << Arg->getValue() << ": no such file or directory\n";
141       return 1;
142     }
143 
144     Expected<NewArchiveMember> MOrErr =
145         NewArchiveMember::getFile(Saver.save(Path), /*Deterministic=*/true);
146     if (!MOrErr) {
147       handleAllErrors(MOrErr.takeError(), [&](const ErrorInfoBase &EIB) {
148         llvm::errs() << Arg->getValue() << ": " << EIB.message() << "\n";
149       });
150       return 1;
151     }
152 
153     file_magic Magic = identify_magic(MOrErr->Buf->getBuffer());
154     if (Magic != file_magic::coff_object && Magic != file_magic::bitcode &&
155         Magic != file_magic::windows_resource) {
156       llvm::errs() << Arg->getValue()
157                    << ": not a COFF object, bitcode or resource file\n";
158       return 1;
159     }
160     Members.emplace_back(std::move(*MOrErr));
161   }
162 
163   // Create an archive file.
164   std::string OutputPath = getOutputPath(&Args, Members[0]);
165   if (Error E =
166           writeArchive(OutputPath, Members,
167                        /*WriteSymtab=*/true, object::Archive::K_GNU,
168                        /*Deterministic*/ true, Args.hasArg(OPT_llvmlibthin))) {
169     handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
170       llvm::errs() << OutputPath << ": " << EI.message() << "\n";
171     });
172     return 1;
173   }
174 
175   return 0;
176 }
177