Skip to content

Commit 1ee0267

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 d43008d + d9aae6e commit 1ee0267

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

+634
-216
lines changed

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/exit-process.h"
1010
#include "win32/lazyload.h"
1111
#include "../config.h"
12+
#include "../string-list.h"
1213

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

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

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

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

@@ -1574,8 +1637,8 @@ static int is_msys2_sh(const char *cmd)
15741637
}
15751638

15761639
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
1577-
const char *dir,
1578-
int prepend_cmd, int fhin, int fhout, int fherr)
1640+
const char *dir, const char *prepend_cmd,
1641+
int fhin, int fhout, int fherr)
15791642
{
15801643
static int restrict_handle_inheritance = 1;
15811644
STARTUPINFOEXW si;
@@ -1649,9 +1712,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16491712
/* concatenate argv, quoting args as we go */
16501713
strbuf_init(&args, 0);
16511714
if (prepend_cmd) {
1652-
char *quoted = (char *)quote_arg(cmd);
1715+
char *quoted = (char *)quote_arg(prepend_cmd);
16531716
strbuf_addstr(&args, quoted);
1654-
if (quoted != cmd)
1717+
if (quoted != prepend_cmd)
16551718
free(quoted);
16561719
}
16571720
for (; *argv; argv++) {
@@ -1809,7 +1872,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
18091872
return (pid_t)pi.dwProcessId;
18101873
}
18111874

1812-
static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
1875+
static pid_t mingw_spawnv(const char *cmd, const char **argv,
1876+
const char *prepend_cmd)
18131877
{
18141878
return mingw_spawnve_fd(cmd, argv, NULL, NULL, prepend_cmd, 0, 1, 2);
18151879
}
@@ -1837,14 +1901,14 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
18371901
pid = -1;
18381902
}
18391903
else {
1840-
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, 1,
1904+
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, interpr,
18411905
fhin, fhout, fherr);
18421906
free(iprog);
18431907
}
18441908
argv[0] = argv0;
18451909
}
18461910
else
1847-
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, 0,
1911+
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, NULL,
18481912
fhin, fhout, fherr);
18491913
free(prog);
18501914
}
@@ -1870,7 +1934,7 @@ static int try_shell_exec(const char *cmd, char *const *argv)
18701934
ALLOC_ARRAY(argv2, argc + 1);
18711935
argv2[0] = (char *)cmd; /* full path to the script file */
18721936
memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
1873-
pid = mingw_spawnv(prog, argv2, 1);
1937+
pid = mingw_spawnv(prog, argv2, interpr);
18741938
if (pid >= 0) {
18751939
int status;
18761940
if (waitpid(pid, &status, 0) < 0)
@@ -1890,7 +1954,7 @@ int mingw_execv(const char *cmd, char *const *argv)
18901954
if (!try_shell_exec(cmd, argv)) {
18911955
int pid, status;
18921956

1893-
pid = mingw_spawnv(cmd, (const char **)argv, 0);
1957+
pid = mingw_spawnv(cmd, (const char **)argv, NULL);
18941958
if (pid < 0)
18951959
return -1;
18961960
if (waitpid(pid, &status, 0) < 0)

config.mak.uname

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