<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">Cool! Some thoughts below.</div><div class="gmail_quote"><br></div><div class="gmail_quote">On Wed, Oct 23, 2013 at 6:39 PM, Rui Ueyama <span dir="ltr"><<a href="mailto:ruiu@google.com" target="_blank">ruiu@google.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">+// Quote double quotes and backslashes in the given string, so that we can embed<br>
+// the string into a resource script file.<br>
+std::string quoteXml(StringRef str) {<br>
+ std::string ret;<br>
+ ret.reserve(str.size() * 2);<br>
+ StringRef line;<br>
+ for (;;) {<br>
+ if (str.empty())<br>
+ return std::move(ret);<br>
+ llvm::tie(line, str) = str.split("\n");<br>
+ if (!line.empty())<br>
+ continue;<br>
+ ret.append("\"");<br>
+ const char *p = line.data();<br>
+ for (int i = 0, size = line.size(); i < size; ++i) {<br>
+ switch (p[i]) {<br>
+ case '\"':<br>
+ case '\\':<br>
+ ret.append("\\");<br>
+ // fallthrough<br>
+ default:<br>
+ ret.append(1, p[i]);<br>
+ }<br>
+ }<br>
+ ret.append("\"\n");<br>
+ }<br>
+}<br></blockquote><div><br></div><div>Micro-optimization nit: use push_back over append for single character things because it won't contain a loop.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
+<br>
+// Create a resource file (.res file) containing the manifest XML. This is done<br>
+// in two steps:<br>
+//<br>
+// 1. Create a resource script file containing the XML as a literal string.<br>
+// 2. Run RC.EXE command to compile the script file to a resource file.<br>
+//<br>
+// The temporary file created in step 1 will be deleted on exit from this<br>
+// function. The file created in step 2 will have the same lifetime as the<br>
+// PECOFFLinkingContext.<br>
+bool createManifestResourceFile(PECOFFLinkingContext &ctx,<br>
+ raw_ostream &diagnostics,<br>
+ std::string &resFile) {<br>
+ // Create a temporary file for the resource script file.<br>
+ SmallString<128> rcFileSmallString;<br>
+ if (llvm::sys::fs::createTemporaryFile("tmp", "rc", rcFileSmallString)) {<br>
+ diagnostics << "Cannot create a temporary file\n";<br>
+ return false;<br>
+ }<br>
+ StringRef rcFile(rcFileSmallString.str());<br>
+ llvm::FileRemover rcFileRemover((Twine(rcFile)));<br>
+<br>
+ // Open the temporary file for writing.<br>
+ std::string errorInfo;<br>
+ llvm::raw_fd_ostream out(rcFile.data(), errorInfo);<br>
+ if (!errorInfo.empty()) {<br>
+ diagnostics << "Failed to open " << ctx.getManifestOutputPath() << ": "<br>
+ << errorInfo << "\n";<br>
+ return false;<br>
+ }<br>
+<br>
+ // Write resource script to the RC file.<br>
+ out << "#define LANG_ENGLISH 9\n"<br>
+ << "#define SUBLANG_DEFAULT 1\n"<br>
+ << "#define APP_MANIFEST " << ctx.getManifestId() << "\n"<br>
+ << "#define RT_MANIFEST 24\n"<br>
+ << "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n"<br>
+ << "APP_MANIFEST RT_MANIFEST {\n"<br>
+ << quoteXml(createManifestXml(ctx))<br></blockquote><div><br></div><div>If this is the only use, then quoteXml should probably take out as a parameter and stream to it, rather than building up a temporary std::string.</div>
<div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
+ << "}\n";<br>
+ out.close();<br>
+<br>
+ // Create output resource file.<br>
+ SmallString<128> resFileSmallString;<br>
+ if (llvm::sys::fs::createTemporaryFile("tmp", "res", resFileSmallString)) {<br>
+ diagnostics << "Cannot create a temporary file";<br>
+ return false;<br>
+ }<br>
+ resFile = resFileSmallString.str();<br>
+<br>
+ // Register the resource file path so that the file will be deleted when the<br>
+ // context's destructor is called.<br>
+ ctx.registerTemporaryFile(resFile);<br>
+<br>
+ // Run RC.EXE /fo tmp.res tmp.rc<br>
+ std::string program = "rc.exe";<br>
+ std::string programPath = llvm::sys::FindProgramByName(program);<br>
+ if (programPath.empty()) {<br>
+ diagnostics << "Unable to find " << program << " in PATH\n";<br>
+ return false;<br>
+ }<br>
+ std::vector<const char *> args;<br>
+ args.push_back(programPath.c_str());<br>
+ args.push_back("/fo");<br>
+ args.push_back(resFile.c_str());<br>
+ args.push_back(rcFile.data());<br></blockquote><div><br></div><div>Are you sure this will be null-terminated? Seems like you do:</div><div><br></div><div>+ SmallString<128> rcFileSmallString;<br>+ if (llvm::sys::fs::createTemporaryFile("tmp", "rc", rcFileSmallString)) {<br>
</div><div>...</div><div>+ StringRef rcFile(rcFileSmallString.str());<br></div><div>...</div><div>+ args.push_back(rcFile.data());<br></div><div><br></div><div>I was able to prove that the temp file comes back null-terminated due to internal implementation details of the Windows UTF16ToUTF8 helper function, but it seems like you should null terminate here.</div>
<div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
+ args.push_back(nullptr);<br>
+<br>
+ if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) {<br>
+ llvm::errs() << program << " failed\n";<br></blockquote><div><br></div><div>llvm::errs() or diagnostics?</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
+ return false;<br>
+ }<br>
+ return true;<br>
+}<br>
+<br>
+// Create a side-by-side manifest file. The side-by-side manifest file is a<br>
+// separate XML file having ".manifest" extension. It will be created in the<br>
+// same directory as the resulting executable.<br>
+bool createSideBySideManifestFile(PECOFFLinkingContext &ctx,<br>
+ raw_ostream &diagnostics) {<br>
+ std::string errorInfo;<br>
+ llvm::raw_fd_ostream out(ctx.getManifestOutputPath().data(), errorInfo);<br>
+ if (!errorInfo.empty()) {<br>
+ diagnostics << "Failed to open " << ctx.getManifestOutputPath() << ": "<br>
+ << errorInfo << "\n";<br>
+ return false;<br>
+ }<br>
+ out << createManifestXml(ctx);<br>
return true;<br>
}<br>
<br>
+// Create the a side-by-side manifest file, or create a resource file for the<br>
+// manifest file and add it to the input graph.<br>
+//<br>
+// The manifest file will convey some information to the linker, such as whether<br>
+// the binary needs to run as Administrator or not. Instead of being placed in<br>
+// the PE/COFF header, it's in XML format for some reason -- I guess it's<br>
+// probably because it's invented in the early dot-com era.<br>
+bool createManifest(PECOFFLinkingContext &ctx, raw_ostream &diagnostics) {<br>
+ if (ctx.getEmbedManifest()) {<br>
+ std::string resourceFilePath;<br>
+ if (!createManifestResourceFile(ctx, diagnostics, resourceFilePath))<br>
+ return false;<br>
+ std::unique_ptr<InputElement> inputElement(<br>
+ new PECOFFFileNode(ctx, resourceFilePath));<br>
+ ctx.inputGraph().addInputElement(std::move(inputElement));<br>
+ return true;<br>
+ }<br>
+ return createSideBySideManifestFile(ctx, diagnostics);<br>
+}<br>
+<br>
// Handle /failifmismatch option.<br>
bool handleFailIfMismatchOption(StringRef option,<br>
std::map<StringRef, StringRef> &mustMatch,<br>
@@ -265,6 +402,10 @@ bool handleFailIfMismatchOption(StringRe<br>
return false;<br>
}<br>
<br>
+//<br>
+// Environment variable<br>
+//<br>
+<br>
// Process "LINK" environment variable. If defined, the value of the variable<br>
// should be processed as command line arguments.<br>
std::vector<const char *> processLinkEnv(PECOFFLinkingContext &context,<br>
@@ -344,6 +485,10 @@ parseArgs(int argc, const char *argv[],<br>
<br>
} // namespace<br>
<br>
+//<br>
+// Main driver<br>
+//<br>
+<br>
ErrorOr<StringRef> PECOFFFileNode::getPath(const LinkingContext &) const {<br>
if (_path.endswith(".lib"))<br>
return _ctx.searchLibraryFile(_path);<br>
@@ -365,6 +510,12 @@ bool WinLinkDriver::linkPECOFF(int argc,<br>
processLibEnv(context);<br>
if (!parse(newargv.size() - 1, &newargv[0], context, diagnostics))<br>
return false;<br>
+<br>
+ // Create the file if needed.<br>
+ if (context.getCreateManifest())<br>
+ if (!createManifest(context, diagnostics))<br>
+ return false;<br>
+<br>
return link(context, diagnostics);<br>
}<br>
<br>
@@ -714,11 +865,6 @@ WinLinkDriver::parse(int argc, const cha<br>
for (auto &e : inputElements)<br>
ctx.inputGraph().addInputElement(std::move(e));<br>
<br>
- // Create the side-by-side manifest file if needed.<br>
- if (!isReadingDirectiveSection && ctx.getCreateManifest())<br>
- if (!createManifestFile(ctx, diagnostics))<br>
- return false;<br>
-<br>
// Validate the combination of options used.<br>
return ctx.validate(diagnostics);<br>
}<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@cs.uiuc.edu">llvm-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br></div></div>