Skip to content

Commit 8195f9a

Browse files
committed
Merge branch 'busybox-w32'
This topic branch brings slightly experimental changes supporting Git for Windows to use BusyBox-w32 to execute its shell scripts as well as its test suite. The test suite can be run by installing the test artifacts into a MinGit that has busybox.exe (and using Git for Windows' SDK's Perl for now, as the test suite requires Perl even when NO_PERL is set, go figure) by using the `install-mingit-test-artifacts` Makefile target with the DESTDIR variable pointing to the top-level directory of the MinGit installation. To facilitate running the test suite (without having `make` available, as `make.exe` is not part of MinGit), this branch brings an experimental patch to the `test-run-command` helper to run Git's test suite. It is still very experimental, though: in this developer's tests it seemed that the `poll()` emulation required for `run_parallel_processes()` to work sometimes hiccups on Windows, causing infinite "hangs". It is also possible that BusyBox itself has problems writing to the pipes opened by `test-run-command` (and merging this branch will help investigate further). Caveat emptor. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents b36526f + 4664221 commit 8195f9a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+641
-216
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*.perl eol=lf diff=perl
55
*.pl eof=lf diff=perl
66
*.pm eol=lf diff=perl
7+
*.png binary
78
*.py eol=lf diff=python
89
*.bat eol=crlf
910
/Documentation/git-*.txt eol=lf

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,7 @@ X =
703703
PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
704704

705705
TEST_BUILTINS_OBJS += test-chmtime.o
706+
TEST_BUILTINS_OBJS += test-cmp.o
706707
TEST_BUILTINS_OBJS += test-config.o
707708
TEST_BUILTINS_OBJS += test-ctype.o
708709
TEST_BUILTINS_OBJS += test-date.o
@@ -713,6 +714,7 @@ TEST_BUILTINS_OBJS += test-dump-split-index.o
713714
TEST_BUILTINS_OBJS += test-example-decorate.o
714715
TEST_BUILTINS_OBJS += test-genrandom.o
715716
TEST_BUILTINS_OBJS += test-hashmap.o
717+
TEST_BUILTINS_OBJS += test-iconv.o
716718
TEST_BUILTINS_OBJS += test-index-version.o
717719
TEST_BUILTINS_OBJS += test-json-writer.o
718720
TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o

compat/mingw.c

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "win32/lazyload.h"
1111
#include "../config.h"
1212
#include "../attr.h"
13+
#include "../string-list.h"
1314

1415
#define HCAST(type, handle) ((type)(intptr_t)handle)
1516

@@ -1374,6 +1375,65 @@ static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
13741375
return NULL;
13751376
}
13761377

