The rewriting of the dependency file content introduced in
2ab31632 just
replaces the object file part of the dependency file with the raw
filename. This is incorrect if the filename includes special characters
such as space or dollar.
Fix this by escaping the filename properly. Note that newlines are not
treated specially since Make cannot handle them anyway.
See also the similar bug fix for the depend mode (but for parsing
instead of escaping in that case) in
1d2b5bf4.
namespace Depfile {
+std::string
+escape_filename(nonstd::string_view filename)
+{
+ std::string result;
+ result.reserve(filename.size());
+ for (const char c : filename) {
+ switch (c) {
+ case '\\':
+ case '#':
+ case ':':
+ case ' ':
+ case '\t':
+ result.push_back('\\');
+ break;
+ case '$':
+ result.push_back('$');
+ break;
+ }
+ result.push_back(c);
+ }
+ return result;
+}
+
nonstd::optional<std::string>
rewrite_paths(const Context& ctx, const std::string& file_content)
{
namespace Depfile {
+std::string escape_filename(nonstd::string_view filename);
nonstd::optional<std::string> rewrite_paths(const Context& ctx,
const std::string& file_content);
void make_paths_relative_in_output_dep(const Context& ctx);
#include "ResultRetriever.hpp"
#include "Context.hpp"
+#include "Depfile.hpp"
#include "Logging.hpp"
using Result::FileType;
if (m_rewrite_dependency_target) {
size_t colon_pos = m_dest_data.find(':');
if (colon_pos != std::string::npos) {
- Util::write_fd(*m_dest_fd,
- m_ctx.args_info.output_obj.data(),
- m_ctx.args_info.output_obj.length());
+ const auto escaped_output_obj =
+ Depfile::escape_filename(m_ctx.args_info.output_obj);
+ Util::write_fd(
+ *m_dest_fd, escaped_output_obj.data(), escaped_output_obj.length());
start_pos = colon_pos;
}
}
expect_stat 'files in cache' 5
# -------------------------------------------------------------------------
+ TEST "Source file with special characters"
- # TODO: Add more test cases (see direct.bash for inspiration)
+ touch 'file with$special#characters.c'
+ $REAL_COMPILER -MMD -c 'file with$special#characters.c'
+ mv 'file with$special#characters.d' reference.d
+
+ CCACHE_DEPEND=1 $CCACHE_COMPILE -MMD -c 'file with$special#characters.c'
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_content 'file with$special#characters.d' reference.d
+
+ rm 'file with$special#characters.d'
+
+ CCACHE_DEPEND=1 $CCACHE_COMPILE -MMD -c 'file with$special#characters.c'
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_content 'file with$special#characters.d' reference.d
}
TEST_SUITE_BEGIN("Depfile");
+TEST_CASE("Depfile::escape_filename")
+{
+ CHECK(Depfile::escape_filename("") == "");
+ CHECK(Depfile::escape_filename("foo") == "foo");
+ CHECK(Depfile::escape_filename("foo\\bar") == "foo\\\\bar");
+ CHECK(Depfile::escape_filename("foo#bar") == "foo\\#bar");
+ CHECK(Depfile::escape_filename("foo bar") == "foo\\ bar");
+ CHECK(Depfile::escape_filename("foo\tbar") == "foo\\\tbar");
+ CHECK(Depfile::escape_filename("foo$bar") == "foo$$bar");
+}
+
TEST_CASE("Depfile::rewrite_paths")
{
Context ctx;