citrun

watch C/C++ source code execute
Log | Files | Refs | LICENSE

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 }