Skip to content

Commit

Permalink
Windows: expand path names on command line
Browse files Browse the repository at this point in the history
  • Loading branch information
laszlocsomor committed Aug 14, 2018
1 parent fe2eef4 commit 414a625
Show file tree
Hide file tree
Showing 8 changed files with 1,615 additions and 21 deletions.
20 changes: 1 addition & 19 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -362,25 +362,7 @@ cc_library(
],
copts = COPTS,
includes = ["src/"],
linkopts = LINK_OPTS + select({
":msvc": [
# Linking to setargv.obj makes the default command line argument
# parser expand wildcards, so the main method's argv will contain the
# expanded list instead of the wildcards.
#
# Adding dummy "-DEFAULTLIB:kernel32.lib", because:
# - Microsoft ships this object file next to default libraries
# - but this file is not a library, just a precompiled object
# - "-WHOLEARCHIVE" and "-DEFAULTLIB" only accept library,
# not precompiled object.
# - Bazel would assume linkopt that does not start with "-" or "$"
# as a label to a target, so we add a harmless "-DEFAULTLIB:kernel32.lib"
# before "setargv.obj".
# See https://msdn.microsoft.com/en-us/library/8bch7bkk.aspx
"-DEFAULTLIB:kernel32.lib setargv.obj",
],
"//conditions:default": [],
}),
linkopts = LINK_OPTS,
visibility = ["//visibility:public"],
deps = [":protobuf"],
)
Expand Down
25 changes: 25 additions & 0 deletions src/google/protobuf/compiler/command_line_interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1388,7 +1388,32 @@ CommandLineInterface::InterpretArgument(const string& name,
return PARSE_ARGUMENT_FAIL;
}

#if defined(_WIN32)
// On Windows, the shell (typically cmd.exe) does not expand wildcards in
// file names (e.g. foo\*.proto), so we do it ourselves.
switch (google::protobuf::internal::win32::expand_wildcards(
value,
[this](const string& path) {
this->input_files_.push_back(path);
})) {
case google::protobuf::internal::win32::ExpandWildcardsResult::kSuccess:
break;
case google::protobuf::internal::win32::ExpandWildcardsResult::kErrorNoMatchingFile:
// Path does not exist, is not a file, or it's longer than MAX_PATH and
// long path handling is disabled.
std::cerr << "Invalid file name pattern or missing input file \""
<< value << "\"" << std::endl;
return PARSE_ARGUMENT_FAIL;
default:
std::cerr << "Cannot convert path \"" << value
<< "\" to or from Windows style" << std::endl;
return PARSE_ARGUMENT_FAIL;
}
#else // not _WIN32
// On other platforms than Windows (e.g. Linux, Mac OS) the shell (typically
// Bash) expands wildcards.
input_files_.push_back(value);
#endif // _WIN32

} else if (name == "-I" || name == "--proto_path") {
if (!descriptor_set_in_names_.empty()) {
Expand Down
91 changes: 91 additions & 0 deletions src/google/protobuf/stubs/io_win32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <wctype.h>

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif

#include <windows.h>

#include <google/protobuf/stubs/io_win32.h>
Expand Down Expand Up @@ -355,6 +360,92 @@ wstring testonly_utf8_to_winpath(const char* path) {
return as_windows_path(path, &wpath) ? wpath : wstring();
}

bool expand_wildcards(
const string& path, std::function<void(const string&)> consume) {
if (path.find_first_of("*?") == string::npos) {
// There are no wildcards in the path, we don't need to expand it.
consume(path);
return ExpandWildcardsResult::kSuccess;
}

#ifdef SUPPORT_LONGPATHS

wstring wpath;
if (!as_windows_path(path.c_str(), &wpath)) {
return ExpandWildcardsResult::kErrorInputPathConversion;
}

static const wstring kDot = L".";
static const wstring kDotDot = L"..";
WIN32_FIND_DATAW metadata;
HANDLE handle = ::FindFirstFileW(wpath.c_str(), &metadata);
if (handle == INVALID_HANDLE_VALUE) {
// The pattern does not match any files (or directories).
return ExpandWildcardsResult::kErrorNoMatchingFile;
}

string::size_type pos = path.find_last_of("\\/");
string dirname;
if (pos != string::npos) {
dirname = path.substr(0, pos + 1);
}

int matched = ExpandWildcardsResult::kErrorNoMatchingFile;
do {
// Ignore ".", "..", and directories.
if ((metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0
&& kDot != metadata.cFileName && kDotDot != metadata.cFileName) {
matched = ExpandWildcardsResult::kSuccess;
string filename;
if (!strings::wcs_to_utf8(metadata.cFileName, &filename)) {
return ExpandWildcardsResult::kErrorOutputPathConversion;
}

if (dirname.empty()) {
consume(filename);
} else {
consume(dirname + filename);
}
}
} while (::FindNextFileW(handle, &metadata));
FindClose(handle);
return matched;

#else // not SUPPORT_LONGPATHS

static const string kDot = ".";
static const string kDotDot = "..";
WIN32_FIND_DATAA metadata;
HANDLE handle = ::FindFirstFileA(path.c_str(), &metadata);
if (handle == INVALID_HANDLE_VALUE) {
// The pattern does not match any files (or directories).
return ExpandWildcardsResult::kErrorNoMatchingFile;
}

string::size_type pos = path.find_last_of("\\/");
string dirname;
if (pos != string::npos) {
dirname = path.substr(0, pos + 1);
}

int matched = ExpandWildcardsResult::kErrorNoMatchingFile;
do {
// Ignore ".", "..", and directories.
if ((metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0
&& kDot != metadata.cFileName && kDotDot != metadata.cFileName) {
matched = ExpandWildcardsResult::kSuccess;
if (!dirname.empty()) {
consume(dirname + metadata.cFileName);
} else {
consume(metadata.cFileName);
}
}
} while (::FindNextFileA(handle, &metadata));
FindClose(handle);
return matched;
#endif // SUPPORT_LONGPATHS
}

namespace strings {

bool wcs_to_mbs(const WCHAR* s, string* out, bool outUtf8) {
Expand Down
Loading

0 comments on commit 414a625

Please sign in to comment.