Skip to content

Commit feaa4d9

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 12f7a44 + 357d423 commit feaa4d9

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

+636
-220
lines changed

Makefile

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

700700
TEST_BUILTINS_OBJS += test-chmtime.o
701+
TEST_BUILTINS_OBJS += test-cmp.o
701702
TEST_BUILTINS_OBJS += test-config.o
702703
TEST_BUILTINS_OBJS += test-ctype.o
703704
TEST_BUILTINS_OBJS += test-date.o
@@ -708,6 +709,7 @@ TEST_BUILTINS_OBJS += test-dump-split-index.o
708709
TEST_BUILTINS_OBJS += test-example-decorate.o
709710
TEST_BUILTINS_OBJS += test-genrandom.o
710711
TEST_BUILTINS_OBJS += test-hashmap.o
712+
TEST_BUILTINS_OBJS += test-iconv.o
711713
TEST_BUILTINS_OBJS += test-index-version.o
712714
TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
713715
TEST_BUILTINS_OBJS += test-match-trees.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

@@ -1260,6 +1261,65 @@ static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
12601261
return NULL;
12611262
}
12621263

1264+
static char *path_lookup(const char *cmd, int exe_only);
1265+
1266+
static char *is_busybox_applet(const char *cmd)
1267+
{
1268+
static struct string_list applets = STRING_LIST_INIT_DUP;
1269+
static char *busybox_path;
1270+
static int busybox_path_initialized;
1271+
1272+
/* Avoid infinite loop */
1273+
if (!strncasecmp(cmd, "busybox", 7) &&
1274+
(!cmd[7] || !strcasecmp(cmd + 7, ".exe")))
1275+
return NULL;
1276+
1277+
if (!busybox_path_initialized) {
1278+
busybox_path = path_lookup("busybox.exe", 1);
1279+
busybox_path_initialized = 1;
1280+
}
1281+
1282+
/* Assume that sh is compiled in... */
1283+
if (!busybox_path || !strcasecmp(cmd, "sh"))
1284+
return xstrdup_or_null(busybox_path);
1285+
1286+
if (!applets.nr) {
1287+
struct child_process cp = CHILD_PROCESS_INIT;
1288+
struct strbuf buf = STRBUF_INIT;
1289+
char *p;
1290+
1291+
argv_array_pushl(&cp.args, busybox_path, "--help", NULL);
1292+
1293+
if (capture_command(&cp, &buf, 2048)) {
1294+
string_list_append(&applets, "");
1295+
return NULL;
1296+
}
1297+
1298+
/* parse output */
1299+
p = strstr(buf.buf, "Currently defined functions:\n");
1300+
if (!p) {
1301+
warning("Could not parse output of busybox --help");
1302+
string_list_append(&applets, "");
1303+
return NULL;
1304+
}
1305+
p = strchrnul(p, '\n');
1306+
for (;;) {
1307+
size_t len;
1308+
1309+
p += strspn(p, "\n\t ,");
1310+
len = strcspn(p, "\n\t ,");
1311+
if (!len)
1312+
break;
1313+
p[len] = '\0';
1314+
string_list_insert(&applets, p);
1315+
p = p + len + 1;
1316+
}
1317+
}
1318+
1319+
return string_list_has_string(&applets, cmd) ?
1320+
xstrdup(busybox_path) : NULL;
1321+
}
1322+
12631323
/*
12641324
* Determines the absolute path of cmd using the split path in path.
12651325
* If cmd contains a slash or backslash, no lookup is performed.
@@ -1288,6 +1348,9 @@ static char *path_lookup(const char *cmd, int exe_only)
12881348
path = sep + 1;
12891349
}
12901350

1351+
if (!prog && !isexe)
1352+
prog = is_busybox_applet(cmd);
1353+
12911354
return prog;
12921355
}
12931356

@@ -1538,8 +1601,8 @@ static int is_msys2_sh(const char *cmd)
15381601
}
15391602

15401603
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
1541-
const char *dir,
1542-
int prepend_cmd, int fhin, int fhout, int fherr)
1604+
const char *dir, const char *prepend_cmd,
1605+
int fhin, int fhout, int fherr)
15431606
{
15441607
STARTUPINFOW si;
15451608
PROCESS_INFORMATION pi;
@@ -1596,9 +1659,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
15961659
/* concatenate argv, quoting args as we go */
15971660
strbuf_init(&args, 0);
15981661
if (prepend_cmd) {
1599-
char *quoted = (char *)quote_arg(cmd);
1662+
char *quoted = (char *)quote_arg(prepend_cmd);
16001663
strbuf_addstr(&args, quoted);
1601-
if (quoted != cmd)
1664+
if (quoted != prepend_cmd)
16021665
free(quoted);
16031666
}
16041667
for (; *argv; argv++) {
@@ -1675,7 +1738,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16751738
return (pid_t)pi.dwProcessId;
16761739
}
16771740

