Bazel client: implement directory tree walking
This change:
- merges the //src/{main,test}/cpp:file and
//src/{main,test}/cpp:file_platform libraries
because "file" and "file_platform" need each other
and this makes logical sense anyway
- implements a function in file_<platform> to run
a custom function on every child of a directory
- implements a function in file.cc to recursively
traverse a directory tree, based on the previosly
mentioned function
- removes the corresponding logic from the Bazel
client to make it more portable
--
MOS_MIGRATED_REVID=139309562
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index 7b3d893..019df86 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -27,7 +27,6 @@
#include <assert.h>
#include <ctype.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
@@ -867,42 +866,6 @@
#endif
}
-// Walks the temporary directory recursively and collects full file paths.
-static void CollectExtractedFiles(const string &dir_path, vector<string> &files) {
- DIR *dir;
- struct dirent *ent;
-
- if ((dir = opendir(dir_path.c_str())) == NULL) {
- die(blaze_exit_code::INTERNAL_ERROR, "opendir failed");
- }
-
- while ((ent = readdir(dir)) != NULL) {
- if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
- continue;
- }
-
- string filename(blaze_util::JoinPath(dir_path, ent->d_name));
- bool is_directory;
- if (ent->d_type == DT_UNKNOWN) {
- struct stat buf;
- if (lstat(filename.c_str(), &buf) == -1) {
- die(blaze_exit_code::INTERNAL_ERROR, "stat failed");
- }
- is_directory = S_ISDIR(buf.st_mode);
- } else {
- is_directory = (ent->d_type == DT_DIR);
- }
-
- if (is_directory) {
- CollectExtractedFiles(filename, files);
- } else {
- files.push_back(filename);
- }
- }
-
- closedir(dir);
-}
-
// A devtools_ijar::ZipExtractorProcessor to extract the files from the blaze
// zip.
class ExtractBlazeZipProcessor : public devtools_ijar::ZipExtractorProcessor {
@@ -974,19 +937,20 @@
// on the disk.
vector<string> extracted_files;
- CollectExtractedFiles(embedded_binaries, extracted_files);
+
+ // Walks the temporary directory recursively and collects full file paths.
+ blaze_util::GetAllFilesUnder(embedded_binaries, &extracted_files);
set<string> synced_directories;
- for (vector<string>::iterator it = extracted_files.begin(); it != extracted_files.end(); it++) {
-
- const char *extracted_path = it->c_str();
+ for (const auto &it : extracted_files) {
+ const char *extracted_path = it.c_str();
// Set the time to a distantly futuristic value so we can observe tampering.
- // Note that keeping the default timestamp set by unzip (1970-01-01) and using
- // that to detect tampering is not enough, because we also need the timestamp
- // to change between Blaze releases so that the metadata cache knows that
- // the files may have changed. This is important for actions that use
- // embedded binaries as artifacts.
+ // Note that keeping the default timestamp set by unzip (1970-01-01) and
+ // using that to detect tampering is not enough, because we also need the
+ // timestamp to change between Blaze releases so that the metadata cache
+ // knows that the files may have changed. This is important for actions that
+ // use embedded binaries as artifacts.
struct utimbuf times = { future_time, future_time };
if (utime(extracted_path, ×) == -1) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
diff --git a/src/main/cpp/util/BUILD b/src/main/cpp/util/BUILD
index 2d772be..bd1e80b 100644
--- a/src/main/cpp/util/BUILD
+++ b/src/main/cpp/util/BUILD
@@ -15,7 +15,6 @@
":blaze_exit_code",
":errors",
":file",
- ":file_platform",
":numbers",
":port",
":strings",
@@ -23,8 +22,8 @@
)
cc_library(
- name = "file_platform",
- srcs = select({
+ name = "file",
+ srcs = ["file.cc"] + select({
"//src:windows_msvc": [
"file_windows.cc",
],
@@ -32,22 +31,10 @@
"file_posix.cc",
],
}),
- hdrs = ["file_platform.h"],
- visibility = [
- "//src/test/cpp/util:__pkg__",
+ hdrs = [
+ "file.h",
+ "file_platform.h",
],
- deps = [
- ":blaze_exit_code",
- ":errors",
- ":file",
- ":strings",
- ],
-)
-
-cc_library(
- name = "file",
- srcs = ["file.cc"],
- hdrs = ["file.h"],
visibility = [
"//src/test/cpp/util:__pkg__",
"//src/tools/singlejar:__pkg__",
diff --git a/src/main/cpp/util/file.cc b/src/main/cpp/util/file.cc
index 0957a56..2aa6642 100644
--- a/src/main/cpp/util/file.cc
+++ b/src/main/cpp/util/file.cc
@@ -18,15 +18,16 @@
#include <cstdlib>
#include <vector>
+#include "src/main/cpp/util/file_platform.h"
#include "src/main/cpp/util/errors.h"
#include "src/main/cpp/util/exit_code.h"
#include "src/main/cpp/util/strings.h"
-using std::pair;
-
namespace blaze_util {
+using std::pair;
using std::string;
+using std::vector;
pair<string, string> SplitPath(const string &path) {
size_t pos = path.rfind('/');
@@ -73,4 +74,35 @@
}
}
+class DirectoryTreeWalker : public DirectoryEntryConsumer {
+ public:
+ DirectoryTreeWalker(vector<string> *files,
+ _ForEachDirectoryEntry walk_entries)
+ : _files(files), _walk_entries(walk_entries) {}
+
+ void Consume(const string &path, bool is_directory) override {
+ if (is_directory) {
+ Walk(path);
+ } else {
+ _files->push_back(path);
+ }
+ }
+
+ void Walk(const string &path) { _walk_entries(path, this); }
+
+ private:
+ vector<string> *_files;
+ _ForEachDirectoryEntry _walk_entries;
+};
+
+void GetAllFilesUnder(const string &path, vector<string> *result) {
+ _GetAllFilesUnder(path, result, &ForEachDirectoryEntry);
+}
+
+void _GetAllFilesUnder(const string &path,
+ vector<string> *result,
+ _ForEachDirectoryEntry walk_entries) {
+ DirectoryTreeWalker(result, walk_entries).Walk(path);
+}
+
} // namespace blaze_util
diff --git a/src/main/cpp/util/file.h b/src/main/cpp/util/file.h
index 238ccf0..dfa08ad 100644
--- a/src/main/cpp/util/file.h
+++ b/src/main/cpp/util/file.h
@@ -15,6 +15,7 @@
#define BAZEL_SRC_MAIN_CPP_UTIL_FILE_H_
#include <string>
+#include <vector>
namespace blaze_util {
@@ -29,6 +30,26 @@
std::string JoinPath(const std::string &path1, const std::string &path2);
+// Lists all files in `path` and all of its subdirectories.
+//
+// Does not follow symlinks / junctions.
+//
+// Populates `result` with the full paths of the files. Every entry will have
+// `path` as its prefix. If `path` is a file, `result` contains just this file.
+void GetAllFilesUnder(const std::string &path,
+ std::vector<std::string> *result);
+
+class DirectoryEntryConsumer;
+
+// Visible for testing only.
+typedef void (*_ForEachDirectoryEntry)(const std::string &path,
+ DirectoryEntryConsumer *consume);
+
+// Visible for testing only.
+void _GetAllFilesUnder(const std::string &path,
+ std::vector<std::string> *result,
+ _ForEachDirectoryEntry walk_entries);
+
} // namespace blaze_util
#endif // BAZEL_SRC_MAIN_CPP_UTIL_FILE_H_
diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h
index b0df5ea..c43be79 100644
--- a/src/main/cpp/util/file_platform.h
+++ b/src/main/cpp/util/file_platform.h
@@ -40,6 +40,27 @@
// Changes the current working directory to `path`, returns true upon success.
bool ChangeDirectory(const std::string& path);
+// Interface to be implemented by ForEachDirectoryEntry clients.
+class DirectoryEntryConsumer {
+ public:
+ virtual ~DirectoryEntryConsumer() {}
+
+ // This method is called for each entry in a directory.
+ // `name` is the full path of the entry.
+ // `is_directory` is true if this entry is a directory (but false if this is a
+ // symlink pointing to a directory).
+ virtual void Consume(const std::string &name, bool is_directory) = 0;
+};
+
+// Executes a function for each entry in a directory (except "." and "..").
+//
+// Returns true if the `path` referred to a directory or directory symlink,
+// false otherwise.
+//
+// See DirectoryEntryConsumer for more details.
+void ForEachDirectoryEntry(const std::string &path,
+ DirectoryEntryConsumer *consume);
+
} // namespace blaze_util
#endif // BAZEL_SRC_MAIN_CPP_UTIL_FILE_PLATFORM_H_
diff --git a/src/main/cpp/util/file_posix.cc b/src/main/cpp/util/file_posix.cc
index 90355b9..32f86da 100644
--- a/src/main/cpp/util/file_posix.cc
+++ b/src/main/cpp/util/file_posix.cc
@@ -14,6 +14,7 @@
#include "src/main/cpp/util/file_platform.h"
#include <sys/stat.h>
+#include <dirent.h> // DIR, dirent, opendir, closedir
#include <limits.h> // PATH_MAX
#include <stdlib.h> // getenv
#include <unistd.h> // access
@@ -84,4 +85,37 @@
return chdir(path.c_str()) == 0;
}
+void ForEachDirectoryEntry(const string &path,
+ DirectoryEntryConsumer *consume) {
+ DIR *dir;
+ struct dirent *ent;
+
+ if ((dir = opendir(path.c_str())) == NULL) {
+ // This is not a directory or it cannot be opened.
+ return;
+ }
+
+ while ((ent = readdir(dir)) != NULL) {
+ if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
+ continue;
+ }
+
+ string filename(blaze_util::JoinPath(path, ent->d_name));
+ bool is_directory;
+ if (ent->d_type == DT_UNKNOWN) {
+ struct stat buf;
+ if (lstat(filename.c_str(), &buf) == -1) {
+ die(blaze_exit_code::INTERNAL_ERROR, "stat failed");
+ }
+ is_directory = S_ISDIR(buf.st_mode);
+ } else {
+ is_directory = (ent->d_type == DT_DIR);
+ }
+
+ consume->Consume(filename, is_directory);
+ }
+
+ closedir(dir);
+}
+
} // namespace blaze_util
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index 5cf9987..c9df3af 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -43,4 +43,10 @@
pdie(255, "blaze_util::ChangeDirectory is not implemented on Windows");
}
+void ForEachDirectoryEntry(const string &path,
+ DirectoryEntryConsumer *consume) {
+ // TODO(bazel-team): implement this.
+ pdie(255, "blaze_util::ForEachDirectoryEntry is not implemented on Windows");
+}
+
} // namespace blaze_util