From 477cafffe8884cd0d33cb9170071e937456042a9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Anders=20Bj=C3=B6rklund?= Date: Thu, 18 Feb 2016 22:00:59 +0100 Subject: [PATCH] Fix Windows quicktest, paths and bats Mostly about path shenaningans, with the drive letter and backslashes Also hack in some support for running echo command, and batch files. All tests now pass, at least on Wine (but with some "fixme" warnings): PASSED: 458 assertions, 89 tests, 10 suites --- ccache.c | 5 ++++ hashutil.c | 23 +++++++++++++++- test/test_args.c | 4 +++ test/test_argument_processing.c | 47 ++++++++++++++++++++++++++++++--- test/test_conf.c | 8 ++++++ test/test_hashutil.c | 12 +++++++++ util.c | 7 +++++ 7 files changed, 101 insertions(+), 5 deletions(-) diff --git a/ccache.c b/ccache.c index f1f1de786..2d4209a70 100644 --- a/ccache.c +++ b/ccache.c @@ -697,6 +697,11 @@ make_relative_path(char *path) return path; } +#ifdef _WIN32 + if (path[0] == '/') + path++; /* skip leading slash */ +#endif + /* x_realpath only works for existing paths, so if path doesn't exist, try * dirname(path) and assemble the path afterwards. We only bother to try * canonicalizing one of these two paths since a compiler path argument diff --git a/hashutil.c b/hashutil.c index 6c4b2799f..ad7a56f31 100644 --- a/hashutil.c +++ b/hashutil.c @@ -187,6 +187,7 @@ hash_command_output(struct mdfour *hash, const char *command, PROCESS_INFORMATION pi; STARTUPINFO si; DWORD exitcode; + bool cmd = false; char *sh = NULL; char *win32args; char *path; @@ -198,6 +199,21 @@ hash_command_output(struct mdfour *hash, const char *command, int pipefd[2]; #endif +#ifdef _WIN32 + /* trim leading space */ + while (isspace(*command)) + command++; + /* add "echo" command */ + if (str_startswith(command, "echo")) { + command = format("cmd.exe /c \"%s\"", command); + cmd = true; + } else if (str_startswith(command, "%compiler%") && str_eq(compiler, "echo")) { + command = format("cmd.exe /c \"%s%s\"", compiler, command + 10); + cmd = true; + } else { + command = x_strdup(command); + } +#endif struct args *args = args_init_from_string(command); int i; for (i = 0; i < args->argc; i++) { @@ -225,11 +241,16 @@ hash_command_output(struct mdfour *hash, const char *command, si.hStdError = pipe_out[1]; si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); si.dwFlags = STARTF_USESTDHANDLES; - win32args = win32argvtos(sh, args->argv); + if (!cmd) + win32args = win32argvtos(sh, args->argv); + else + win32args = (char *) command; /* quoted */ ret = CreateProcess(path, win32args, NULL, NULL, 1, 0, NULL, NULL, &si, &pi); CloseHandle(pipe_out[1]); args_free(args); free(win32args); + if (cmd) + free((char *) command); /* original argument was replaced above */ if (ret == 0) { stats_update(STATS_COMPCHECK); return false; diff --git a/test/test_args.c b/test/test_args.c index d0a3bac55..5a8a4707c 100644 --- a/test/test_args.c +++ b/test/test_args.c @@ -78,7 +78,11 @@ TEST(args_init_from_gcc_atfile) CHECK_STR_EQ("fourth", args->argv[3]); CHECK_STR_EQ("fif th", args->argv[4]); CHECK_STR_EQ("si'x\" th", args->argv[5]); +#ifndef _WIN32 CHECK_STR_EQ("seve\nth", args->argv[6]); +#else + CHECK_STR_EQ("seve\r\nth", args->argv[6]); +#endif CHECK(!args->argv[7]); args_free(args); } diff --git a/test/test_argument_processing.c b/test/test_argument_processing.c index 67f73718e..6fc67ac5e 100644 --- a/test/test_argument_processing.c +++ b/test/test_argument_processing.c @@ -27,6 +27,41 @@ extern struct conf *conf; +static char * +get_root(void) +{ +#ifndef _WIN32 + return x_strdup("/"); +#else + char volume[4]; /* "C:\" */ + GetVolumePathName(get_cwd(), volume, sizeof(volume)); + return x_strdup(volume); +#endif +} + +static char * +get_posix_path(char *path) +{ +#ifndef _WIN32 + return x_strdup(path); +#else + char *posix; + char *p; + + /* / escape volume */ + if (path[0] >= 'A' && path[0] <= 'Z' && path[1] == ':') + posix = format("/%s", path); + else + posix = x_strdup(path); + /* convert slashes */ + for (p = posix; *p; p++) { + if (*p == '\\') + *p = '/'; + } + return posix; +#endif +} + TEST_SUITE(argument_processing) TEST(dash_E_should_result_in_called_for_preprocessing) @@ -121,7 +156,7 @@ TEST(sysroot_should_be_rewritten_if_basedir_is_used) create_file("foo.c", ""); free(conf->base_dir); - conf->base_dir = x_strdup("/"); + conf->base_dir = get_root(); current_working_dir = get_cwd(); arg_string = format("cc --sysroot=%s/foo -c foo.c", current_working_dir); orig = args_init_from_string(arg_string); @@ -309,7 +344,7 @@ TEST(isystem_flag_with_separate_arg_should_be_rewritten_if_basedir_is_used) create_file("foo.c", ""); free(conf->base_dir); - conf->base_dir = x_strdup("/"); + conf->base_dir = get_root(); current_working_dir = get_cwd(); arg_string = format("cc -isystem %s/foo -c foo.c", current_working_dir); orig = args_init_from_string(arg_string); @@ -326,15 +361,18 @@ TEST(isystem_flag_with_separate_arg_should_be_rewritten_if_basedir_is_used) TEST(isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used) { extern char *current_working_dir; + char *cwd; char *arg_string; struct args *orig; struct args *act_cpp = NULL, *act_cc = NULL; create_file("foo.c", ""); free(conf->base_dir); - conf->base_dir = x_strdup("/"); + conf->base_dir = x_strdup("/"); /* posix */ current_working_dir = get_cwd(); - arg_string = format("cc -isystem%s/foo -c foo.c", current_working_dir); + /* windows path don't work concatenated */ + cwd = get_posix_path(current_working_dir); + arg_string = format("cc -isystem%s/foo -c foo.c", cwd); orig = args_init_from_string(arg_string); free(arg_string); @@ -342,6 +380,7 @@ TEST(isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used) CHECK_STR_EQ("-isystem", act_cpp->argv[1]); CHECK_STR_EQ("./foo", act_cpp->argv[2]); + free(cwd); args_free(orig); args_free(act_cpp); args_free(act_cc); diff --git a/test/test_conf.c b/test/test_conf.c index 749859ab3..6a1524185 100644 --- a/test/test_conf.c +++ b/test/test_conf.c @@ -92,7 +92,11 @@ TEST(conf_read_valid_config) CHECK_STR_EQ("rabbit", user); create_file( "ccache.conf", +#ifndef _WIN32 "base_dir = /$USER/foo/${USER} \n" +#else + "base_dir = C:/$USER/foo/${USER}\n" +#endif "cache_dir=\n" "cache_dir = $USER$/${USER}/.ccache\n" "\n" @@ -128,7 +132,11 @@ TEST(conf_read_valid_config) CHECK(conf_read(conf, "ccache.conf", &errmsg)); CHECK(!errmsg); +#ifndef _WIN32 CHECK_STR_EQ_FREE1(format("/%s/foo/%s", user, user), conf->base_dir); +#else + CHECK_STR_EQ_FREE1(format("C:/%s/foo/%s", user, user), conf->base_dir); +#endif CHECK_STR_EQ_FREE1(format("%s$/%s/.ccache", user, user), conf->cache_dir); CHECK_INT_EQ(4, conf->cache_dir_levels); CHECK_STR_EQ("foo", conf->compiler); diff --git a/test/test_hashutil.c b/test/test_hashutil.c index 9b6fd3495..aa77066f4 100644 --- a/test/test_hashutil.c +++ b/test/test_hashutil.c @@ -72,10 +72,16 @@ TEST(hash_command_output_stdout_versus_stderr) struct mdfour h1, h2; hash_start(&h1); hash_start(&h2); +#ifndef _WIN32 create_file("stderr.sh", "#!/bin/sh\necho foo >&2\n"); chmod("stderr.sh", 0555); CHECK(hash_command_output(&h1, "echo foo", "not used")); CHECK(hash_command_output(&h2, "./stderr.sh", "not used")); +#else + create_file("stderr.bat", "@echo off\r\necho foo>&2\r\n"); + CHECK(hash_command_output(&h1, "echo foo", "not used")); + CHECK(hash_command_output(&h2, "stderr.bat", "not used")); +#endif CHECK(hash_equal(&h1, &h2)); } @@ -84,10 +90,16 @@ TEST(hash_multicommand_output) struct mdfour h1, h2; hash_start(&h1); hash_start(&h2); +#ifndef _WIN32 create_file("foo.sh", "#!/bin/sh\necho foo\necho bar\n"); chmod("foo.sh", 0555); CHECK(hash_multicommand_output(&h2, "echo foo; echo bar", "not used")); CHECK(hash_multicommand_output(&h1, "./foo.sh", "not used")); +#else + create_file("foo.bat", "@echo off\r\necho foo\r\necho bar\r\n"); + CHECK(hash_multicommand_output(&h2, "echo foo; echo bar", "not used")); + CHECK(hash_multicommand_output(&h1, "foo.bat", "not used")); +#endif CHECK(hash_equal(&h1, &h2)); } diff --git a/util.c b/util.c index f0c7a5e17..aa5553535 100644 --- a/util.c +++ b/util.c @@ -1094,6 +1094,8 @@ x_realpath(const char *path) #if HAVE_REALPATH p = realpath(path, ret); #elif defined(_WIN32) + if (path[0] == '/') + path++; /* skip leading slash */ path_handle = CreateFile( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); @@ -1339,6 +1341,11 @@ get_relative_path(const char *from, const char *to) } #ifdef _WIN32 + // Paths can be escaped by a slash for use with -isystem + if (from[0] == '/') + from++; + if (to[0] == '/') + to++; // Both paths are absolute, drop the drive letters assert(from[0] == to[0]); // Assume the same drive letter from += 2; -- 2.47.2