win32: add a helper to run `git.exe` without a foreground window
On Windows, there are two kinds of executables, console ones and non-console ones. Git's executables are all console ones. When launching the former e.g. in a scheduled task, a CMD window pops up. This is not what we want for the tasks installed via the `git maintenance` command. To work around this, let's introduce `headless-git.exe`, which is a non-console program that does _not_ pop up any window. All it does is to re-launch `git.exe`, suppressing that console window, passing through all command-line arguments as-are. Helped-by: Carlo Marcelo Arenas Belón <carenas@gmail.com> Helped-by: Yuyi Wang <Strawberry_Str@hotmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									fb7d80edca
								
							
						
					
					
						commit
						4b8a2717bb
					
				
							
								
								
									
										9
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										9
									
								
								Makefile
								
								
								
								
							|  | @ -2778,6 +2778,13 @@ compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \ | ||||||
| compat/nedmalloc/nedmalloc.sp: SP_EXTRA_FLAGS += -Wno-non-pointer-null | compat/nedmalloc/nedmalloc.sp: SP_EXTRA_FLAGS += -Wno-non-pointer-null | ||||||
| endif | endif | ||||||
|  |  | ||||||
|  | headless-git.o: compat/win32/headless.c GIT-CFLAGS | ||||||
|  | 	$(QUIET_CC)$(CC) $(ALL_CFLAGS) $(COMPAT_CFLAGS) \ | ||||||
|  | 		-fno-stack-protector -o $@ -c -Wall -Wwrite-strings $< | ||||||
|  |  | ||||||
|  | headless-git$X: headless-git.o git.res GIT-LDFLAGS | ||||||
|  | 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -mwindows -o $@ $< git.res | ||||||
|  |  | ||||||
| git-%$X: %.o GIT-LDFLAGS $(GITLIBS) | git-%$X: %.o GIT-LDFLAGS $(GITLIBS) | ||||||
| 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) | 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) | ||||||
|  |  | ||||||
|  | @ -3651,6 +3658,7 @@ clean: profile-clean coverage-clean cocciclean | ||||||
| 	$(RM) po/git.pot po/git-core.pot | 	$(RM) po/git.pot po/git-core.pot | ||||||
| 	$(RM) git.res | 	$(RM) git.res | ||||||
| 	$(RM) $(OBJECTS) | 	$(RM) $(OBJECTS) | ||||||
|  | 	$(RM) headless-git.o | ||||||
| 	$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB) | 	$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB) | ||||||
| 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) | 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) | ||||||
| 	$(RM) $(TEST_PROGRAMS) | 	$(RM) $(TEST_PROGRAMS) | ||||||
|  | @ -3679,6 +3687,7 @@ endif | ||||||
| 	$(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS | 	$(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS | ||||||
| ifdef MSVC | ifdef MSVC | ||||||
| 	$(RM) $(patsubst %.o,%.o.pdb,$(OBJECTS)) | 	$(RM) $(patsubst %.o,%.o.pdb,$(OBJECTS)) | ||||||
|  | 	$(RM) headless-git.o.pdb | ||||||
| 	$(RM) $(patsubst %.exe,%.pdb,$(OTHER_PROGRAMS)) | 	$(RM) $(patsubst %.exe,%.pdb,$(OTHER_PROGRAMS)) | ||||||
| 	$(RM) $(patsubst %.exe,%.iobj,$(OTHER_PROGRAMS)) | 	$(RM) $(patsubst %.exe,%.iobj,$(OTHER_PROGRAMS)) | ||||||
| 	$(RM) $(patsubst %.exe,%.ipdb,$(OTHER_PROGRAMS)) | 	$(RM) $(patsubst %.exe,%.ipdb,$(OTHER_PROGRAMS)) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,115 @@ | ||||||
|  | /* | ||||||
|  |  * headless Git - run Git without opening a console window on Windows | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #define STRICT | ||||||
|  | #define WIN32_LEAN_AND_MEAN | ||||||
|  | #define UNICODE | ||||||
|  | #define _UNICODE | ||||||
|  | #include <windows.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <wchar.h> | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * If `dir` contains the path to a Git exec directory, extend `PATH` to | ||||||
|  |  * include the corresponding `bin/` directory (which is where all those | ||||||
|  |  * `.dll` files needed by `git.exe` are, on Windows). | ||||||
|  |  */ | ||||||
|  | static int extend_path(wchar_t *dir, size_t dir_len) | ||||||
|  | { | ||||||
|  | 	const wchar_t *suffix = L"\\libexec\\git-core"; | ||||||
|  | 	size_t suffix_len = wcslen(suffix); | ||||||
|  | 	wchar_t *env; | ||||||
|  | 	DWORD len; | ||||||
|  |  | ||||||
|  | 	if (dir_len < suffix_len) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	dir_len -= suffix_len; | ||||||
|  | 	if (memcmp(dir + dir_len, suffix, suffix_len * sizeof(wchar_t))) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	len = GetEnvironmentVariableW(L"PATH", NULL, 0); | ||||||
|  | 	if (!len) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	env = _alloca((dir_len + 5 + len) * sizeof(wchar_t)); | ||||||
|  | 	wcsncpy(env, dir, dir_len); | ||||||
|  | 	wcscpy(env + dir_len, L"\\bin;"); | ||||||
|  | 	if (!GetEnvironmentVariableW(L"PATH", env + dir_len + 5, len)) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	SetEnvironmentVariableW(L"PATH", env); | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int WINAPI wWinMain(_In_ HINSTANCE instance, | ||||||
|  | 		    _In_opt_ HINSTANCE previous_instance, | ||||||
|  | 		    _In_ LPWSTR command_line, _In_ int show) | ||||||
|  | { | ||||||
|  | 	wchar_t git_command_line[32768]; | ||||||
|  | 	size_t size = sizeof(git_command_line) / sizeof(wchar_t); | ||||||
|  | 	const wchar_t *needs_quotes = L""; | ||||||
|  | 	int slash = 0, i; | ||||||
|  |  | ||||||
|  | 	STARTUPINFO startup_info = { | ||||||
|  | 		.cb = sizeof(STARTUPINFO), | ||||||
|  | 		.dwFlags = STARTF_USESHOWWINDOW, | ||||||
|  | 		.wShowWindow = SW_HIDE, | ||||||
|  | 	}; | ||||||
|  | 	PROCESS_INFORMATION process_info = { 0 }; | ||||||
|  | 	DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT | | ||||||
|  | 		CREATE_NEW_CONSOLE | CREATE_NO_WINDOW; | ||||||
|  | 	DWORD exit_code; | ||||||
|  |  | ||||||
|  | 	/* First, determine the full path of argv[0] */ | ||||||
|  | 	for (i = 0; _wpgmptr[i]; i++) | ||||||
|  | 		if (_wpgmptr[i] == L' ') | ||||||
|  | 			needs_quotes = L"\""; | ||||||
|  | 		else if (_wpgmptr[i] == L'\\') | ||||||
|  | 			slash = i; | ||||||
|  |  | ||||||
|  | 	if (slash >= size - 11) | ||||||
|  | 		return 127; /* Too long path */ | ||||||
|  |  | ||||||
|  | 	/* If it is in Git's exec path, add the bin/ directory to the PATH */ | ||||||
|  | 	extend_path(_wpgmptr, slash); | ||||||
|  |  | ||||||
|  | 	/* Then, add the full path of `git.exe` as argv[0] */ | ||||||
|  | 	i = swprintf_s(git_command_line, size, L"%ls%.*ls\\git.exe%ls", | ||||||
|  | 		       needs_quotes, slash, _wpgmptr, needs_quotes); | ||||||
|  | 	if (i < 0) | ||||||
|  | 		return 127; /* Too long path */ | ||||||
|  |  | ||||||
|  | 	if (*command_line) { | ||||||
|  | 		/* Now, append the command-line arguments */ | ||||||
|  | 		i = swprintf_s(git_command_line + i, size - i, | ||||||
|  | 			       L" %ls", command_line); | ||||||
|  | 		if (i < 0) | ||||||
|  | 			return 127; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); | ||||||
|  | 	startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); | ||||||
|  | 	startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); | ||||||
|  |  | ||||||
|  | 	if (!CreateProcess(NULL, /* infer argv[0] from the command line */ | ||||||
|  | 			   git_command_line, /* modified command line */ | ||||||
|  | 			   NULL, /* inherit process handles? */ | ||||||
|  | 			   NULL, /* inherit thread handles? */ | ||||||
|  | 			   FALSE, /* handles inheritable? */ | ||||||
|  | 			   creation_flags, | ||||||
|  | 			   NULL, /* use this process' environment */ | ||||||
|  | 			   NULL, /* use this process' working directory */ | ||||||
|  | 			   &startup_info, &process_info)) | ||||||
|  | 		return 129; /* could not start */ | ||||||
|  | 	WaitForSingleObject(process_info.hProcess, INFINITE); | ||||||
|  | 	if (!GetExitCodeProcess(process_info.hProcess, &exit_code)) | ||||||
|  | 		exit_code = 130; /* Could not determine exit code? */ | ||||||
|  |  | ||||||
|  | 	CloseHandle(process_info.hProcess); | ||||||
|  | 	CloseHandle(process_info.hThread); | ||||||
|  |  | ||||||
|  | 	return (int)exit_code; | ||||||
|  | } | ||||||
|  | @ -526,6 +526,8 @@ else | ||||||
| endif | endif | ||||||
| 	X = .exe | 	X = .exe | ||||||
|  |  | ||||||
|  | 	EXTRA_PROGRAMS += headless-git$X | ||||||
|  |  | ||||||
| compat/msvc.o: compat/msvc.c compat/mingw.c GIT-CFLAGS | compat/msvc.o: compat/msvc.c compat/mingw.c GIT-CFLAGS | ||||||
| endif | endif | ||||||
| ifeq ($(uname_S),Interix) | ifeq ($(uname_S),Interix) | ||||||
|  | @ -705,6 +707,7 @@ ifeq ($(uname_S),MINGW) | ||||||
| 	COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \ | 	COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \ | ||||||
| 		-fstack-protector-strong | 		-fstack-protector-strong | ||||||
| 	EXTLIBS += -lntdll | 	EXTLIBS += -lntdll | ||||||
|  | 	EXTRA_PROGRAMS += headless-git$X | ||||||
| 	INSTALL = /bin/install | 	INSTALL = /bin/install | ||||||
| 	INTERNAL_QSORT = YesPlease | 	INTERNAL_QSORT = YesPlease | ||||||
| 	HAVE_LIBCHARSET_H = YesPlease | 	HAVE_LIBCHARSET_H = YesPlease | ||||||
|  |  | ||||||
|  | @ -738,6 +738,15 @@ if(WIN32) | ||||||
| 	else() | 	else() | ||||||
| 		message(FATAL_ERROR "Unhandled compiler: ${CMAKE_C_COMPILER_ID}") | 		message(FATAL_ERROR "Unhandled compiler: ${CMAKE_C_COMPILER_ID}") | ||||||
| 	endif() | 	endif() | ||||||
|  |  | ||||||
|  | 	add_executable(headless-git ${CMAKE_SOURCE_DIR}/compat/win32/headless.c) | ||||||
|  | 	if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") | ||||||
|  | 		target_link_options(headless-git PUBLIC -municode -Wl,-subsystem,windows) | ||||||
|  | 	elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC") | ||||||
|  | 		target_link_options(headless-git PUBLIC /NOLOGO /ENTRY:wWinMainCRTStartup /SUBSYSTEM:WINDOWS) | ||||||
|  | 	else() | ||||||
|  | 		message(FATAL_ERROR "Unhandled compiler: ${CMAKE_C_COMPILER_ID}") | ||||||
|  | 	endif() | ||||||
| elseif(UNIX) | elseif(UNIX) | ||||||
| 	target_link_libraries(common-main pthread rt) | 	target_link_libraries(common-main pthread rt) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | @ -76,7 +76,7 @@ sub createProject { | ||||||
|  |  | ||||||
|     my $libs_release = "\n    "; |     my $libs_release = "\n    "; | ||||||
|     my $libs_debug = "\n    "; |     my $libs_debug = "\n    "; | ||||||
|     if (!$static_library) { |     if (!$static_library && $name ne 'headless-git') { | ||||||
|       $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); |       $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); | ||||||
|       $libs_debug = $libs_release; |       $libs_debug = $libs_release; | ||||||
|       $libs_debug =~ s/zlib\.lib/zlibd\.lib/g; |       $libs_debug =~ s/zlib\.lib/zlibd\.lib/g; | ||||||
|  | @ -230,7 +230,7 @@ EOM | ||||||
|     print F << "EOM"; |     print F << "EOM"; | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| EOM | EOM | ||||||
|     if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') { |     if ((!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') && !($name =~ /headless-git/)) { | ||||||
|       my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"}; |       my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"}; | ||||||
|       my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"}; |       my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"}; | ||||||
|       my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"}; |       my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"}; | ||||||
|  |  | ||||||
|  | @ -371,6 +371,7 @@ sub handleLinkLine | ||||||
| #    exit(1); | #    exit(1); | ||||||
|     foreach (@objfiles) { |     foreach (@objfiles) { | ||||||
|         my $sourcefile = $_; |         my $sourcefile = $_; | ||||||
|  |         $sourcefile =~ s/^headless-git\.o$/compat\/win32\/headless.c/; | ||||||
|         $sourcefile =~ s/\.o$/.c/; |         $sourcefile =~ s/\.o$/.c/; | ||||||
|         push(@sources, $sourcefile); |         push(@sources, $sourcefile); | ||||||
|         push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}}); |         push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}}); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Johannes Schindelin
						Johannes Schindelin