1678-
static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
1741+
static pid_t mingw_spawnv(const char *cmd, const char **argv,
1742+
const char *prepend_cmd)
16791743
{
16801744
return mingw_spawnve_fd(cmd, argv, NULL, NULL, prepend_cmd, 0, 1, 2);
16811745
}
@@ -1703,14 +1767,14 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
17031767
pid = -1;
17041768
}
17051769
else {
1706-
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, 1,
1770+
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, interpr,
17071771
fhin, fhout, fherr);
17081772
free(iprog);
17091773
}
17101774
argv[0] = argv0;
17111775
}
17121776
else
1713-
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, 0,
1777+
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, NULL,
17141778
fhin, fhout, fherr);
17151779
free(prog);
17161780
}
@@ -1736,7 +1800,7 @@ static int try_shell_exec(const char *cmd, char *const *argv)
17361800
ALLOC_ARRAY(argv2, argc + 1);
17371801
argv2[0] = (char *)cmd; /* full path to the script file */
17381802
memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
1739-
pid = mingw_spawnv(prog, argv2, 1);
1803+
pid = mingw_spawnv(prog, argv2, interpr);
17401804
if (pid >= 0) {
17411805
int status;
17421806
if (waitpid(pid, &status, 0) < 0)
@@ -1756,7 +1820,7 @@ int mingw_execv(const char *cmd, char *const *argv)
17561820
if (!try_shell_exec(cmd, argv)) {
17571821
int pid, status;
17581822

1759-
pid = mingw_spawnv(cmd, (const char **)argv, 0);
1823+
pid = mingw_spawnv(cmd, (const char **)argv, NULL);
17601824
if (pid < 0)
17611825
return -1;
17621826
if (waitpid(pid, &status, 0) < 0)

config.mak.uname

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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", NULL
15+
};
16+
17+
argv[2] = path1;
18+
argv[3] = path2;
19+
return run_command_v_opt_cd_env(argv,
20+
RUN_COMMAND_NO_STDIN | RUN_GIT_CMD,
21+
NULL, env);
22+
}
23+
24+
int cmd__cmp(int argc, const char **argv)
25+
{
26+
FILE *f0, *f1;
27+
struct strbuf b0 = STRBUF_INIT, b1 = STRBUF_INIT;
28+
29+
if (argc != 3)
30+
die("Require exactly 2 arguments, got %d", argc);
31+
32+
if (!(f0 = !strcmp(argv[1], "-") ? stdin : fopen(argv[1], "r")))
33+
return error_errno("could not open '%s'", argv[1]);
34+
if (!(f1 = !strcmp(argv[2], "-") ? stdin : fopen(argv[2], "r"))) {
35+
fclose(f0);
36+
return error_errno("could not open '%s'", argv[2]);
37+
}
38+
39+
for (;;) {
40+
int r0 = strbuf_getline(&b0, f0);
41+
int r1 = strbuf_getline(&b1, f1);
42+
43+
if (r0 == EOF) {
44+
fclose(f0);
45+
fclose(f1);
46+
strbuf_release(&b0);
47+
strbuf_release(&b1);
48+
if (r1 == EOF)
49+
return 0;
50+
cmp_failed:
51+
return !!run_diff(argv[1], argv[2]);
52+
}
53+
if (r1 == EOF || strbuf_cmp(&b0, &b1)) {
54+
fclose(f0);
55+
fclose(f1);
56+
strbuf_release(&b0);
57+
strbuf_release(&b1);
58+
goto cmp_failed;
59+
}
60+
}
61+
}

t/helper/test-iconv.c

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

0 commit comments

Comments
 (0)