From a3d71c1aad073200de8259e4e9747091e0fcbf37 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 5 Apr 2021 12:15:00 -0400 Subject: [PATCH 01/12] fixup! win32: add a helper to run `git.exe` without a foreground window Revert this in preparation for re-doing it after fixing a couple of CMake issues. Signed-off-by: Jeff Hostetler --- Makefile | 7 -- compat/win32/headless.c | 114 ---------------------------- config.mak.uname | 3 - contrib/buildsystems/CMakeLists.txt | 3 - 4 files changed, 127 deletions(-) delete mode 100644 compat/win32/headless.c diff --git a/Makefile b/Makefile index 3275125aa6..f3dc217832 100644 --- a/Makefile +++ b/Makefile @@ -2504,13 +2504,6 @@ compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \ compat/nedmalloc/nedmalloc.sp: SP_EXTRA_FLAGS += -Wno-non-pointer-null endif -headless-git.o: compat/win32/headless.c - $(QUIET_CC)$(CC) $(ALL_CFLAGS) $(COMPAT_CFLAGS) \ - -fno-stack-protector -o $@ -c -Wall -Wwrite-strings $< - -headless-git$X: headless-git.o git.res - $(QUIET_LINK)$(CC) $(ALL_LDFLAGS) -mwindows $(COMPAT_CFLAGS) -o $@ $^ - git-%$X: %.o GIT-LDFLAGS $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) diff --git a/compat/win32/headless.c b/compat/win32/headless.c deleted file mode 100644 index d04ddc0795..0000000000 --- a/compat/win32/headless.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * headless Git - run Git without opening a console window on Windows - */ - -#define STRICT -#define WIN32_LEAN_AND_MEAN -#define UNICODE -#define _UNICODE -#include -#include -#include -#include - -/* - * 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(HINSTANCE instance, HINSTANCE previous_instance, - wchar_t *command_line, 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 = { - .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 + 11 >= sizeof(git_command_line) / sizeof(wchar_t)) - 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.cb = sizeof(STARTUPINFO); - - 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)) { - CloseHandle(process_info.hProcess); - return 130; /* Could not determine exit code? */ - } - - return (int)exit_code; -} diff --git a/config.mak.uname b/config.mak.uname index c524d2b1f1..d204c20a64 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -484,8 +484,6 @@ else endif X = .exe - EXTRA_PROGRAMS += headless-git$X - compat/msvc.o: compat/msvc.c compat/mingw.c GIT-CFLAGS endif ifeq ($(uname_S),Interix) @@ -627,7 +625,6 @@ ifneq (,$(findstring MINGW,$(uname_S))) RC = windres -O coff NATIVE_CRLF = YesPlease X = .exe - EXTRA_PROGRAMS += headless-git$X ifneq (,$(wildcard ../THIS_IS_MSYSGIT)) htmldir = doc/git/html/ prefix = diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index 1621dabc33..ac3dbc079a 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -642,9 +642,6 @@ if(WIN32) else() message(FATAL_ERROR "Unhandled compiler: ${CMAKE_C_COMPILER_ID}") endif() - - add_executable(headless-git ${CMAKE_SOURCE_DIR}/compat/win32/headless.c) - target_link_options(headless-git PUBLIC /NOLOGO /ENTRY:wWinMainCRTStartup /SUBSYSTEM:WINDOWS) elseif(UNIX) target_link_libraries(common-main pthread rt) endif() From e927933e103c5253f682750aab5247f2ee5b8071 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Tue, 30 Mar 2021 14:25:31 -0400 Subject: [PATCH 02/12] clink.pl: fix libexpatd.lib link error when using MSVC When building with `make MSVC=1 DEBUG=1`, link to `libexpatd.lib` rather than `libexpat.lib`. It appears that the `vcpkg` package for "libexpat" has changed and now creates `libexpatd.lib` for debug mode builds. Previously, both debug and release builds created a ".lib" with the same basename. Signed-off-by: Jeff Hostetler --- compat/vcbuild/scripts/clink.pl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl index 3bd824154b..2768ae15f1 100755 --- a/compat/vcbuild/scripts/clink.pl +++ b/compat/vcbuild/scripts/clink.pl @@ -66,7 +66,11 @@ while (@ARGV) { } push(@args, $lib); } elsif ("$arg" eq "-lexpat") { + if ($is_debug) { + push(@args, "libexpatd.lib"); + } else { push(@args, "libexpat.lib"); + } } elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") { $arg =~ s/^-L/-LIBPATH:/; push(@lflags, $arg); From b76dee57dc1658f737c4c3d456ecb98ba37947b9 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 5 Apr 2021 15:27:38 -0400 Subject: [PATCH 03/12] Makefile: clean up .ilk files when MSVC=1 Signed-off-by: Jeff Hostetler --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index f3dc217832..151a0684a6 100644 --- a/Makefile +++ b/Makefile @@ -3171,12 +3171,15 @@ endif ifdef MSVC $(RM) $(patsubst %.o,%.o.pdb,$(OBJECTS)) $(RM) $(patsubst %.exe,%.pdb,$(OTHER_PROGRAMS)) + $(RM) $(patsubst %.exe,%.ilk,$(OTHER_PROGRAMS)) $(RM) $(patsubst %.exe,%.iobj,$(OTHER_PROGRAMS)) $(RM) $(patsubst %.exe,%.ipdb,$(OTHER_PROGRAMS)) $(RM) $(patsubst %.exe,%.pdb,$(PROGRAMS)) + $(RM) $(patsubst %.exe,%.ilk,$(PROGRAMS)) $(RM) $(patsubst %.exe,%.iobj,$(PROGRAMS)) $(RM) $(patsubst %.exe,%.ipdb,$(PROGRAMS)) $(RM) $(patsubst %.exe,%.pdb,$(TEST_PROGRAMS)) + $(RM) $(patsubst %.exe,%.ilk,$(TEST_PROGRAMS)) $(RM) $(patsubst %.exe,%.iobj,$(TEST_PROGRAMS)) $(RM) $(patsubst %.exe,%.ipdb,$(TEST_PROGRAMS)) $(RM) compat/vcbuild/MSVC-DEFS-GEN From cb7fd5c7c2ce311ee21ea4442fbab931082b0e84 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 5 Apr 2021 14:08:22 -0400 Subject: [PATCH 04/12] vcbuild: add support for compiling Windows resource files Create a wrapper for the Windows Resource Compiler (RC.EXE) for use by the MSVC=1 builds. This is similar to the CL.EXE and LIB.EXE wrappers used for the MSVC=1 builds. Signed-off-by: Jeff Hostetler --- compat/vcbuild/find_vs_env.bat | 7 ++++++ compat/vcbuild/scripts/rc.pl | 46 ++++++++++++++++++++++++++++++++++ config.mak.uname | 3 ++- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 compat/vcbuild/scripts/rc.pl diff --git a/compat/vcbuild/find_vs_env.bat b/compat/vcbuild/find_vs_env.bat index b35d264c0e..379b16296e 100644 --- a/compat/vcbuild/find_vs_env.bat +++ b/compat/vcbuild/find_vs_env.bat @@ -99,6 +99,7 @@ REM ================================================================ SET sdk_dir=%WindowsSdkDir% SET sdk_ver=%WindowsSDKVersion% + SET sdk_ver_bin_dir=%WindowsSdkVerBinPath%%tgt% SET si=%sdk_dir%Include\%sdk_ver% SET sdk_includes=-I"%si%ucrt" -I"%si%um" -I"%si%shared" SET sl=%sdk_dir%lib\%sdk_ver% @@ -130,6 +131,7 @@ REM ================================================================ SET sdk_dir=%WindowsSdkDir% SET sdk_ver=%WindowsSDKVersion% + SET sdk_ver_bin_dir=%WindowsSdkVerBinPath%bin\amd64 SET si=%sdk_dir%Include\%sdk_ver% SET sdk_includes=-I"%si%ucrt" -I"%si%um" -I"%si%shared" -I"%si%winrt" SET sl=%sdk_dir%lib\%sdk_ver% @@ -160,6 +162,11 @@ REM ================================================================ echo msvc_includes=%msvc_includes% echo msvc_libs=%msvc_libs% + echo sdk_ver_bin_dir=%sdk_ver_bin_dir% + SET X1=%sdk_ver_bin_dir:C:=/C% + SET X2=%X1:\=/% + echo sdk_ver_bin_dir_msys=%X2% + echo sdk_includes=%sdk_includes% echo sdk_libs=%sdk_libs% diff --git a/compat/vcbuild/scripts/rc.pl b/compat/vcbuild/scripts/rc.pl new file mode 100644 index 0000000000..7bca4cd81c --- /dev/null +++ b/compat/vcbuild/scripts/rc.pl @@ -0,0 +1,46 @@ +#!/usr/bin/perl -w +###################################################################### +# Compile Resources on Windows +# +# This is a wrapper to facilitate the compilation of Git with MSVC +# using GNU Make as the build system. So, instead of manipulating the +# Makefile into something nasty, just to support non-space arguments +# etc, we use this wrapper to fix the command line options +# +###################################################################### +use strict; +my @args = (); +my @input = (); + +while (@ARGV) { + my $arg = shift @ARGV; + if ("$arg" =~ /^-[dD]/) { + # GIT_VERSION gets passed with too many + # layers of dquote escaping. + $arg =~ s/\\"/"/g; + + push(@args, $arg); + + } elsif ("$arg" eq "-i") { + my $arg = shift @ARGV; + # TODO complain if NULL or is dashed ?? + push(@input, $arg); + + } elsif ("$arg" eq "-o") { + my $arg = shift @ARGV; + # TODO complain if NULL or is dashed ?? + push(@args, "-fo$arg"); + + } else { + push(@args, $arg); + } +} + +push(@args, "-nologo"); +push(@args, "-v"); +push(@args, @input); + +unshift(@args, "rc.exe"); +printf("**** @args\n"); + +exit (system(@args) != 0); diff --git a/config.mak.uname b/config.mak.uname index d204c20a64..ba34430667 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -399,7 +399,7 @@ ifeq ($(uname_S),Windows) # link.exe next to, and required by, cl.exe, we have to prepend this # onto the existing $PATH. # - SANE_TOOL_PATH ?= $(msvc_bin_dir_msys) + SANE_TOOL_PATH ?= $(msvc_bin_dir_msys):$(sdk_ver_bin_dir_msys) HAVE_ALLOCA_H = YesPlease NO_PREAD = YesPlease NEEDS_CRYPTO_WITH_SSL = YesPlease @@ -453,6 +453,7 @@ ifeq ($(uname_S),Windows) # See https://msdn.microsoft.com/en-us/library/ms235330.aspx EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj kernel32.lib ntdll.lib PTHREAD_LIBS = + RC = compat/vcbuild/scripts/rc.pl lib = BASIC_CFLAGS += $(vcpkg_inc) $(sdk_includes) $(msvc_includes) ifndef DEBUG From f4d586354526cbf940abbfee29e027d02fb5febd Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 5 Apr 2021 14:12:14 -0400 Subject: [PATCH 05/12] config.mak.uname: add git.rc to MSVC builds Teach MSVC=1 builds to depend on the `git.rc` file so that the resulting executables have Windows-style resources and version number information within them. Signed-off-by: Jeff Hostetler --- config.mak.uname | 1 + 1 file changed, 1 insertion(+) diff --git a/config.mak.uname b/config.mak.uname index ba34430667..457f5cab59 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -452,6 +452,7 @@ ifeq ($(uname_S),Windows) # handle twice, or to access the osfhandle of an already-closed stdout # See https://msdn.microsoft.com/en-us/library/ms235330.aspx EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj kernel32.lib ntdll.lib + GITLIBS += git.res PTHREAD_LIBS = RC = compat/vcbuild/scripts/rc.pl lib = From ef4dcda7175eaa81c8cda6f39913550b5e4eae8e Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 5 Apr 2021 14:24:52 -0400 Subject: [PATCH 06/12] clink.pl: ignore no-stack-protector arg on MSVC=1 builds Ignore the `-fno-stack-protector` compiler argument when building with MSVC. This will be used in a later commit that needs to build a Win32 GUI app. Signed-off-by: Jeff Hostetler --- compat/vcbuild/scripts/clink.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl index 2768ae15f1..73c8a2b184 100755 --- a/compat/vcbuild/scripts/clink.pl +++ b/compat/vcbuild/scripts/clink.pl @@ -122,6 +122,8 @@ while (@ARGV) { push(@cflags, "-wd4996"); } elsif ("$arg" =~ /^-W[a-z]/) { # let's ignore those + } elsif ("$arg" eq "-fno-stack-protector") { + # eat this } else { push(@args, $arg); } From 0b7610063b03411be77b5e163fe382b5e6ba1421 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 5 Apr 2021 14:39:33 -0400 Subject: [PATCH 07/12] clink.pl: move default linker options for MSVC=1 builds Move the default `-ENTRY` and `-SUBSYSTEM` arguments for MSVC=1 builds from `config.mak.uname` into `clink.pl`. These args are constant for console-mode executables. Add support to `clink.pl` for generating a Win32 GUI application using the `-mwindows` argument (to match how GCC does it). This changes the `-ENTRY` and `-SUBSYSTEM` arguments accordingly. Signed-off-by: Jeff Hostetler --- compat/vcbuild/scripts/clink.pl | 11 +++++++++++ config.mak.uname | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl index 73c8a2b184..a38b360015 100755 --- a/compat/vcbuild/scripts/clink.pl +++ b/compat/vcbuild/scripts/clink.pl @@ -15,6 +15,7 @@ my @cflags = (); my @lflags = (); my $is_linking = 0; my $is_debug = 0; +my $is_gui = 0; while (@ARGV) { my $arg = shift @ARGV; if ("$arg" eq "-DDEBUG") { @@ -124,11 +125,21 @@ while (@ARGV) { # let's ignore those } elsif ("$arg" eq "-fno-stack-protector") { # eat this + } elsif ("$arg" eq "-mwindows") { + $is_gui = 1; } else { push(@args, $arg); } } if ($is_linking) { + if ($is_gui) { + push(@args, "-ENTRY:wWinMainCRTStartup"); + push(@args, "-SUBSYSTEM:WINDOWS"); + } else { + push(@args, "-ENTRY:wmainCRTStartup"); + push(@args, "-SUBSYSTEM:CONSOLE"); + } + push(@args, @lflags); unshift(@args, "link.exe"); } else { diff --git a/config.mak.uname b/config.mak.uname index 457f5cab59..1d57fc5aab 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -447,7 +447,7 @@ ifeq ($(uname_S),Windows) compat/win32/trace2_win32_process_info.o \ compat/win32/dirent.o COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DDETECT_MSYS_TTY -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" - BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -ENTRY:wmainCRTStartup -SUBSYSTEM:CONSOLE + BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO # invalidcontinue.obj allows Git's source code to close the same file # handle twice, or to access the osfhandle of an already-closed stdout # See https://msdn.microsoft.com/en-us/library/ms235330.aspx From 67a5d6e564fb2d7f3ce1cae40ec4c78938f253cb Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 7 Apr 2021 15:29:21 +0200 Subject: [PATCH 08/12] buildsystems: remove duplicate clause This seems to have been there since 259d87c35495 (Add scripts to generate projects for other buildsystems (MSVC vcproj, QMake), 2009-09-16), i.e. since the beginning of that file. Signed-off-by: Johannes Schindelin --- contrib/buildsystems/engine.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl index ed6c45988a..37b0b026f2 100755 --- a/contrib/buildsystems/engine.pl +++ b/contrib/buildsystems/engine.pl @@ -265,7 +265,6 @@ sub handleCompileLine shift @parts; } elsif ("$part" eq "-c") { # ignore compile flag - } elsif ("$part" eq "-c") { } elsif ($part =~ /^.?-I/) { push(@incpaths, $part); } elsif ($part =~ /^.?-D/) { From b7db310862a1d27cdfbaf21f1537b5d0dbbee9f4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 7 Apr 2021 15:15:08 +0200 Subject: [PATCH 09/12] vcxproj: handle resource files, too On Windows, we also compile a "resource" file, which is similar to source code, but contains metadata (such as the program version). So far, we did not compile it in `MSVC` mode, only when compiling Git for Windows with the GNU C Compiler. In preparation for including it also when compiling with MS Visual C, let's teach our `vcxproj` generator to handle those sort of files, too. Signed-off-by: Johannes Schindelin --- contrib/buildsystems/Generators/Vcxproj.pm | 17 ++++++++++++++++- contrib/buildsystems/engine.pl | 9 +++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/contrib/buildsystems/Generators/Vcxproj.pm b/contrib/buildsystems/Generators/Vcxproj.pm index d2584450ba..a440b4b6ff 100644 --- a/contrib/buildsystems/Generators/Vcxproj.pm +++ b/contrib/buildsystems/Generators/Vcxproj.pm @@ -89,6 +89,9 @@ sub createProject { $defines =~ s/>/>/g; $defines =~ s/\'//g; + my $rcdefines = $defines; + $rcdefines =~ s/(?$vcxproj" or die "Could not open $vcxproj for writing!\n"; @@ -201,6 +204,9 @@ EOM WIN32;_DEBUG;$defines;%(PreprocessorDefinitions) MultiThreadedDebugDLL + + WIN32;_DEBUG;$rcdefines;%(PreprocessorDefinitions) + true @@ -214,6 +220,9 @@ EOM true Speed + + WIN32;NDEBUG;$rcdefines;%(PreprocessorDefinitions) + true true @@ -223,9 +232,15 @@ EOM EOM foreach(@sources) { - print F << "EOM"; + if (/\.rc$/) { + print F << "EOM"; + +EOM + } else { + print F << "EOM"; EOM + } } print F << "EOM"; diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl index 37b0b026f2..441059a737 100755 --- a/contrib/buildsystems/engine.pl +++ b/contrib/buildsystems/engine.pl @@ -165,7 +165,7 @@ sub parseMakeOutput next; } - if($text =~ / -c /) { + if($text =~ / -c / || $text =~ / -i \S+\.rc /) { # compilation handleCompileLine($text, $line); @@ -263,7 +263,7 @@ sub handleCompileLine if ("$part" eq "-o") { # ignore object file shift @parts; - } elsif ("$part" eq "-c") { + } elsif ("$part" eq "-c" || "$part" eq "-i") { # ignore compile flag } elsif ($part =~ /^.?-I/) { push(@incpaths, $part); @@ -271,7 +271,7 @@ sub handleCompileLine push(@defines, $part); } elsif ($part =~ /^-/) { push(@cflags, $part); - } elsif ($part =~ /\.(c|cc|cpp)$/) { + } elsif ($part =~ /\.(c|cc|cpp|rc)$/) { $sourcefile = $part; } else { die "Unhandled compiler option @ line $lineno: $part"; @@ -358,7 +358,7 @@ sub handleLinkLine push(@libs, $part); } elsif ($part eq 'invalidcontinue.obj') { # ignore - known to MSVC - } elsif ($part =~ /\.o$/) { + } elsif ($part =~ /\.(o|res)$/) { push(@objfiles, $part); } elsif ($part =~ /\.obj$/) { # do nothing, 'make' should not be producing .obj, only .o files @@ -371,6 +371,7 @@ sub handleLinkLine foreach (@objfiles) { my $sourcefile = $_; $sourcefile =~ s/\.o$/.c/; + $sourcefile =~ s/\.res$/.rc/; push(@sources, $sourcefile); push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}}); push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}}); From 2865ae9f55c76a45c58669f068f71a7ccb957b47 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 7 Apr 2021 21:57:31 +0200 Subject: [PATCH 10/12] vcxproj: ignore -fno-stack-protector and -fno-common An upcoming commit will introduce those compile options; MSVC does not understand them, so let's suppress them when generating the Visual Studio project files. Signed-off-by: Johannes Schindelin --- contrib/buildsystems/engine.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl index 441059a737..7453bc9697 100755 --- a/contrib/buildsystems/engine.pl +++ b/contrib/buildsystems/engine.pl @@ -263,7 +263,7 @@ sub handleCompileLine if ("$part" eq "-o") { # ignore object file shift @parts; - } elsif ("$part" eq "-c" || "$part" eq "-i") { + } elsif ("$part" eq "-c" || "$part" eq "-i" || "$part" =~ /^-fno-/) { # ignore compile flag } elsif ($part =~ /^.?-I/) { push(@incpaths, $part); From dc255fe453c08788ad209e7e4fbba232efe8650d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 7 Apr 2021 15:48:50 +0200 Subject: [PATCH 11/12] vcxproj: handle GUI programs, too So far, we only built Console programs, but we are about to introduce a program that targets the Windows subsystem (i.e. it is a so-called "GUI" program). Let's handle this preemptively in the script that generates the Visual Studio files. Signed-off-by: Johannes Schindelin --- contrib/buildsystems/Generators/Vcxproj.pm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/contrib/buildsystems/Generators/Vcxproj.pm b/contrib/buildsystems/Generators/Vcxproj.pm index a440b4b6ff..151d02ce80 100644 --- a/contrib/buildsystems/Generators/Vcxproj.pm +++ b/contrib/buildsystems/Generators/Vcxproj.pm @@ -92,6 +92,13 @@ sub createProject { my $rcdefines = $defines; $rcdefines =~ s/(?$vcxproj" or die "Could not open $vcxproj for writing!\n"; @@ -177,9 +184,9 @@ sub createProject { \$(VCPKGLibDirectory);%(AdditionalLibraryDirectories) \$(VCPKGLibs);\$(AdditionalDependencies) invalidcontinue.obj %(AdditionalOptions) - wmainCRTStartup + $entrypoint $cdup\\compat\\win32\\git.manifest - Console + $subsystem EOM if ($target eq 'libgit') { From fcd635d908e679b3bf1a26661a778ee6be04f559 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 3 Dec 2020 22:44:24 +0100 Subject: [PATCH 12/12] 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. Signed-off-by: Johannes Schindelin Signed-off-by: Derrick Stolee --- Makefile | 9 ++ compat/win32/headless.c | 114 +++++++++++++++++++++ config.mak.uname | 3 + contrib/buildsystems/CMakeLists.txt | 3 + contrib/buildsystems/Generators/Vcxproj.pm | 4 +- contrib/buildsystems/engine.pl | 1 + 6 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 compat/win32/headless.c diff --git a/Makefile b/Makefile index 151a0684a6..7b02380547 100644 --- a/Makefile +++ b/Makefile @@ -2504,6 +2504,13 @@ compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \ compat/nedmalloc/nedmalloc.sp: SP_EXTRA_FLAGS += -Wno-non-pointer-null 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) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) @@ -3142,6 +3149,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) *.res $(RM) $(OBJECTS) + $(RM) headless-git.o $(RM) $(LIB_FILE) $(XDIFF_LIB) $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) @@ -3170,6 +3178,7 @@ endif $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS ifdef MSVC $(RM) $(patsubst %.o,%.o.pdb,$(OBJECTS)) + $(RM) headless-git.o.pdb $(RM) $(patsubst %.exe,%.pdb,$(OTHER_PROGRAMS)) $(RM) $(patsubst %.exe,%.ilk,$(OTHER_PROGRAMS)) $(RM) $(patsubst %.exe,%.iobj,$(OTHER_PROGRAMS)) diff --git a/compat/win32/headless.c b/compat/win32/headless.c new file mode 100644 index 0000000000..d04ddc0795 --- /dev/null +++ b/compat/win32/headless.c @@ -0,0 +1,114 @@ +/* + * headless Git - run Git without opening a console window on Windows + */ + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#define UNICODE +#define _UNICODE +#include +#include +#include +#include + +/* + * 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(HINSTANCE instance, HINSTANCE previous_instance, + wchar_t *command_line, 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 = { + .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 + 11 >= sizeof(git_command_line) / sizeof(wchar_t)) + 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.cb = sizeof(STARTUPINFO); + + 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)) { + CloseHandle(process_info.hProcess); + return 130; /* Could not determine exit code? */ + } + + return (int)exit_code; +} diff --git a/config.mak.uname b/config.mak.uname index 1d57fc5aab..f16c86b2c9 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -486,6 +486,8 @@ else endif X = .exe + EXTRA_PROGRAMS += headless-git$X + compat/msvc.o: compat/msvc.c compat/mingw.c GIT-CFLAGS endif ifeq ($(uname_S),Interix) @@ -627,6 +629,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) RC = windres -O coff NATIVE_CRLF = YesPlease X = .exe + EXTRA_PROGRAMS += headless-git$X ifneq (,$(wildcard ../THIS_IS_MSYSGIT)) htmldir = doc/git/html/ prefix = diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index ac3dbc079a..1621dabc33 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -642,6 +642,9 @@ if(WIN32) else() message(FATAL_ERROR "Unhandled compiler: ${CMAKE_C_COMPILER_ID}") endif() + + add_executable(headless-git ${CMAKE_SOURCE_DIR}/compat/win32/headless.c) + target_link_options(headless-git PUBLIC /NOLOGO /ENTRY:wWinMainCRTStartup /SUBSYSTEM:WINDOWS) elseif(UNIX) target_link_libraries(common-main pthread rt) endif() diff --git a/contrib/buildsystems/Generators/Vcxproj.pm b/contrib/buildsystems/Generators/Vcxproj.pm index 151d02ce80..5d2a9d87de 100644 --- a/contrib/buildsystems/Generators/Vcxproj.pm +++ b/contrib/buildsystems/Generators/Vcxproj.pm @@ -76,7 +76,7 @@ sub createProject { my $libs_release = "\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)/, @{$$build_structure{"$prefix${name}_LIBS"}})); $libs_debug = $libs_release; $libs_debug =~ s/zlib\.lib/zlibd\.lib/g; @@ -252,7 +252,7 @@ EOM print F << "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_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"}; diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl index 7453bc9697..417ae71d44 100755 --- a/contrib/buildsystems/engine.pl +++ b/contrib/buildsystems/engine.pl @@ -370,6 +370,7 @@ sub handleLinkLine # exit(1); foreach (@objfiles) { my $sourcefile = $_; + $sourcefile =~ s/^headless-git\.o$/compat\/win32\/headless.c/; $sourcefile =~ s/\.o$/.c/; $sourcefile =~ s/\.res$/.rc/; push(@sources, $sourcefile);