clang: lib/Driver/Job.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

15#include "llvm/ADT/ArrayRef.h"

16#include "llvm/ADT/SmallString.h"

17#include "llvm/ADT/SmallVector.h"

18#include "llvm/ADT/StringExtras.h"

19#include "llvm/ADT/StringRef.h"

20#include "llvm/ADT/StringSet.h"

21#include "llvm/ADT/StringSwitch.h"

22#include "llvm/Support/CrashRecoveryContext.h"

23#include "llvm/Support/FileSystem.h"

24#include "llvm/Support/Path.h"

25#include "llvm/Support/PrettyStackTrace.h"

26#include "llvm/Support/Program.h"

27#include "llvm/Support/raw_ostream.h"

28#include

29#include

30#include

31#include <system_error>

32#include

33

34using namespace clang;

35using namespace driver;

36

39 const llvm::opt::ArgStringList &Arguments,

41 const char *PrependArg)

42 : Source(Source), Creator(Creator), ResponseSupport(ResponseSupport),

43 Executable(Executable), PrependArg(PrependArg), Arguments(Arguments) {

44 for (const auto &II : Inputs)

45 if (II.isFilename())

47 for (const auto &II : Outputs)

48 if (II.isFilename())

49 OutputFilenames.push_back(II.getFilename());

50}

51

52

53

54

55static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum,

56 bool &IsInclude) {

57 SkipNum = 2;

58

59

60 bool ShouldSkip = llvm::StringSwitch(Flag)

61 .Cases("-MF", "-MT", "-MQ", "-serialize-diagnostic-file", true)

62 .Cases("-o", "-dependency-file", true)

63 .Cases("-fdebug-compilation-dir", "-diagnostic-log-file", true)

64 .Cases("-dwarf-debug-flags", "-ivfsoverlay", true)

65 .Default(false);

66 if (ShouldSkip)

67 return true;

68

69

70 IsInclude = llvm::StringSwitch(Flag)

71 .Cases("-include", "-header-include-file", true)

72 .Cases("-idirafter", "-internal-isystem", "-iwithprefix", true)

73 .Cases("-internal-externc-isystem", "-iprefix", true)

74 .Cases("-iwithprefixbefore", "-isystem", "-iquote", true)

75 .Cases("-isysroot", "-I", "-F", "-resource-dir", true)

76 .Cases("-iframework", "-include-pch", true)

77 .Default(false);

78 if (IsInclude)

79 return !HaveCrashVFS;

80

81

82

83

84 ShouldSkip = llvm::StringSwitch(Flag)

85 .Cases("-M", "-MM", "-MG", "-MP", "-MD", true)

86 .Case("-MMD", true)

87 .Default(false);

88

89

90 SkipNum = 1;

91 if (ShouldSkip)

92 return true;

93

94

95 StringRef FlagRef(Flag);

96 IsInclude = FlagRef.starts_with("-F") || FlagRef.starts_with("-I");

97 if (IsInclude)

98 return !HaveCrashVFS;

99 if (FlagRef.starts_with("-fmodules-cache-path="))

100 return true;

101

102 SkipNum = 0;

103 return false;

104}

105

106void Command::writeResponseFile(raw_ostream &OS) const {

107

109 for (const auto *Arg : InputFileList) {

110 OS << Arg << '\n';

111 }

112 return;

113 }

114

115

116

117

118 for (const auto *Arg : Arguments) {

119 OS << '"';

120

121 for (; *Arg != '\0'; Arg++) {

122 if (*Arg == '\"' || *Arg == '\\') {

123 OS << '\\';

124 }

125 OS << *Arg;

126 }

127

128 OS << "\" ";

129 }

130}

131

132void Command::buildArgvForResponseFile(

134

135

136

138 Out.push_back(Executable);

139 Out.push_back(ResponseFileFlag.c_str());

140 return;

141 }

142

143 llvm::StringSet<> Inputs;

144 for (const auto *InputName : InputFileList)

145 Inputs.insert(InputName);

146 Out.push_back(Executable);

147

148 if (PrependArg)

149 Out.push_back(PrependArg);

150

151

152

153 bool FirstInput = true;

154 for (const auto *Arg : Arguments) {

155 if (Inputs.count(Arg) == 0) {

156 Out.push_back(Arg);

157 } else if (FirstInput) {

158 FirstInput = false;

160 Out.push_back(ResponseFile);

161 }

162 }

163}

164

165

166static void

168 size_t NumArgs,

170 using namespace llvm;

171 using namespace sys;

172

174 if (path::is_absolute(InInc))

175 return false;

176 std::error_code EC = fs::current_path(OutInc);

177 if (EC)

178 return false;

179 path::append(OutInc, InInc);

180 return true;

181 };

182

