fewin32.cc (4091B)
1 // 2 // Copyright (c) 2016 Kyle Milz <kyle@0x30.net> 3 // 4 // Permission to use, copy, modify, and distribute this software for any 5 // purpose with or without fee is hereby granted, provided that the above 6 // copyright notice and this permission notice appear in all copies. 7 // 8 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 // 16 #include "inst_fewin32.h" 17 18 #include <windows.h> // CreateProcess 19 #include <Shlwapi.h> // PathFindOnPath 20 21 #include <array> 22 #include <cstdio> // tmpnam 23 #include <cstring> // strcmp 24 #include <fstream> // ifstream, ofstream 25 #include <iostream> // cerr 26 #include <sstream> // ostringstream 27 28 29 static void 30 Err(int code, const char *fmt) 31 { 32 char buf[256]; 33 34 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 35 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL); 36 37 std::cerr << fmt << ": " << buf << std::endl; 38 ExitProcess(code); 39 } 40 41 char 42 InstFrontendWin32::dir_sep() 43 { 44 return '\\'; 45 } 46 47 char 48 InstFrontendWin32::path_sep() 49 { 50 return ';'; 51 } 52 53 std::string 54 InstFrontendWin32::lib_name() 55 { 56 return "libcitrun.lib"; 57 } 58 59 void 60 InstFrontendWin32::log_os_str() 61 { 62 m_log << " (Windows x86)"; 63 } 64 65 void 66 InstFrontendWin32::set_path(std::string const & new_path) 67 { 68 if (SetEnvironmentVariableA("Path", new_path.c_str()) == 0) 69 Err(1, "SetEnvironmentVariableA"); 70 } 71 72 void 73 InstFrontendWin32::copy_file(std::string const &dst_fn, std::string const &src_fn) 74 { 75 // TODO: Timestamp saving. 76 77 std::ifstream src(src_fn, std::ios::binary); 78 std::ofstream dst(dst_fn, std::ios::binary); 79 80 dst << src.rdbuf(); 81 82 src.close(); 83 dst.close(); 84 } 85 86 bool 87 InstFrontendWin32::is_link(bool object_arg, bool compile_arg) 88 { 89 if (std::strcmp(m_args[0], "link") == 0) 90 // If we're called as link.exe we're linking for sure. 91 return true; 92 if (!compile_arg && m_source_files.size() > 0) 93 // cl.exe main.c 94 return true; 95 96 return false; 97 } 98 99 // 100 // On Windows the best exec alternative is to CreateProcess, wait for it to 101 // finish and exit with its exit code. Windows has execvp, but it looks to 102 // CreateProcess and then itself exit, leading to race conditions. 103 // 104 void 105 InstFrontendWin32::exec_compiler() 106 { 107 if (m_is_citruninst) { 108 m_log << "Running as citrun_inst, not calling exec()" << std::endl; 109 exit(0); 110 } 111 112 exit(fork_compiler()); 113 } 114 115 // 116 // Fork and wait for a compiler to finish and return the exit code. 117 // Do our own PATH lookup because the default one CreateProcess does will find 118 // our cl.exe again instead of searching the PATH for a new one. Exits on error. 119 // 120 int 121 InstFrontendWin32::fork_compiler() 122 { 123 DWORD exit = -1; 124 STARTUPINFOA si; 125 PROCESS_INFORMATION pi; 126 127 ZeroMemory(&si, sizeof(si)); 128 si.cb = sizeof(si); 129 ZeroMemory(&pi, sizeof(pi)); 130 131 char real_cc[MAX_PATH]; 132 std::strcpy(real_cc, m_args[0]); 133 134 std::array<std::string, 2> exts = {{ ".exe", ".EXE" }}; 135 if (std::find_if(exts.begin(), exts.end(), ends_with(real_cc)) == exts.end()) 136 std::strcat(real_cc, ".exe"); 137 138 if (PathFindOnPathA(real_cc, NULL) == FALSE) 139 m_log << "PathFindOnPathA failed for " << real_cc << std::endl; 140 141 std::stringstream argv; 142 for (unsigned int i = 1; i < m_args.size(); ++i) 143 argv << " " << m_args[i]; 144 145 if (!CreateProcessA(real_cc, 146 (LPSTR) argv.str().c_str(), 147 NULL, 148 NULL, 149 FALSE, 150 0, 151 NULL, 152 NULL, 153 &si, 154 &pi)) 155 Err(1, "CreateProcess"); 156 157 m_log << "Forked compiler '" << real_cc << "' " 158 << "pid is '" << pi.dwProcessId << "'" << std::endl; 159 160 if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) 161 Err(1, "WaitForSingleObject"); 162 163 if (GetExitCodeProcess(pi.hProcess, &exit) == FALSE) 164 Err(1, "GetExitCodeProcess"); 165 166 CloseHandle(pi.hProcess); 167 CloseHandle(pi.hThread); 168 169 return exit; 170 }