Skip to content

Commit 8f68423

Browse files
dschojamill
authored andcommitted
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 3e67999 + 45b8422 commit 8f68423

Some content is hidden

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

57 files changed

+646
-222
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
/Documentation/git-*.txt eol=lf
910
/command-list.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
@@ -9,6 +9,7 @@
99
#include "win32/lazyload.h"
1010
#include "win32/exit-process.h"
1111
#include "../config.h"
12+
#include "../string-list.h"
1213

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

@@ -1297,6 +1298,65 @@ static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
12971298
return NULL;
12981299
}
12991300

1301+
static char *path_lookup(const char *cmd, int exe_only);
1302+
1303+
static char *is_busybox_applet(const char *cmd)
1304+
{
1305+
static struct string_list applets = STRING_LIST_INIT_DUP;
1306+
static char *busybox_path;
1307+
static int busybox_path_initialized;
1308+
1309+
/* Avoid infinite loop */
1310+
if (!strncasecmp(cmd, "busybox", 7) &&
1311+
(!cmd[7] || !strcasecmp(cmd + 7, ".exe")))
1312+
return NULL;
1313+
1314+
if (!busybox_path_initialized) {
1315+
busybox_path = path_lookup("busybox.exe", 1);
1316+
busybox_path_initialized = 1;
1317+
}
1318+
1319+
/* Assume that sh is compiled in... */
1320+
if (!busybox_path || !strcasecmp(cmd, "sh"))
1321+
return xstrdup_or_null(busybox_path);
1322+
1323+
if (!applets.nr) {
1324+
struct child_process cp = CHILD_PROCESS_INIT;
1325+
struct strbuf buf = STRBUF_INIT;
1326+
char *p;
1327+
1328+
argv_array_pushl(&cp.args, busybox_path, "--help", NULL);
1329+
1330+
if (capture_command(&cp, &buf, 2048)) {
1331+
string_list_append(&applets, "");
1332+
return NULL;
1333+
}
1334+
1335+
/* parse output */
1336+
p = strstr(buf.buf, "Currently defined functions:\n");
1337+
if (!p) {
1338+
warning("Could not parse output of busybox --help");
1339+
string_list_append(&applets, "");
1340+
return NULL;
1341+
}
1342+
p = strchrnul(p, '\n');
1343+
for (;;) {
1344+
size_t len;
1345+
1346+
p += strspn(p, "\n\t ,");
1347+
len = strcspn(p, "\n\t ,");
1348+
if (!len)
1349+
break;
1350+
p[len] = '\0';
1351+
string_list_insert(&applets, p);
1352+
p = p + len + 1;
1353+
}
1354+
}
1355+
1356+
return string_list_has_string(&applets, cmd) ?
1357+
xstrdup(busybox_path) : NULL;
1358+
}
1359+
13001360
/*
13011361
* Determines the absolute path of cmd using the split path in path.
13021362
* If cmd contains a slash or backslash, no lookup is performed.
@@ -1325,6 +1385,9 @@ static char *path_lookup(const char *cmd, int exe_only)
13251385
path = sep + 1;
13261386
}
13271387

1388+
if (!prog && !isexe)
1389+
prog = is_busybox_applet(cmd);
1390+
13281391
return prog;
13291392
}
13301393

@@ -1575,8 +1638,8 @@ static int is_msys2_sh(const char *cmd)
15751638
}
15761639

15771640
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
1578-
const char *dir,
1579-
int prepend_cmd, int fhin, int fhout, int fherr)
1641+
const char *dir, const char *prepend_cmd,
1642+
int fhin, int fhout, int fherr)
15801643
{
15811644
STARTUPINFOW si;
15821645
PROCESS_INFORMATION pi;
@@ -1633,9 +1696,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16331696
/* concatenate argv, quoting args as we go */
16341697
strbuf_init(&args, 0);
16351698
if (prepend_cmd) {
1636-
char *quoted = (char *)quote_arg(cmd);
1699+
char *quoted = (char *)quote_arg(prepend_cmd);
16371700
strbuf_addstr(&args, quoted);
1638-
if (quoted != cmd)
1701+
if (quoted != prepend_cmd)
16391702
free(quoted);
16401703
}
16411704
for (; *argv; argv++) {
@@ -1712,7 +1775,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17121775
return (pid_t)pi.dwProcessId;
17131776
}
17141777

