Skip to content

Commit 26188cc

Browse files
authored
Merge pull request swiftlang#22268 from compnerd/planning-an-escape
2 parents 7989d3f + d30a2ab commit 26188cc

File tree

5 files changed

+82
-22
lines changed

5 files changed

+82
-22
lines changed

include/swift/FrontendTool/FrontendTool.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ class FrontendObserver {
4444
virtual void configuredCompiler(CompilerInstance &instance);
4545
};
4646

47+
namespace frontend {
48+
namespace utils {
49+
StringRef escapeForMake(StringRef raw, llvm::SmallVectorImpl<char> &buffer);
50+
}
51+
}
52+
4753
/// Perform all the operations of the frontend, exactly as if invoked
4854
/// with -frontend.
4955
///

lib/FrontendTool/FrontendTool.cpp

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,40 @@ static std::string displayName(StringRef MainExecutablePath) {
9999
return Name;
100100
}
101101

102+
StringRef
103+
swift::frontend::utils::escapeForMake(StringRef raw,
104+
llvm::SmallVectorImpl<char> &buffer) {
105+
buffer.clear();
106+
107+
// The escaping rules for GNU make are complicated due to the various
108+
// subsitutions and use of the tab in the leading position for recipes.
109+
// Various symbols have significance in different contexts. It is not
110+
// possible to correctly quote all characters in Make (as of 3.7). Match
111+
// gcc and clang's behaviour for the escaping which covers only a subset of
112+
// characters.
113+
for (unsigned I = 0, E = raw.size(); I != E; ++I) {
114+
switch (raw[I]) {
115+
case '#': // Handle '#' the broken GCC way
116+
buffer.push_back('\\');
117+
break;
118+
119+
case ' ':
120+
for (unsigned J = I; J && raw[J - 1] == '\\'; --J)
121+
buffer.push_back('\\');
122+
buffer.push_back('\\');
123+
break;
124+
125+
case '$': // $ is escaped by $
126+
buffer.push_back('$');
127+
break;
128+
}
129+
buffer.push_back(raw[I]);
130+
}
131+
buffer.push_back('\0');
132+
133+
return buffer.data();
134+
}
135+
102136
/// Emits a Make-style dependencies file.
103137
static bool emitMakeDependenciesIfNeeded(DiagnosticEngine &diags,
104138
DependencyTracker *depTracker,
@@ -118,39 +152,21 @@ static bool emitMakeDependenciesIfNeeded(DiagnosticEngine &diags,
118152
return true;
119153
}
120154

121-
// Declare a helper for escaping file names for use in Makefiles.
122-
llvm::SmallString<256> pathBuf;
123-
auto escape = [&](StringRef raw) -> StringRef {
124-
pathBuf.clear();
125-
126-
static const char badChars[] = " $#:\n";
127-
size_t prev = 0;
128-
for (auto index = raw.find_first_of(badChars); index != StringRef::npos;
129-
index = raw.find_first_of(badChars, index+1)) {
130-
pathBuf.append(raw.slice(prev, index));
131-
if (raw[index] == '$')
132-
pathBuf.push_back('$');
133-
else
134-
pathBuf.push_back('\\');
135-
prev = index;
136-
}
137-
pathBuf.append(raw.substr(prev));
138-
return pathBuf;
139-
};
155+
llvm::SmallString<256> buffer;
140156

141157
// FIXME: Xcode can't currently handle multiple targets in a single
142158
// dependency line.
143159
opts.forAllOutputPaths(input, [&](const StringRef targetName) {
144-
out << escape(targetName) << " :";
160+
out << swift::frontend::utils::escapeForMake(targetName, buffer) << " :";
145161
// First include all other files in the module. Make-style dependencies
146162
// need to be conservative!
147163
for (auto const &path :
148164
reversePathSortedFilenames(opts.InputsAndOutputs.getInputFilenames()))
149-
out << ' ' << escape(path);
165+
out << ' ' << swift::frontend::utils::escapeForMake(path, buffer);
150166
// Then print dependencies we've picked up during compilation.
151167
for (auto const &path :
152168
reversePathSortedFilenames(depTracker->getDependencies()))
153-
out << ' ' << escape(path);
169+
out << ' ' << swift::frontend::utils::escapeForMake(path, buffer);
154170
out << '\n';
155171
});
156172

unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ if(SWIFT_INCLUDE_TOOLS)
77
add_subdirectory(AST)
88
add_subdirectory(Basic)
99
add_subdirectory(Driver)
10+
add_subdirectory(FrontendTool)
1011
add_subdirectory(IDE)
1112
add_subdirectory(Parse)
1213
add_subdirectory(SwiftDemangle)

unittests/FrontendTool/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
add_swift_unittest(SwiftFrontendTests
2+
FrontendToolTests.cpp)
3+
target_link_libraries(SwiftFrontendTests PRIVATE swiftFrontendTool)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- FrontendTests.cpp ------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "gtest/gtest.h"
14+
#include "swift/FrontendTool/FrontendTool.h"
15+
#include "llvm/ADT/SmallString.h"
16+
17+
using namespace swift;
18+
19+
namespace {
20+
21+
TEST(FrontendTests, MakefileQuotingTest) {
22+
using namespace frontend::utils;
23+
24+
llvm::SmallString<256> buffer;
25+
EXPECT_EQ(escapeForMake("S:\\b\\llvm", buffer), "S:\\b\\llvm");
26+
EXPECT_EQ(escapeForMake("S:\\b\\swift\\$sBoWV", buffer),
27+
"S:\\b\\swift\\$$sBoWV");
28+
EXPECT_EQ(escapeForMake("S:\\b\\swift\\#hashtag", buffer),
29+
"S:\\b\\swift\\\\#hashtag");
30+
31+
}
32+
33+
} // end anonymous namespace
34+

0 commit comments

Comments
 (0)