Skip to content

Commit c4e5f96

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 9d37622 + fe3b28a commit c4e5f96

Some content is hidden

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

53 files changed

+637
-220
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ TEST_PROGRAMS_NEED_X += test-example-decorate
662662
TEST_PROGRAMS_NEED_X += test-fake-ssh
663663
TEST_PROGRAMS_NEED_X += test-genrandom
664664
TEST_PROGRAMS_NEED_X += test-hashmap
665+
TEST_PROGRAMS_NEED_X += test-helper
665666
TEST_PROGRAMS_NEED_X += test-index-version
666667
TEST_PROGRAMS_NEED_X += test-lazy-init-name-hash
667668
TEST_PROGRAMS_NEED_X += test-line-buffer

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

@@ -1217,6 +1218,65 @@ static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
12171218
return NULL;
12181219
}
12191220

1221+
static char *path_lookup(const char *cmd, int exe_only);
1222+
1223+
static char *is_busybox_applet(const char *cmd)
1224+
{
1225+
static struct string_list applets = STRING_LIST_INIT_DUP;
1226+
static char *busybox_path;
1227+
static int busybox_path_initialized;
1228+
1229+
/* Avoid infinite loop */
1230+
if (!strncasecmp(cmd, "busybox", 7) &&
1231+
(!cmd[7] || !strcasecmp(cmd + 7, ".exe")))
1232+
return NULL;
1233+
1234+
if (!busybox_path_initialized) {
1235+
busybox_path = path_lookup("busybox.exe", 1);
1236+
busybox_path_initialized = 1;
1237+
}
1238+
1239+
/* Assume that sh is compiled in... */
1240+
if (!busybox_path || !strcasecmp(cmd, "sh"))
1241+
return xstrdup_or_null(busybox_path);
1242+
1243+
if (!applets.nr) {
1244+
struct child_process cp = CHILD_PROCESS_INIT;
1245+
struct strbuf buf = STRBUF_INIT;
1246+
char *p;
1247+
1248+
argv_array_pushl(&cp.args, busybox_path, "--help", NULL);
1249+
1250+
if (capture_command(&cp, &buf, 2048)) {
1251+
string_list_append(&applets, "");
1252+
return NULL;
1253+
}
1254+
1255+
/* parse output */
1256+
p = strstr(buf.buf, "Currently defined functions:\n");
1257+
if (!p) {
1258+
warning("Could not parse output of busybox --help");
1259+
string_list_append(&applets, "");
1260+
return NULL;
1261+
}
1262+
p = strchrnul(p, '\n');
1263+
for (;;) {
1264+
size_t len;
1265+
1266+
p += strspn(p, "\n\t ,");
1267+
len = strcspn(p, "\n\t ,");
1268+
if (!len)
1269+
break;
1270+
p[len] = '\0';
1271+
string_list_insert(&applets, p);
1272+
p = p + len + 1;
1273+
}
1274+
}
1275+
1276+
return string_list_has_string(&applets, cmd) ?
1277+
xstrdup(busybox_path) : NULL;
1278+
}
1279+
12201280
/*
12211281
* Determines the absolute path of cmd using the split path in path.
12221282
* If cmd contains a slash or backslash, no lookup is performed.
@@ -1245,6 +1305,9 @@ static char *path_lookup(const char *cmd, int exe_only)
12451305
path = sep + 1;
12461306
}
12471307

1308+
if (!prog && !isexe)
1309+
prog = is_busybox_applet(cmd);
1310+
12481311
return prog;
12491312
}
12501313

@@ -1528,8 +1591,8 @@ static int is_msys2_sh(const char *cmd)
15281591
}
15291592

15301593
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
1531-
const char *dir,
1532-
int prepend_cmd, int fhin, int fhout, int fherr)
1594+
const char *dir, const char *prepend_cmd,
1595+
int fhin, int fhout, int fherr)
15331596
{
15341597
static int atexit_handler_initialized;
15351598
STARTUPINFOW si;
@@ -1598,9 +1661,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
15981661
/* concatenate argv, quoting args as we go */
15991662
strbuf_init(&args, 0);
16001663
if (prepend_cmd) {
1601-
char *quoted = (char *)quote_arg(cmd);
1664+
char *quoted = (char *)quote_arg(prepend_cmd);
16021665
strbuf_addstr(&args, quoted);
1603-
if (quoted != cmd)
1666+
if (quoted != prepend_cmd)
16041667
free(quoted);
16051668
}
16061669
for (; *argv; argv++) {
@@ -1677,7 +1740,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16771740
return (pid_t)pi.dwProcessId;
16781741
}
16791742

