1//===- StaticLibraryCreateCLI.cpp -----------------------------------------===//
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// Implements the `static-library create` CLI action. The class contains
10// no cl::opt globals of its own: the driver in SSAFLinker.cpp owns all
11// flag definitions and hands values in via the Config struct passed to
12// run().
13//
14//===----------------------------------------------------------------------===//
15
16#include "StaticLibraryCreateCLI.h"
17
18#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
19#include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h"
20#include "clang/ScalableStaticAnalysis/Core/Support/ErrorBuilder.h"
21#include "clang/ScalableStaticAnalysis/Core/Support/FormatProviders.h"
22#include "llvm/ADT/STLExtras.h"
23#include "llvm/Support/Path.h"
24#include <memory>
25
26using namespace llvm;
27using namespace clang::ssaf;
28
29namespace path = llvm::sys::path;
30
31//===----------------------------------------------------------------------===//
32// Error Messages
33//===----------------------------------------------------------------------===//
34
35namespace {
36
37constexpr const char *ReadingSummary = "Reading TU summary '{0}'";
38
39constexpr const char *TargetTripleMismatch =
40 "target triple '{0}' from TU summary '{1}' does not match expected "
41 "triple '{2}'";
42
43constexpr const char *NoInputs =
44 "no input TU summaries: at least one input is required";
45
46constexpr const char *DuplicateMember =
47 "duplicate TU summary member with namespace {0}";
48
49constexpr const char *InvalidTargetTriple =
50 "invalid --target-triple '{0}': unrecognized {1}";
51
52BuildNamespace makeStaticLibraryNamespace(llvm::StringRef Name) {
53 return BuildNamespace(BuildNamespaceKind::StaticLibrary, Name);
54}
55
56} // namespace
57
58//===----------------------------------------------------------------------===//
59// StaticLibraryCreateCLI
60//===----------------------------------------------------------------------===//
61
62void StaticLibraryCreateCLI::run(llvm::TimerGroup &TG, const Config &InCfg) {
63 Cfg = InCfg;
64
65 info(Level: 0, Fmt: "Creating StaticLibrary started.");
66
67 {
68 info(Level: 1, Fmt: "Validating input.");
69 validate(TG);
70 }
71
72 info(Level: 1, Fmt: "Bundling StaticLibrary objects.");
73 StaticLibrary Result = bundle(TG);
74 write(TG, Result);
75
76 info(Level: 0, Fmt: "Creating StaticLibrary finished.");
77}
78
79void StaticLibraryCreateCLI::validate(llvm::TimerGroup &TG) {
80 llvm::Timer TValidate("validate", "Validate Input", TG);
81 llvm::TimeRegion _(Cfg.Time ? &TValidate : nullptr);
82
83 OutputFile = FormatFile::fromOutputPath(Path: Cfg.OutputPath);
84 info(Level: 2, Fmt: "Validated output path '{0}'.", Args&: OutputFile.Path);
85
86 if (Cfg.InputPaths.empty()) {
87 fail(Msg: NoInputs);
88 }
89 for (const auto &InputPath : Cfg.InputPaths) {
90 InputFiles.push_back(x: FormatFile::fromInputPath(Path: InputPath));
91 }
92 info(Level: 2, Fmt: "Validated {0} input summary paths.", Args: InputFiles.size());
93
94 NamespaceName = Cfg.Namespace.empty() ? path::stem(path: OutputFile.Path).str()
95 : Cfg.Namespace.str();
96 info(Level: 2, Fmt: "Namespace name: '{0}'.", Args&: NamespaceName);
97
98 if (!Cfg.TargetTriple.empty()) {
99 // Enforce that the supplied triple names a recognized architecture, vendor,
100 // and OS up front so obviously-malformed values fail here with a targeted
101 // message rather than surfacing later as a triple mismatch against an
102 // input's real triple.
103 llvm::Triple T(Cfg.TargetTriple);
104 if (T.getArch() == llvm::Triple::UnknownArch) {
105 fail(Fmt: InvalidTargetTriple, Args&: Cfg.TargetTriple, Args: "architecture");
106 }
107 if (T.getVendor() == llvm::Triple::UnknownVendor) {
108 fail(Fmt: InvalidTargetTriple, Args&: Cfg.TargetTriple, Args: "vendor");
109 }
110 if (T.getOS() == llvm::Triple::UnknownOS) {
111 fail(Fmt: InvalidTargetTriple, Args&: Cfg.TargetTriple, Args: "OS");
112 }
113 ResolvedTriple = std::move(T);
114 info(Level: 2, Fmt: "Explicit target triple: '{0}'.",
115 Args: llvm::Triple::normalize(Str: ResolvedTriple->str()));
116 }
117}
118
119StaticLibrary StaticLibraryCreateCLI::bundle(llvm::TimerGroup &TG) {
120 llvm::Timer TRead("read", "Read Summaries", TG);
121 llvm::Timer TAssemble("assemble", "Assemble StaticLibrary", TG);
122
123 // If the target triple came from --target-triple, we can build the
124 // StaticLibrary upfront; otherwise its triple is the first member's
125 // and construction has to wait until we've read that member.
126 std::optional<StaticLibrary> Result;
127 if (ResolvedTriple) {
128 Result.emplace(args&: *ResolvedTriple, args: makeStaticLibraryNamespace(Name: NamespaceName));
129 }
130
131 info(Level: 2, Fmt: "Bundling members.");
132
133 for (auto [Index, InputFile] : llvm::enumerate(First&: InputFiles)) {
134 std::unique_ptr<TUSummaryEncoding> Member;
135
136 {
137 info(Level: 3, Fmt: "[{0}/{1}] Reading '{2}'.", Args: (Index + 1), Args: InputFiles.size(),
138 Args&: InputFile.Path);
139 llvm::TimeRegion _(Cfg.Time ? &TRead : nullptr);
140
141 auto ExpectedSummary =
142 InputFile.Format->readTUSummaryEncoding(Path: InputFile.Path);
143 if (!ExpectedSummary) {
144 fail(Err: ErrorBuilder::wrap(E: ExpectedSummary.takeError())
145 .context(Fmt: ReadingSummary, ArgVals&: InputFile.Path)
146 .build());
147 }
148 Member = std::make_unique<TUSummaryEncoding>(args: std::move(*ExpectedSummary));
149 }
150
151 // Resolve (or verify) the target triple and, if this is the first
152 // input under triple inference, construct the StaticLibrary now.
153 const llvm::Triple &MemberTriple = Member->getTargetTriple();
154 if (!Result) {
155 Result.emplace(args: MemberTriple, args: makeStaticLibraryNamespace(Name: NamespaceName));
156 } else if (MemberTriple != Result->TargetTriple) {
157 fail(Fmt: TargetTripleMismatch, Args: llvm::Triple::normalize(Str: MemberTriple.str()),
158 Args&: InputFile.Path, Args: llvm::Triple::normalize(Str: Result->TargetTriple.str()));
159 }
160
161 {
162 info(Level: 3, Fmt: "[{0}/{1}] Assembling '{2}'.", Args: (Index + 1), Args: InputFiles.size(),
163 Args&: InputFile.Path);
164 llvm::TimeRegion _(Cfg.Time ? &TAssemble : nullptr);
165
166 auto MemberNamespace = Member->TUNamespace;
167 auto [It, Inserted] = Result->Members.insert(x: std::move(Member));
168 if (!Inserted) {
169 fail(Fmt: DuplicateMember, Args&: MemberNamespace);
170 }
171 }
172 }
173
174 info(Level: 2, Fmt: "Target triple: '{0}'.",
175 Args: llvm::Triple::normalize(Str: Result->TargetTriple.str()));
176
177 return std::move(*Result);
178}
179
180void StaticLibraryCreateCLI::write(llvm::TimerGroup &TG,
181 const StaticLibrary &Result) {
182 info(Level: 2, Fmt: "Writing StaticLibrary to '{0}'.", Args&: OutputFile.Path);
183
184 llvm::Timer TWrite("write", "Write StaticLibrary", TG);
185 llvm::TimeRegion _(Cfg.Time ? &TWrite : nullptr);
186
187 if (auto Err =
188 OutputFile.Format->writeStaticLibrary(S: Result, Path: OutputFile.Path)) {
189 fail(Err: std::move(Err));
190 }
191}
192