Skip to content

Commit 5992e66

Browse files
author
Git for Windows Build Agent
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 df4e943 + 42af5e8 commit 5992e66

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

+638
-220
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@ TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
655655
TEST_PROGRAMS_NEED_X += test-fake-ssh
656656
TEST_PROGRAMS_NEED_X += test-genrandom
657657
TEST_PROGRAMS_NEED_X += test-hashmap
658+
TEST_PROGRAMS_NEED_X += test-helper
658659
TEST_PROGRAMS_NEED_X += test-index-version
659660
TEST_PROGRAMS_NEED_X += test-lazy-init-name-hash
660661
TEST_PROGRAMS_NEED_X += test-line-buffer

compat/mingw.c

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "../cache.h"
99
#include "win32/exit-process.h"
1010
#include "../config.h"
11+
#include "../string-list.h"
1112

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

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

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

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

@@ -1557,8 +1620,8 @@ static int is_msys2_sh(const char *cmd)
15571620
}
15581621

15591622
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
1560-
const char *dir,
1561-
int prepend_cmd, int fhin, int fhout, int fherr)
1623+
const char *dir, const char *prepend_cmd,
1624+
int fhin, int fhout, int fherr)
15621625
{
15631626
static int atexit_handler_initialized;
15641627
STARTUPINFOW si;
@@ -1627,9 +1690,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16271690
/* concatenate argv, quoting args as we go */
16281691
strbuf_init(&args, 0);
16291692
if (prepend_cmd) {
1630-
char *quoted = (char *)quote_arg(cmd);
1693+
char *quoted = (char *)quote_arg(prepend_cmd);
16311694
strbuf_addstr(&args, quoted);
1632-
if (quoted != cmd)
1695+
if (quoted != prepend_cmd)
16331696
free(quoted);
16341697
}
16351698
for (; *argv; argv++) {
@@ -1706,7 +1769,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17061769
return (pid_t)pi.dwProcessId;
17071770
}
17081771

1709-
static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
1772+
static pid_t mingw_spawnv(const char *cmd, const char **argv,
1773+
const char *prepend_cmd)
17101774
{
17111775
return mingw_spawnve_fd(cmd, argv, NULL, NULL, prepend_cmd, 0, 1, 2);
17121776
}
@@ -1734,14 +1798,14 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
17341798
pid = -1;
17351799
}
17361800
else {
1737-
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, 1,
1801+
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, interpr,
17381802
fhin, fhout, fherr);
17391803
free(iprog);
17401804
}
17411805
argv[0] = argv0;
17421806
}
17431807
else
1744-
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, 0,
1808+
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, NULL,
17451809
fhin, fhout, fherr);
17461810
free(prog);
17471811
}
@@ -1767,7 +1831,7 @@ static int try_shell_exec(const char *cmd, char *const *argv)
17671831
ALLOC_ARRAY(argv2, argc + 1);
17681832
argv2[0] = (char *)cmd; /* full path to the script file */
17691833
memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
1770-
pid = mingw_spawnv(prog, argv2, 1);
1834+
pid = mingw_spawnv(prog, argv2, interpr);
17711835
if (pid >= 0) {
17721836
int status;
17731837
if (waitpid(pid, &status, 0) < 0)
@@ -1787,7 +1851,7 @@ int mingw_execv(const char *cmd, char *const *argv)
17871851
if (!try_shell_exec(cmd, argv)) {
17881852
int pid, status;
17891853

1790-
pid = mingw_spawnv(cmd, (const char **)argv, 0);
1854+
pid = mingw_spawnv(cmd, (const char **)argv, NULL);
17911855
if (pid < 0)
17921856
return -1;
17931857
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/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
/test-scrap-cache-tree
1313
/test-genrandom
1414
/test-hashmap
15+
/test-helper
1516
/test-index-version
1617
/test-lazy-init-name-hash
1718
/test-line-buffer

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)