1680-
static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
1743+
static pid_t mingw_spawnv(const char *cmd, const char **argv,
1744+
const char *prepend_cmd)
16811745
{
16821746
return mingw_spawnve_fd(cmd, argv, NULL, NULL, prepend_cmd, 0, 1, 2);
16831747
}
@@ -1705,14 +1769,14 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
17051769
pid = -1;
17061770
}
17071771
else {
1708-
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, 1,
1772+
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, interpr,
17091773
fhin, fhout, fherr);
17101774
free(iprog);
17111775
}
17121776
argv[0] = argv0;
17131777
}
17141778
else
1715-
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, 0,
1779+
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, NULL,
17161780
fhin, fhout, fherr);
17171781
free(prog);
17181782
}
@@ -1738,7 +1802,7 @@ static int try_shell_exec(const char *cmd, char *const *argv)
17381802
ALLOC_ARRAY(argv2, argc + 1);
17391803
argv2[0] = (char *)cmd; /* full path to the script file */
17401804
memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
1741-
pid = mingw_spawnv(prog, argv2, 1);
1805+
pid = mingw_spawnv(prog, argv2, interpr);
17421806
if (pid >= 0) {
17431807
int status;
17441808
if (waitpid(pid, &status, 0) < 0)
@@ -1758,7 +1822,7 @@ int mingw_execv(const char *cmd, char *const *argv)
17581822
if (!try_shell_exec(cmd, argv)) {
17591823
int pid, status;
17601824

1761-
pid = mingw_spawnv(cmd, (const char **)argv, 0);
1825+
pid = mingw_spawnv(cmd, (const char **)argv, NULL);
17621826
if (pid < 0)
17631827
return -1;
17641828
if (waitpid(pid, &status, 0) < 0)

config.mak.uname

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,65 @@ else
688688
NO_CURL = YesPlease
689689
endif
690690
endif
691+
ifeq (i686,$(uname_M))
692+
MINGW_PREFIX := mingw32
693+
endif
694+
ifeq (x86_64,$(uname_M))
695+
MINGW_PREFIX := mingw64
696+
endif
697+
698+
DESTDIR_WINDOWS = $(shell cygpath -aw '$(DESTDIR_SQ)')
699+
DESTDIR_MIXED = $(shell cygpath -am '$(DESTDIR_SQ)')
700+
install-mingit-test-artifacts:
701+
install -m755 -d '$(DESTDIR_SQ)/usr/bin'
702+
printf '%s\n%s\n' >'$(DESTDIR_SQ)/usr/bin/perl' \
703+
"#!/mingw64/bin/busybox sh" \
704+
"exec \"$(shell cygpath -am /usr/bin/perl.exe)\" \"\$$@\""
705+
706+
install -m755 -d '$(DESTDIR_SQ)'
707+
printf '%s%s\n%s\n%s\n%s\n%s\n' >'$(DESTDIR_SQ)/init.bat' \
708+
"PATH=$(DESTDIR_WINDOWS)\\$(MINGW_PREFIX)\\bin;" \
709+
"C:\\WINDOWS;C:\\WINDOWS\\system32" \
710+
"@set GIT_TEST_INSTALLED=$(DESTDIR_MIXED)/$(MINGW_PREFIX)/bin" \
711+
"@`echo "$(DESTDIR_WINDOWS)" | sed 's/:.*/:/'`" \
712+
"@cd `echo "$(DESTDIR_WINDOWS)" | sed 's/^.://'`\\test-git\\t" \
713+
"@echo Now, run 'helper\\test-run-command testsuite'"
714+
715+
install -m755 -d '$(DESTDIR_SQ)/test-git'
716+
sed 's/^\(NO_PERL\|NO_PYTHON\)=.*/\1=YesPlease/' \
717+
<GIT-BUILD-OPTIONS >'$(DESTDIR_SQ)/test-git/GIT-BUILD-OPTIONS'
718+
719+
install -m755 -d '$(DESTDIR_SQ)/test-git/t/helper'
720+
install -m755 $(TEST_PROGRAMS) '$(DESTDIR_SQ)/test-git/t/helper'
721+
(cd t && $(TAR) cf - t[0-9][0-9][0-9][0-9] diff-lib) | \
722+
(cd '$(DESTDIR_SQ)/test-git/t' && $(TAR) xf -)
723+
install -m755 t/t556x_common t/*.sh '$(DESTDIR_SQ)/test-git/t'
724+
725+
install -m755 -d '$(DESTDIR_SQ)/test-git/templates'
726+
(cd templates && $(TAR) cf - blt) | \
727+
(cd '$(DESTDIR_SQ)/test-git/templates' && $(TAR) xf -)
728+
729+
# po/build/locale for t0200
730+
install -m755 -d '$(DESTDIR_SQ)/test-git/po/build/locale'
731+
(cd po/build/locale && $(TAR) cf - .) | \
732+
(cd '$(DESTDIR_SQ)/test-git/po/build/locale' && $(TAR) xf -)
733+
734+
# git-daemon.exe for t5802, git-http-backend.exe for t5560
735+
install -m755 -d '$(DESTDIR_SQ)/$(MINGW_PREFIX)/bin'
736+
install -m755 git-daemon.exe git-http-backend.exe \
737+
'$(DESTDIR_SQ)/$(MINGW_PREFIX)/bin'
738+
739+
# git-remote-testgit for t5801
740+
install -m755 -d '$(DESTDIR_SQ)/$(MINGW_PREFIX)/libexec/git-core'
741+
install -m755 git-remote-testgit \
742+
'$(DESTDIR_SQ)/$(MINGW_PREFIX)/libexec/git-core'
743+
744+
# git-upload-archive (dashed) for t5000
745+
install -m755 git-upload-archive.exe '$(DESTDIR_SQ)/$(MINGW_PREFIX)/bin'
746+
747+
# git-difftool--helper for t7800
748+
install -m755 git-difftool--helper \
749+
'$(DESTDIR_SQ)/$(MINGW_PREFIX)/libexec/git-core'
691750
endif
692751
ifeq ($(uname_S),QNX)
693752
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]:*)

t/.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
t[0-9][0-9][0-9][0-9]/* -whitespace
22
/diff-lib/* eol=lf
3+
/diff-lib/*.png binary
34
/t0110/url-* binary
45
/t3900/*.txt eol=lf
56
/t3901/*.txt eol=lf
File renamed without changes.
File renamed without changes.

t/helper/test-helper.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#include "git-compat-util.h"
2+
#include "strbuf.h"
3+
#include "gettext.h"
4+
#include "parse-options.h"
5+
#include "utf8.h"
6+
7+
static const char * const test_helper_usage[] = {
8+
N_("test-helper [<options>]"),
9+
NULL
10+
};
11+
12+
static int cmp(int argc, const char **argv)
13+
{
14+
FILE *f0, *f1;
15+
struct strbuf b0 = STRBUF_INIT, b1 = STRBUF_INIT;
16+
17+
if (argc != 3)
18+
die("Require exactly 2 arguments, got %d", argc);
19+
20+
if (!(f0 = !strcmp(argv[1], "-") ? stdin : fopen(argv[1], "r")))
21+
return error_errno("could not open '%s'", argv[1]);
22+
if (!(f1 = !strcmp(argv[2], "-") ? stdin : fopen(argv[2], "r"))) {
23+
fclose(f0);
24+
return error_errno("could not open '%s'", argv[2]);
25+
}
26+
27+
for (;;) {
28+
int r0 = strbuf_getline(&b0, f0);
29+
int r1 = strbuf_getline(&b1, f1);
30+
31+
if (r0 == EOF) {
32+
fclose(f0);
33+
fclose(f1);
34+
strbuf_release(&b0);
35+
strbuf_release(&b1);
36+
if (r1 == EOF)
37+
return 0;
38+
return 1;
39+
}
40+
if (r1 == EOF || strbuf_cmp(&b0, &b1)) {
41+
fclose(f0);
42+
fclose(f1);
43+
strbuf_release(&b0);
44+
strbuf_release(&b1);
45+
return 1;
46+
}
47+
}
48+
}
49+
50+
static int iconv_(int argc, const char **argv)
51+
{
52+
struct strbuf buf = STRBUF_INIT;
53+
char *from = NULL, *to = NULL, *p;
54+
int len, ret = 0;
55+
const char * const iconv_usage[] = {
56+
N_("test-helper --iconv [<options>]"),
57+
NULL
58+
};
59+
struct option options[] = {
60+
OPT_STRING('f', "from-code", &from, "encoding", "from"),
61+
OPT_STRING('t', "to-code", &to, "encoding", "to"),
62+
OPT_END()
63+
};
64+
65+
argc = parse_options(argc, argv, NULL, options,
66+
iconv_usage, 0);
67+
68+
if (argc > 1 || !from || !to)
69+
usage_with_options(iconv_usage, options);
70+
71+
if (!argc) {
72+
if (strbuf_read(&buf, 0, 2048) < 0)
73+
die_errno("Could not read from stdin");
74+
} else if (strbuf_read_file(&buf, argv[0], 2048) < 0)
75+
die_errno("Could not read from '%s'", argv[0]);
76+
77+
p = reencode_string_len(buf.buf, buf.len, to, from, &len);
78+
if (!p)
79+
die_errno("Could not reencode");
80+
if (write(1, p, len) < 0)
81+
ret = !!error_errno("Could not write %d bytes", len);
82+
83+
strbuf_release(&buf);
84+
free(p);
85+
86+
return ret;
87+
}
88+
89+
int cmd_main(int argc, const char **argv)
90+
{
91+
enum mode {
92+
CMP = 1, ICONV
93+
} command = 0;
94+
struct option options[] = {
95+
OPT_CMDMODE(0, "cmp", &command,
96+
N_("compare files (ignoring LF vs CR/LF)"), CMP),
97+
OPT_CMDMODE(0, "iconv", &command,
98+
N_("act as drop-in replacement for `iconv`"), ICONV),
99+
OPT_END()
100+
};
101+
102+
argc = parse_options(argc, argv, NULL, options,
103+
test_helper_usage,
104+
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
105+
106+
if (command == CMP)
107+
return !!cmp(argc, argv);
108+
if (command == ICONV)
109+
return !!iconv_(argc, argv);
110+
111+
die("unhandled mode");
112+
}
113+

0 commit comments

Comments
 (0)