1715-
static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
1778+
static pid_t mingw_spawnv(const char *cmd, const char **argv,
1779+
const char *prepend_cmd)
17161780
{
17171781
return mingw_spawnve_fd(cmd, argv, NULL, NULL, prepend_cmd, 0, 1, 2);
17181782
}
@@ -1740,14 +1804,14 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
17401804
pid = -1;
17411805
}
17421806
else {
1743-
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, 1,
1807+
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, interpr,
17441808
fhin, fhout, fherr);
17451809
free(iprog);
17461810
}
17471811
argv[0] = argv0;
17481812
}
17491813
else
1750-
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, 0,
1814+
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, NULL,
17511815
fhin, fhout, fherr);
17521816
free(prog);
17531817
}
@@ -1773,7 +1837,7 @@ static int try_shell_exec(const char *cmd, char *const *argv)
17731837
ALLOC_ARRAY(argv2, argc + 1);
17741838
argv2[0] = (char *)cmd; /* full path to the script file */
17751839
memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
1776-
pid = mingw_spawnv(prog, argv2, 1);
1840+
pid = mingw_spawnv(prog, argv2, interpr);
17771841
if (pid >= 0) {
17781842
int status;
17791843
if (waitpid(pid, &status, 0) < 0)
@@ -1793,7 +1857,7 @@ int mingw_execv(const char *cmd, char *const *argv)
17931857
if (!try_shell_exec(cmd, argv)) {
17941858
int pid, status;
17951859

1796-
pid = mingw_spawnv(cmd, (const char **)argv, 0);
1860+
pid = mingw_spawnv(cmd, (const char **)argv, NULL);
17971861
if (pid < 0)
17981862
return -1;
17991863
if (waitpid(pid, &status, 0) < 0)

config.mak.uname

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,65 @@ else
707707
NO_CURL = YesPlease
708708
endif
709709
endif
710+
ifeq (i686,$(uname_M))
711+
MINGW_PREFIX := mingw32
712+
endif
713+
ifeq (x86_64,$(uname_M))
714+
MINGW_PREFIX := mingw64
715+
endif
716+
717+
DESTDIR_WINDOWS = $(shell cygpath -aw '$(DESTDIR_SQ)')
718+
DESTDIR_MIXED = $(shell cygpath -am '$(DESTDIR_SQ)')
719+
install-mingit-test-artifacts:
720+
install -m755 -d '$(DESTDIR_SQ)/usr/bin'
721+
printf '%s\n%s\n' >'$(DESTDIR_SQ)/usr/bin/perl' \
722+
"#!/mingw64/bin/busybox sh" \
723+
"exec \"$(shell cygpath -am /usr/bin/perl.exe)\" \"\$$@\""
724+
725+
install -m755 -d '$(DESTDIR_SQ)'
726+
printf '%s%s\n%s\n%s\n%s\n%s\n' >'$(DESTDIR_SQ)/init.bat' \
727+
"PATH=$(DESTDIR_WINDOWS)\\$(MINGW_PREFIX)\\bin;" \
728+
"C:\\WINDOWS;C:\\WINDOWS\\system32" \
729+
"@set GIT_TEST_INSTALLED=$(DESTDIR_MIXED)/$(MINGW_PREFIX)/bin" \
730+
"@`echo "$(DESTDIR_WINDOWS)" | sed 's/:.*/:/'`" \
731+
"@cd `echo "$(DESTDIR_WINDOWS)" | sed 's/^.://'`\\test-git\\t" \
732+
"@echo Now, run 'helper\\test-run-command testsuite'"
733+
734+
install -m755 -d '$(DESTDIR_SQ)/test-git'
735+
sed 's/^\(NO_PERL\|NO_PYTHON\)=.*/\1=YesPlease/' \
736+
<GIT-BUILD-OPTIONS >'$(DESTDIR_SQ)/test-git/GIT-BUILD-OPTIONS'
737+
738+
install -m755 -d '$(DESTDIR_SQ)/test-git/t/helper'
739+
install -m755 $(TEST_PROGRAMS) '$(DESTDIR_SQ)/test-git/t/helper'
740+
(cd t && $(TAR) cf - t[0-9][0-9][0-9][0-9] diff-lib) | \
741+
(cd '$(DESTDIR_SQ)/test-git/t' && $(TAR) xf -)
742+
install -m755 t/t556x_common t/*.sh '$(DESTDIR_SQ)/test-git/t'
743+
744+
install -m755 -d '$(DESTDIR_SQ)/test-git/templates'
745+
(cd templates && $(TAR) cf - blt) | \
746+
(cd '$(DESTDIR_SQ)/test-git/templates' && $(TAR) xf -)
747+
748+
# po/build/locale for t0200
749+
install -m755 -d '$(DESTDIR_SQ)/test-git/po/build/locale'
750+
(cd po/build/locale && $(TAR) cf - .) | \
751+
(cd '$(DESTDIR_SQ)/test-git/po/build/locale' && $(TAR) xf -)
752+
753+
# git-daemon.exe for t5802, git-http-backend.exe for t5560
754+
install -m755 -d '$(DESTDIR_SQ)/$(MINGW_PREFIX)/bin'
755+
install -m755 git-daemon.exe git-http-backend.exe \
756+
'$(DESTDIR_SQ)/$(MINGW_PREFIX)/bin'
757+
758+
# git-remote-testgit for t5801
759+
install -m755 -d '$(DESTDIR_SQ)/$(MINGW_PREFIX)/libexec/git-core'
760+
install -m755 git-remote-testgit \
761+
'$(DESTDIR_SQ)/$(MINGW_PREFIX)/libexec/git-core'
762+
763+
# git-upload-archive (dashed) for t5000
764+
install -m755 git-upload-archive.exe '$(DESTDIR_SQ)/$(MINGW_PREFIX)/bin'
765+
766+
# git-difftool--helper for t7800
767+
install -m755 git-difftool--helper \
768+
'$(DESTDIR_SQ)/$(MINGW_PREFIX)/libexec/git-core'
710769
endif
711770
ifeq ($(uname_S),QNX)
712771
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: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
static int run_diff(const char *path1, const char *path2)
9+
{
10+
const char *argv[] = {
11+
"diff", "--no-index", NULL, NULL, NULL
12+
};
13+
const char *env[] = {
14+
"GIT_PAGER=cat",
15+
"GIT_DIR=/dev/null",
16+
"HOME=/dev/null",
17+
NULL
18+
};
19+
20+
argv[2] = path1;
21+
argv[3] = path2;
22+
return run_command_v_opt_cd_env(argv,
23+
RUN_COMMAND_NO_STDIN | RUN_GIT_CMD,
24+
NULL, env);
25+
}
26+
27+
int cmd__cmp(int argc, const char **argv)
28+
{
29+
FILE *f0, *f1;
30+
struct strbuf b0 = STRBUF_INIT, b1 = STRBUF_INIT;
31+
32+
if (argc != 3)
33+
die("Require exactly 2 arguments, got %d", argc);
34+
35+
if (!(f0 = !strcmp(argv[1], "-") ? stdin : fopen(argv[1], "r")))
36+
return error_errno("could not open '%s'", argv[1]);
37+
if (!(f1 = !strcmp(argv[2], "-") ? stdin : fopen(argv[2], "r"))) {
38+
fclose(f0);
39+
return error_errno("could not open '%s'", argv[2]);
40+
}
41+
42+
for (;;) {
43+
int r0 = strbuf_getline(&b0, f0);
44+
int r1 = strbuf_getline(&b1, f1);
45+
46+
if (r0 == EOF) {
47+
fclose(f0);
48+
fclose(f1);
49+
strbuf_release(&b0);
50+
strbuf_release(&b1);
51+
if (r1 == EOF)
52+
return 0;
53+
cmp_failed:
54+
if (!run_diff(argv[1], argv[2]))
55+
die("Huh? 'diff --no-index %s %s' succeeded",
56+
argv[1], argv[2]);
57+
return 1;
58+
}
59+
if (r1 == EOF || strbuf_cmp(&b0, &b1)) {
60+
fclose(f0);
61+
fclose(f1);
62+
strbuf_release(&b0);
63+
strbuf_release(&b1);
64+
goto cmp_failed;
65+
}
66+
}
67+
}

t/helper/test-iconv.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 "utf8.h"
7+
8+
int cmd__iconv(int argc, const char **argv)
9+
{
10+
struct strbuf buf = STRBUF_INIT;
11+
char *from = NULL, *to = NULL, *p;
12+
size_t len;
13+
int ret = 0;
14+
const char * const iconv_usage[] = {
15+
N_("test-helper --iconv [<options>]"),
16+
NULL
17+
};
18+
struct option options[] = {
19+
OPT_STRING('f', "from-code", &from, "encoding", "from"),
20+
OPT_STRING('t', "to-code", &to, "encoding", "to"),
21+
OPT_END()
22+
};
23+
24+
argc = parse_options(argc, argv, NULL, options,
25+
iconv_usage, 0);
26+
27+
if (argc > 1 || !from || !to)
28+
usage_with_options(iconv_usage, options);
29+
30+
if (!argc) {
31+
if (strbuf_read(&buf, 0, 2048) < 0)
32+
die_errno("Could not read from stdin");
33+
} else if (strbuf_read_file(&buf, argv[0], 2048) < 0)
34+
die_errno("Could not read from '%s'", argv[0]);
35+
36+
p = reencode_string_len(buf.buf, buf.len, to, from, &len);
37+
if (!p)
38+
die_errno("Could not reencode");
39+
if (write(1, p, len) < 0)
40+
ret = !!error_errno("Could not write %"PRIuMAX" bytes",
41+
(uintmax_t)len);
42+
43+
strbuf_release(&buf);
44+
free(p);
45+
46+
return ret;
47+
}

0 commit comments

Comments
 (0)