1378+
static char *path_lookup(const char *cmd, int exe_only);
1379+
1380+
static char *is_busybox_applet(const char *cmd)
1381+
{
1382+
static struct string_list applets = STRING_LIST_INIT_DUP;
1383+
static char *busybox_path;
1384+
static int busybox_path_initialized;
1385+
1386+
/* Avoid infinite loop */
1387+
if (!strncasecmp(cmd, "busybox", 7) &&
1388+
(!cmd[7] || !strcasecmp(cmd + 7, ".exe")))
1389+
return NULL;
1390+
1391+
if (!busybox_path_initialized) {
1392+
busybox_path = path_lookup("busybox.exe", 1);
1393+
busybox_path_initialized = 1;
1394+
}
1395+
1396+
/* Assume that sh is compiled in... */
1397+
if (!busybox_path || !strcasecmp(cmd, "sh"))
1398+
return xstrdup_or_null(busybox_path);
1399+
1400+
if (!applets.nr) {
1401+
struct child_process cp = CHILD_PROCESS_INIT;
1402+
struct strbuf buf = STRBUF_INIT;
1403+
char *p;
1404+
1405+
argv_array_pushl(&cp.args, busybox_path, "--help", NULL);
1406+
1407+
if (capture_command(&cp, &buf, 2048)) {
1408+
string_list_append(&applets, "");
1409+
return NULL;
1410+
}
1411+
1412+
/* parse output */
1413+
p = strstr(buf.buf, "Currently defined functions:\n");
1414+
if (!p) {
1415+
warning("Could not parse output of busybox --help");
1416+
string_list_append(&applets, "");
1417+
return NULL;
1418+
}
1419+
p = strchrnul(p, '\n');
1420+
for (;;) {
1421+
size_t len;
1422+
1423+
p += strspn(p, "\n\t ,");
1424+
len = strcspn(p, "\n\t ,");
1425+
if (!len)
1426+
break;
1427+
p[len] = '\0';
1428+
string_list_insert(&applets, p);
1429+
p = p + len + 1;
1430+
}
1431+
}
1432+
1433+
return string_list_has_string(&applets, cmd) ?
1434+
xstrdup(busybox_path) : NULL;
1435+
}
1436+
13771437
/*
13781438
* Determines the absolute path of cmd using the split path in path.
13791439
* If cmd contains a slash or backslash, no lookup is performed.
@@ -1402,6 +1462,9 @@ static char *path_lookup(const char *cmd, int exe_only)
14021462
path = sep + 1;
14031463
}
14041464

1465+
if (!prog && !isexe)
1466+
prog = is_busybox_applet(cmd);
1467+
14051468
return prog;
14061469
}
14071470

@@ -1580,8 +1643,8 @@ static int is_msys2_sh(const char *cmd)
15801643
}
15811644

15821645
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
1583-
const char *dir,
1584-
int prepend_cmd, int fhin, int fhout, int fherr)
1646+
const char *dir, const char *prepend_cmd,
1647+
int fhin, int fhout, int fherr)
15851648
{
15861649
static int restrict_handle_inheritance = 1;
15871650
STARTUPINFOEXW si;
@@ -1655,9 +1718,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16551718
/* concatenate argv, quoting args as we go */
16561719
strbuf_init(&args, 0);
16571720
if (prepend_cmd) {
1658-
char *quoted = (char *)quote_arg(cmd);
1721+
char *quoted = (char *)quote_arg(prepend_cmd);
16591722
strbuf_addstr(&args, quoted);
1660-
if (quoted != cmd)
1723+
if (quoted != prepend_cmd)
16611724
free(quoted);
16621725
}
16631726
for (; *argv; argv++) {
@@ -1815,7 +1878,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
18151878
return (pid_t)pi.dwProcessId;
18161879
}
18171880