184 if (NumArgs == 1) {

185 StringRef FlagRef(Args[Idx + NumArgs - 1]);

186 assert((FlagRef.starts_with("-F") || FlagRef.starts_with("-I")) &&

187 "Expecting -I or -F");

188 StringRef Inc = FlagRef.slice(2, StringRef::npos);

189 if (getAbsPath(Inc, NewInc)) {

191 NewArg += NewInc;

192 IncFlags.push_back(std::move(NewArg));

193 }

194 return;

195 }

196

197 assert(NumArgs == 2 && "Not expecting more than two arguments");

198 StringRef Inc(Args[Idx + NumArgs - 1]);

199 if (!getAbsPath(Inc, NewInc))

200 return;

202 IncFlags.push_back(std::move(NewInc));

203}

204

205void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote,

207

208 OS << ' ';

209 llvm::sys::printArg(OS, Executable, true);

210

213 if (ResponseFile != nullptr) {

214 buildArgvForResponseFile(ArgsRespFile);

216 } else if (PrependArg) {

217 OS << ' ';

218 llvm::sys::printArg(OS, PrependArg, true);

219 }

220

221 bool HaveCrashVFS = CrashInfo && !CrashInfo->VFSPath.empty();

222 for (size_t i = 0, e = Args.size(); i < e; ++i) {

223 const char *const Arg = Args[i];

224

225 if (CrashInfo) {

226 int NumArgs = 0;

227 bool IsInclude = false;

228 if (skipArgs(Arg, HaveCrashVFS, NumArgs, IsInclude)) {

229 i += NumArgs - 1;

230 continue;

231 }

232

233

234 if (HaveCrashVFS && IsInclude) {

237 if (!NewIncFlags.empty()) {

238 for (auto &F : NewIncFlags) {

239 OS << ' ';

240 llvm::sys::printArg(OS, F.c_str(), Quote);

241 }

242 i += NumArgs - 1;

243 continue;

244 }

245 }

246

249 });

251 (i == 0 || StringRef(Args[i - 1]) != "-main-file-name")) {

252

253 OS << ' ';

254 StringRef ShortName = llvm::sys::path::filename(CrashInfo->Filename);

255 llvm::sys::printArg(OS, ShortName.str(), Quote);

256 continue;

257 }

258 }

259

260 OS << ' ';

261 llvm::sys::printArg(OS, Arg, Quote);

262 }

263

264 if (CrashInfo && HaveCrashVFS) {

265 OS << ' ';

266 llvm::sys::printArg(OS, "-ivfsoverlay", Quote);

267 OS << ' ';

268 llvm::sys::printArg(OS, CrashInfo->VFSPath.str(), Quote);

269

270

271

272

273

274

275 SmallString<128> RelModCacheDir = llvm::sys::path::parent_path(

276 llvm::sys::path::parent_path(CrashInfo->VFSPath));

277 llvm::sys::path::append(RelModCacheDir, "repro-modules");

278

279 std::string ModCachePath = "-fmodules-cache-path=";

280 ModCachePath.append(RelModCacheDir.c_str());

281

282 OS << ' ';

283 llvm::sys::printArg(OS, ModCachePath, Quote);

284 }

285

286 if (ResponseFile != nullptr) {

287 OS << "\n Arguments passed via response file:\n";

288 writeResponseFile(OS);

289

290

292 OS << "\n";

293 OS << " (end of response file)";

294 }

295

296 OS << Terminator;

297}

298

301 ResponseFileFlag = ResponseSupport.ResponseFlag;

302 ResponseFileFlag += FileName;

303}

304

306 Environment.reserve(NewEnvironment.size() + 1);

307 Environment.assign(NewEnvironment.begin(), NewEnvironment.end());

308 Environment.push_back(nullptr);

309}

310

312 const std::vector<std::optionalstd::string> &Redirects) {

313 RedirectFiles = Redirects;

314}

315

319 llvm::outs() << llvm::sys::path::filename(Arg.getFilename()) << "\n";

320 llvm::outs().flush();

321 }

322}

323

325 std::string *ErrMsg, bool *ExecutionFailed) const {

327

329 if (ResponseFile == nullptr) {

330 Argv.push_back(Executable);

331 if (PrependArg)

332 Argv.push_back(PrependArg);

333 Argv.append(Arguments.begin(), Arguments.end());

334 Argv.push_back(nullptr);

335 } else {

336

337 std::string RespContents;

338 llvm::raw_string_ostream SS(RespContents);

339

340

341 writeResponseFile(SS);

342 buildArgvForResponseFile(Argv);

343 Argv.push_back(nullptr);

344

345

346 if (std::error_code EC = writeFileWithEncoding(

347 ResponseFile, RespContents, ResponseSupport.ResponseEncoding)) {

348 if (ErrMsg)

349 *ErrMsg = EC.message();

350 if (ExecutionFailed)

351 *ExecutionFailed = true;

352

353

354 return -1;

355 }

356 }

357

358 std::optional<ArrayRef> Env;

359 std::vector ArgvVectorStorage;

360 if (!Environment.empty()) {

361 assert(Environment.back() == nullptr &&

362 "Environment vector should be null-terminated by now");

363 ArgvVectorStorage = llvm::toStringRefArray(Environment.data());

365 }

366

367 auto Args = llvm::toStringRefArray(Argv.data());

368

369

370 if (!RedirectFiles.empty()) {

371 std::vector<std::optional> RedirectFilesOptional;

372 for (const auto &Ele : RedirectFiles)

373 if (Ele)

374 RedirectFilesOptional.push_back(std::optional(*Ele));

375 else

376 RedirectFilesOptional.push_back(std::nullopt);

377

378 return llvm::sys::ExecuteAndWait(Executable, Args, Env,

379 ArrayRef(RedirectFilesOptional),

380 0, 0,

381 ErrMsg, ExecutionFailed, &ProcStat);

382 }

383

384 return llvm::sys::ExecuteAndWait(Executable, Args, Env, Redirects,

385 0, 0,

386 ErrMsg, ExecutionFailed, &ProcStat);

387}

