@@ -282,6 +282,53 @@ static bool parseManifest(StringRef option, bool &enable, bool &embed,
282
282
return true ;
283
283
}
284
284
285
+ // Returns true if the given file is a Windows resource file.
286
+ static bool isResoruceFile (StringRef path) {
287
+ llvm::sys::fs::file_magic fileType;
288
+ if (llvm::sys::fs::identify_magic (path, fileType)) {
289
+ // If we cannot read the file, assume it's not a resource file.
290
+ // The further stage will raise an error on this unreadable file.
291
+ return false ;
292
+ }
293
+ return fileType == llvm::sys::fs::file_magic::windows_resource;
294
+ }
295
+
296
+ // Merge Windows resource files and convert them to a single COFF file.
297
+ // The temporary file path is set to result.
298
+ static bool convertResourceFiles (std::vector<std::string> inFiles,
299
+ std::string &result) {
300
+ // Create an output file path.
301
+ SmallString<128 > outFile;
302
+ if (llvm::sys::fs::createTemporaryFile (" resource" , " obj" , outFile))
303
+ return false ;
304
+ std::string outFileArg = (" /out:" + outFile).str ();
305
+
306
+ // Construct CVTRES.EXE command line and execute it.
307
+ std::string program = " cvtres.exe" ;
308
+ std::string programPath = llvm::sys::FindProgramByName (program);
309
+ if (programPath.empty ()) {
310
+ llvm::errs () << " Unable to find " << program << " in PATH\n " ;
311
+ return false ;
312
+ }
313
+
314
+ std::vector<const char *> args;
315
+ args.push_back (programPath.c_str ());
316
+ args.push_back (" /machine:x86" );
317
+ args.push_back (" /readonly" );
318
+ args.push_back (" /nologo" );
319
+ args.push_back (outFileArg.c_str ());
320
+ for (const std::string &path : inFiles)
321
+ args.push_back (path.c_str ());
322
+ args.push_back (nullptr );
323
+
324
+ if (llvm::sys::ExecuteAndWait (programPath.c_str (), &args[0 ]) != 0 ) {
325
+ llvm::errs () << program << " failed\n " ;
326
+ return false ;
327
+ }
328
+ result = outFile.str ();
329
+ return true ;
330
+ }
331
+
285
332
// Parse /manifestuac:(level=<string>|uiAccess=<string>).
286
333
//
287
334
// The arguments will be embedded to the manifest XML file with no error check,
@@ -503,43 +550,37 @@ static bool createManifestResourceFile(PECOFFLinkingContext &ctx,
503
550
return true ;
504
551
}
505
552
506
- // Create a side-by-side manifest file. The side-by-side manifest file is a
507
- // separate XML file having ".manifest" extension. It will be created in the
508
- // same directory as the resulting executable.
553
+
554
+ // Create the a side-by-side manifest file.
555
+ //
556
+ // The manifest file will convey some information to the linker, such as whether
557
+ // the binary needs to run as Administrator or not. Instead of being placed in
558
+ // the PE/COFF header, it's in XML format for some reason -- I guess it's
559
+ // probably because it's invented in the early dot-com era.
560
+ //
561
+ // The side-by-side manifest file is a separate XML file having ".manifest"
562
+ // extension. It will be created in the same directory as the resulting
563
+ // executable.
509
564
static bool createSideBySideManifestFile (PECOFFLinkingContext &ctx,
510
565
raw_ostream &diag) {
566
+ std::string path = ctx.getManifestOutputPath ();
567
+ if (path.empty ()) {
568
+ // Default name of the manifest file is "foo.exe.manifest" where "foo.exe" is
569
+ // the output path.
570
+ path = ctx.outputPath ();
571
+ path.append (" .manifest" );
572
+ }
573
+
511
574
std::string errorInfo;
512
- llvm::raw_fd_ostream out (ctx.getManifestOutputPath ().data (), errorInfo,
513
- llvm::sys::fs::F_Text);
575
+ llvm::raw_fd_ostream out (path.c_str (), errorInfo, llvm::sys::fs::F_Text);
514
576
if (!errorInfo.empty ()) {
515
- diag << " Failed to open " << ctx.getManifestOutputPath () << " : "
516
- << errorInfo << " \n " ;
577
+ diag << errorInfo << " \n " ;
517
578
return false ;
518
579
}
519
580
out << createManifestXml (ctx);
520
581
return true ;
521
582
}
522
583
523
- // Create the a side-by-side manifest file, or create a resource file for the
524
- // manifest file and add it to the input graph.
525
- //
526
- // The manifest file will convey some information to the linker, such as whether
527
- // the binary needs to run as Administrator or not. Instead of being placed in
528
- // the PE/COFF header, it's in XML format for some reason -- I guess it's
529
- // probably because it's invented in the early dot-com era.
530
- static bool createManifest (PECOFFLinkingContext &ctx, raw_ostream &diag) {
531
- if (ctx.getEmbedManifest ()) {
532
- std::string resourceFilePath;
533
- if (!createManifestResourceFile (ctx, diag, resourceFilePath))
534
- return false ;
535
- std::unique_ptr<InputElement> inputElement (
536
- new PECOFFFileNode (ctx, ctx.allocate (resourceFilePath)));
537
- ctx.getInputGraph ().addInputElement (std::move (inputElement));
538
- return true ;
539
- }
540
- return createSideBySideManifestFile (ctx, diag);
541
- }
542
-
543
584
// Handle /failifmismatch option.
544
585
static bool
545
586
handleFailIfMismatchOption (StringRef option,
@@ -770,14 +811,13 @@ bool WinLinkDriver::linkPECOFF(int argc, const char **argv, raw_ostream &diag) {
770
811
return false ;
771
812
772
813
// Create the file if needed.
773
- if (context.getCreateManifest ())
774
- if (!createManifest (context, diag))
814
+ if (context.getCreateManifest () && !context. getEmbedManifest () )
815
+ if (!createSideBySideManifestFile (context, diag))
775
816
return false ;
776
817
777
818
// Register possible input file parsers.
778
819
context.registry ().addSupportCOFFObjects (context);
779
820
context.registry ().addSupportCOFFImportLibraries ();
780
- context.registry ().addSupportWindowsResourceFiles ();
781
821
context.registry ().addSupportArchives (context.logInputFiles ());
782
822
context.registry ().addSupportNativeObjects ();
783
823
context.registry ().addSupportYamlFiles ();
@@ -1202,6 +1242,35 @@ bool WinLinkDriver::parse(int argc, const char *argv[],
1202
1242
for (const StringRef value : dashdash->getValues ())
1203
1243
inputFiles.push_back (value);
1204
1244
1245
+ // Compile Windows resource files to compiled resource file.
1246
+ if (ctx.getCreateManifest () && ctx.getEmbedManifest () &&
1247
+ !isReadingDirectiveSection) {
1248
+ std::string resFile;
1249
+ if (!createManifestResourceFile (ctx, diag, resFile))
1250
+ return false ;
1251
+ inputFiles.push_back (ctx.allocate (resFile));
1252
+ }
1253
+
1254
+ // A Windows Resource file is not an object file. It contains data,
1255
+ // such as an icon image, and is not in COFF file format. If resource
1256
+ // files are given, the linker merge them into one COFF file using
1257
+ // CVTRES.EXE and then link the resulting file.
1258
+ {
1259
+ auto it = std::partition (inputFiles.begin (), inputFiles.end (),
1260
+ isResoruceFile);
1261
+ if (it != inputFiles.begin ()) {
1262
+ std::vector<std::string> resFiles (inputFiles.begin (), it);
1263
+ std::string resObj;
1264
+ if (!convertResourceFiles (resFiles, resObj)) {
1265
+ diag << " Failed to convert resource files\n " ;
1266
+ return false ;
1267
+ }
1268
+ inputFiles = std::vector<StringRef>(it, inputFiles.end ());
1269
+ inputFiles.push_back (ctx.allocate (resObj));
1270
+ ctx.registerTemporaryFile (resObj);
1271
+ }
1272
+ }
1273
+
1205
1274
// Prepare objects to add them to input graph.
1206
1275
for (StringRef path : inputFiles) {
1207
1276
path = ctx.allocate (path);
@@ -1253,14 +1322,6 @@ bool WinLinkDriver::parse(int argc, const char *argv[],
1253
1322
ctx.setOutputPath (replaceExtension (ctx, path, " .exe" ));
1254
1323
}
1255
1324
1256
- // Default name of the manifest file is "foo.exe.manifest" where "foo.exe" is
1257
- // the output path.
1258
- if (ctx.getManifestOutputPath ().empty ()) {
1259
- std::string path = ctx.outputPath ();
1260
- path.append (" .manifest" );
1261
- ctx.setManifestOutputPath (ctx.allocate (path));
1262
- }
1263
-
1264
1325
// Add the input files to the input graph.
1265
1326
if (!ctx.hasInputGraph ())
1266
1327
ctx.setInputGraph (std::unique_ptr<InputGraph>(new InputGraph ()));
0 commit comments