1818-
static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
1881+
static pid_t mingw_spawnv(const char *cmd, const char **argv,
1882+
const char *prepend_cmd)
18191883
{
18201884
return mingw_spawnve_fd(cmd, argv, NULL, NULL, prepend_cmd, 0, 1, 2);
18211885
}
@@ -1843,14 +1907,14 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
18431907
pid = -1;
18441908
}
18451909
else {
1846-
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, 1,
1910+
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, interpr,
18471911
fhin, fhout, fherr);
18481912
free(iprog);
18491913
}
18501914
argv[0] = argv0;
18511915
}
18521916
else
1853-
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, 0,
1917+
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, NULL,
18541918
fhin, fhout, fherr);
18551919
free(prog);
18561920
}
@@ -1876,7 +1940,7 @@ static int try_shell_exec(const char *cmd, char *const *argv)
18761940
ALLOC_ARRAY(argv2, argc + 1);
18771941
argv2[0] = (char *)cmd; /* full path to the script file */
18781942
memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
1879-
pid = mingw_spawnv(prog, argv2, 1);
1943+
pid = mingw_spawnv(prog, argv2, interpr);
18801944
if (pid >= 0) {
18811945
int status;
18821946
if (waitpid(pid, &status, 0) < 0)
@@ -1896,7 +1960,7 @@ int mingw_execv(const char *cmd, char *const *argv)
18961960
if (!try_shell_exec(cmd, argv)) {
18971961
int pid, status;
18981962

1899-
pid = mingw_spawnv(cmd, (const char **)argv, 0);
1963+
pid = mingw_spawnv(cmd, (const char **)argv, NULL);
19001964
if (pid < 0)
19011965
return -1;
19021966
if (waitpid(pid, &status, 0) < 0)

config.mak.uname

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,65 @@ else
649649
NO_CURL = YesPlease
650650
endif
651651
endif
652+
ifeq (i686,$(uname_M))
653+
MINGW_PREFIX := mingw32
654+
endif
655+
ifeq (x86_64,$(uname_M))
656+
MINGW_PREFIX := mingw64
657+
endif
658+
659+
DESTDIR_WINDOWS = $(shell cygpath -aw '$(DESTDIR_SQ)')
660+
DESTDIR_MIXED = $(shell cygpath -am '$(DESTDIR_SQ)')
661+
install-mingit-test-artifacts:
662+
install -m755 -d '$(DESTDIR_SQ)/usr/bin'
663+
printf '%s\n%s\n' >'$(DESTDIR_SQ)/usr/bin/perl' \
664+
"#!/mingw64/bin/busybox sh" \
665+
"exec \"$(shell cygpath -am /usr/bin/perl.exe)\" \"\$$@\""
666+
667+
install -m755 -d '$(DESTDIR_SQ)'
668+
printf '%s%s\n%s\n%s\n%s\n%s\n' >'$(DESTDIR_SQ)/init.bat' \
669+
"PATH=$(DESTDIR_WINDOWS)\\$(MINGW_PREFIX)\\bin;" \
670+
"C:\\WINDOWS;C:\\WINDOWS\\system32" \
671+
"@set GIT_TEST_INSTALLED=$(DESTDIR_MIXED)/$(MINGW_PREFIX)/bin" \
672+
"@`echo "$(DESTDIR_WINDOWS)" | sed 's/:.*/:/'`" \
673+
"@cd `echo "$(DESTDIR_WINDOWS)" | sed 's/^.://'`\\test-git\\t" \
674+
"@echo Now, run 'helper\\test-run-command testsuite'"
675+
676+
install -m755 -d '$(DESTDIR_SQ)/test-git'
677+
sed 's/^\(NO_PERL\|NO_PYTHON\)=.*/\1=YesPlease/' \
678+
<GIT-BUILD-OPTIONS >'$(DESTDIR_SQ)/test-git/GIT-BUILD-OPTIONS'
679+
680+
install -m755 -d '$(DESTDIR_SQ)/test-git/t/helper'
681+
install -m755 $(TEST_PROGRAMS) '$(DESTDIR_SQ)/test-git/t/helper'
682+
(cd t && $(TAR) cf - t[0-9][0-9][0-9][0-9] diff-lib) | \
683+
(cd '$(DESTDIR_SQ)/test-git/t' && $(TAR) xf -)
684+
install -m755 t/t556x_common t/*.sh '$(DESTDIR_SQ)/test-git/t'
685+
686+
install -m755 -d '$(DESTDIR_SQ)/test-git/templates'
687+
(cd templates && $(TAR) cf - blt) | \
688+
(cd '$(DESTDIR_SQ)/test-git/templates' && $(TAR) xf -)
689+
690+
# po/build/locale for t0200
691+
install -m755 -d '$(DESTDIR_SQ)/test-git/po/build/locale'
692+
(cd po/build/locale && $(TAR) cf - .) | \
693+
(cd '$(DESTDIR_SQ)/test-git/po/build/locale' && $(TAR) xf -)
694+
695+
# git-daemon.exe for t5802, git-http-backend.exe for t5560
696+
install -m755 -d '$(DESTDIR_SQ)/$(MINGW_PREFIX)/bin'
697+
install -m755 git-daemon.exe git-http-backend.exe \
698+
'$(DESTDIR_SQ)/$(MINGW_PREFIX)/bin'
699+
700+
# git-remote-testgit for t5801
701+
install -m755 -d '$(DESTDIR_SQ)/$(MINGW_PREFIX)/libexec/git-core'
702+
install -m755 git-remote-testgit \
703+
'$(DESTDIR_SQ)/$(MINGW_PREFIX)/libexec/git-core'
704+
705+
# git-upload-archive (dashed) for t5000
706+
install -m755 git-upload-archive.exe '$(DESTDIR_SQ)/$(MINGW_PREFIX)/bin'
707+
708+
# git-difftool--helper for t7800
709+
install -m755 git-difftool--helper \
710+
'$(DESTDIR_SQ)/$(MINGW_PREFIX)/libexec/git-core'
652711
endif
653712
ifeq ($(uname_S),QNX)
654713
COMPAT_CFLAGS += -DSA_RESTART=0

git-sh-setup.sh

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -332,17 +332,30 @@ create_virtual_base() {
332332
# Platform specific tweaks to work around some commands
333333
case $(uname -s) in
334334
*MINGW*)
335-
# Windows has its own (incompatible) sort and find
336-
sort () {
337-
/usr/bin/sort "$@"
338-
}
339-
find () {
340-
/usr/bin/find "$@"
341-
}
342-
# git sees Windows-style pwd
343-
pwd () {
344-
builtin pwd -W
345-
}
335+
if test -x /usr/bin/sort
336+
then
337+
# Windows has its own (incompatible) sort; override
338+
sort () {
339+
/usr/bin/sort "$@"
340+
}
341+
fi
342+
if test -x /usr/bin/find
343+
then
344+
# Windows has its own (incompatible) find; override
345+
find () {
346+
/usr/bin/find "$@"
347+
}
348+
fi
349+
# On Windows, Git wants Windows paths. But /usr/bin/pwd spits out
350+
# Unix-style paths. At least in Bash, we have a builtin pwd that
351+
# understands the -W option to force "mixed" paths, i.e. with drive
352+
# prefix but still with forward slashes. Let's use that, if available.
353+
if type builtin >/dev/null 2>&1
354+
then
355+
pwd () {
356+
builtin pwd -W
357+
}
358+
fi
346359
is_absolute_path () {
347360
case "$1" in
348361
[/\\]* | [A-Za-z]:*)
File renamed without changes.
File renamed without changes.

t/helper/test-cmp.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include "test-tool.h"
2+
#include "git-compat-util.h"
3+
#include "strbuf.h"
4+
#include "gettext.h"
5+
#include "parse-options.h"
6+
#include "run-command.h"
7+
8+
#ifdef WIN32
9+
#define NO_SUCH_DIR "\\\\.\\GLOBALROOT\\invalid"
10+
#else
11+
#define NO_SUCH_DIR "/dev/null"
12+
#endif
13+
14+
static int run_diff(const char *path1, const char *path2)
15+
{
16+
const char *argv[] = {
17+
"diff", "--no-index", NULL, NULL, NULL
18+
};
19+
const char *env[] = {
20+
"GIT_PAGER=cat",
21+
"GIT_DIR=" NO_SUCH_DIR,
22+
"HOME=" NO_SUCH_DIR,
23+
NULL
24+
};
25+
26+
argv[2] = path1;
27+
argv[3] = path2;
28+
return run_command_v_opt_cd_env(argv,
29+
RUN_COMMAND_NO_STDIN | RUN_GIT_CMD,
30+
NULL, env);
31+
}
32+
33+
int cmd__cmp(int argc, const char **argv)
34+
{
35+
FILE *f0, *f1;
36+
struct strbuf b0 = STRBUF_INIT, b1 = STRBUF_INIT;
37+
38+
if (argc != 3)
39+
die("Require exactly 2 arguments, got %d", argc);
40+
41+
if (!(f0 = !strcmp(argv[1], "-") ? stdin : fopen(argv[1], "r")))
42+
return error_errno("could not open '%s'", argv[1]);
43+
if (!(f1 = !strcmp(argv[2], "-") ? stdin : fopen(argv[2], "r"))) {
44+
fclose(f0);
45+
return error_errno("could not open '%s'", argv[2]);
46+
}
47+
48+
for (;;) {
49+
int r0 = strbuf_getline(&b0, f0);
50+
int r1 = strbuf_getline(&b1, f1);
51+
52+
if (r0 == EOF) {
53+
fclose(f0);
54+
fclose(f1);
55+
strbuf_release(&b0);
56+
strbuf_release(&b1);
57+
if (r1 == EOF)
58+
return 0;
59+
cmp_failed:
60+
if (!run_diff(argv[1], argv[2]))
61+
die("Huh? 'diff --no-index %s %s' succeeded",
62+
argv[1], argv[2]);
63+
return 1;
64+
}
65+
if (r1 == EOF || strbuf_cmp(&b0, &b1)) {
66+
fclose(f0);
67+
fclose(f1);
68+
strbuf_release(&b0);
69+
strbuf_release(&b1);
70+
goto cmp_failed;
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)