388

391 const char *Executable,

392 const llvm::opt::ArgStringList &Arguments,

394 const char *PrependArg)

395 : Command(Source, Creator, ResponseSupport, Executable, Arguments, Inputs,

396 Outputs, PrependArg) {

398}

399

403 OS << " (in-process)\n";

405}

406

408 std::string *ErrMsg, bool *ExecutionFailed) const {

409

410

411

414

416

420 Argv.push_back(nullptr);

421 Argv.pop_back();

422

423

424

425

426 if (ExecutionFailed)

427 *ExecutionFailed = false;

428

429 llvm::CrashRecoveryContext CRC;

430 CRC.DumpStackAndCleanupOnFailure = true;

431

432 const void *PrettyState = llvm::SavePrettyStackState();

434

435 int R = 0;

436

437 if (!CRC.RunSafely([&]() { R = D.CC1Main(Argv); })) {

438 llvm::RestorePrettyStackState(PrettyState);

439 return CRC.RetCode;

440 }

441 return R;

442}

443

445

446 llvm_unreachable(

447 "The CC1Command doesn't support changing the environment vars!");

448}

449

450void JobList::Print(raw_ostream &OS, const char *Terminator, bool Quote,

452 for (const auto &Job : *this)

453 Job.Print(OS, Terminator, Quote, CrashInfo);

454}

455

static void rewriteIncludes(const llvm::ArrayRef< const char * > &Args, size_t Idx, size_t NumArgs, llvm::SmallVectorImpl< llvm::SmallString< 128 > > &IncFlags)

Rewrite relative include-like flag paths to absolute ones.

static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum, bool &IsInclude)

Check if the compiler flag in question should be skipped when emitting a reproducer.

Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.

Action - Represent an abstract compilation step to perform.

void setEnvironment(llvm::ArrayRef< const char * > NewEnvironment) override

Sets the environment to be used by the new process.

void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const override

int Execute(ArrayRef< std::optional< StringRef > > Redirects, std::string *ErrMsg, bool *ExecutionFailed) const override

CC1Command(const Action &Source, const Tool &Creator, ResponseFileSupport ResponseSupport, const char *Executable, const llvm::opt::ArgStringList &Arguments, ArrayRef< InputInfo > Inputs, ArrayRef< InputInfo > Outputs={}, const char *PrependArg=nullptr)

Command - An executable path/name and argument vector to execute.

const Tool & getCreator() const

getCreator - Return the Tool which caused the creation of this job.

const llvm::opt::ArgStringList & getArguments() const

void setResponseFile(const char *FileName)

Set to pass arguments via a response file when launching the command.

void setRedirectFiles(const std::vector< std::optional< std::string > > &Redirects)

Command(const Action &Source, const Tool &Creator, ResponseFileSupport ResponseSupport, const char *Executable, const llvm::opt::ArgStringList &Arguments, ArrayRef< InputInfo > Inputs, ArrayRef< InputInfo > Outputs={}, const char *PrependArg=nullptr)

bool PrintInputFilenames

Whether to print the input filenames when executing.

const char * getExecutable() const

virtual void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const

bool InProcess

Whether the command will be executed in this process or not.

virtual void setEnvironment(llvm::ArrayRef< const char * > NewEnvironment)

Sets the environment to be used by the new process.

void PrintFileNames() const

Optionally print the filenames to be compiled.

virtual int Execute(ArrayRef< std::optional< StringRef > > Redirects, std::string *ErrMsg, bool *ExecutionFailed) const

Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...

InputInfo - Wrapper for information about an input source.

const char * getFilename() const

void clear()

Clear the job list.

void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo=nullptr) const

The JSON file list parser is used to communicate input to InstallAPI.

Diagnostic wrappers for TextAPI types for error reporting.

ResponseFileKind ResponseKind

The level of support for response files.

llvm::sys::WindowsEncodingMethod ResponseEncoding

The encoding to use when writing response files on Windows.

const char * ResponseFlag

What prefix to use for the command-line argument when passing a response file.