Skip to content

Commit adacecb

Browse files
committed
[NFC] Split Make-Style Dependency Emission from FrontendTool
1 parent 67cc4d0 commit adacecb

File tree

4 files changed

+178
-122
lines changed

4 files changed

+178
-122
lines changed

lib/FrontendTool/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ set_swift_llvm_is_available()
22
add_swift_host_library(swiftFrontendTool STATIC
33
FrontendTool.cpp
44
ImportedModules.cpp
5+
MakeStyleDependencies.cpp
56
ScanDependencies.cpp
67
TBD.cpp)
78
add_dependencies(swiftFrontendTool

lib/FrontendTool/Dependencies.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===--- Dependencies.h -- Unified header for dependnecy tracing utilies --===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 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+
#ifndef SWIFT_FRONTENDTOOL_DEPENDENCIES_H
14+
#define SWIFT_FRONTENDTOOL_DEPENDENCIES_H
15+
16+
namespace swift {
17+
18+
class ASTContext;
19+
class DependencyTracker;
20+
class DiagnosticEngine;
21+
class FrontendOptions;
22+
class InputFile;
23+
class ModuleDecl;
24+
25+
bool emitMakeDependenciesIfNeeded(DiagnosticEngine &diags,
26+
DependencyTracker *depTracker,
27+
const FrontendOptions &opts,
28+
const InputFile &input);
29+
} // end namespace swift
30+
31+
#endif

lib/FrontendTool/FrontendTool.cpp

Lines changed: 2 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
//===----------------------------------------------------------------------===//
2222

2323
#include "swift/FrontendTool/FrontendTool.h"
24-
#include "ImportedModules.h"
24+
#include "Dependencies.h"
2525
#include "ScanDependencies.h"
2626
#include "TBD.h"
2727

@@ -107,132 +107,12 @@ static std::string displayName(StringRef MainExecutablePath) {
107107
return Name;
108108
}
109109

110-
StringRef
111-
swift::frontend::utils::escapeForMake(StringRef raw,
112-
llvm::SmallVectorImpl<char> &buffer) {
113-
buffer.clear();
114-
115-
// The escaping rules for GNU make are complicated due to the various
116-
// subsitutions and use of the tab in the leading position for recipes.
117-
// Various symbols have significance in different contexts. It is not
118-
// possible to correctly quote all characters in Make (as of 3.7). Match
119-
// gcc and clang's behaviour for the escaping which covers only a subset of
120-
// characters.
121-
for (unsigned I = 0, E = raw.size(); I != E; ++I) {
122-
switch (raw[I]) {
123-
case '#': // Handle '#' the broken GCC way
124-
buffer.push_back('\\');
125-
break;
126-
127-
case ' ':
128-
for (unsigned J = I; J && raw[J - 1] == '\\'; --J)
129-
buffer.push_back('\\');
130-
buffer.push_back('\\');
131-
break;
132-
133-
case '$': // $ is escaped by $
134-
buffer.push_back('$');
135-
break;
136-
}
137-
buffer.push_back(raw[I]);
138-
}
139-
buffer.push_back('\0');
140-
141-
return buffer.data();
142-
}
143-
144-
/// This sorting function is used to stabilize the order in which dependencies
145-
/// are emitted into \c .d files that are consumed by external build systems.
146-
/// This serves to eliminate order as a source of non-determinism in these
147-
/// outputs.
148-
///
149-
/// The exact sorting predicate is not important. Currently, it is a
150-
/// lexicographic comparison that reverses the provided strings before applying
151-
/// the sorting predicate. This has the benefit of being somewhat
152-
/// invariant with respect to the installation location of various system
153-
/// components. e.g. on two systems, the same file identified by two different
154-
/// paths differing only in their relative install location such as
155-
///
156-
/// /Applications/MyXcode.app/Path/To/A/Framework/In/The/SDK/Header.h
157-
/// /Applications/Xcodes/AnotherXcode.app/Path/To/A/Framework/In/The/SDK/Header.h
158-
///
159-
/// should appear in roughly the same order relative to other paths. Ultimately,
160-
/// this makes it easier to test the contents of the emitted files with tools
161-
/// like FileCheck.
162-
static std::vector<std::string>
163-
reversePathSortedFilenames(const ArrayRef<std::string> elts) {
164-
std::vector<std::string> tmp(elts.begin(), elts.end());
165-
std::sort(tmp.begin(), tmp.end(), [](const std::string &a,
166-
const std::string &b) -> bool {
167-
return std::lexicographical_compare(a.rbegin(), a.rend(),
168-
b.rbegin(), b.rend());
169-
});
170-
return tmp;
171-
}
172-
173-
/// Emits a Make-style dependencies file.
174-
static bool emitMakeDependenciesIfNeeded(DiagnosticEngine &diags,
175-
DependencyTracker *depTracker,
176-
const FrontendOptions &opts,
177-
const InputFile &input) {
178-
const std::string &dependenciesFilePath = input.dependenciesFilePath();
179-
if (dependenciesFilePath.empty())
180-
return false;
181-
182-
std::error_code EC;
183-
llvm::raw_fd_ostream out(dependenciesFilePath, EC, llvm::sys::fs::F_None);
184-
185-
if (out.has_error() || EC) {
186-
diags.diagnose(SourceLoc(), diag::error_opening_output,
187-
dependenciesFilePath, EC.message());
188-
out.clear_error();
189-
return true;
190-
}
191-
192-
llvm::SmallString<256> buffer;
193-
194-
// collect everything in memory to avoid redundant work
195-
// when there are multiple targets
196-
std::string dependencyString;
197-
198-
// First include all other files in the module. Make-style dependencies
199-
// need to be conservative!
200-
auto inputPaths =
201-
reversePathSortedFilenames(opts.InputsAndOutputs.getInputFilenames());
202-
for (auto const &path : inputPaths) {
203-
dependencyString.push_back(' ');
204-
dependencyString.append(frontend::utils::escapeForMake(path, buffer).str());
205-
}
206-
// Then print dependencies we've picked up during compilation.
207-
auto dependencyPaths =
208-
reversePathSortedFilenames(depTracker->getDependencies());
209-
for (auto const &path : dependencyPaths) {
210-
dependencyString.push_back(' ');
211-
dependencyString.append(frontend::utils::escapeForMake(path, buffer).str());
212-
}
213-
auto incrementalDependencyPaths =
214-
reversePathSortedFilenames(depTracker->getIncrementalDependencies());
215-
for (auto const &path : incrementalDependencyPaths) {
216-
dependencyString.push_back(' ');
217-
dependencyString.append(frontend::utils::escapeForMake(path, buffer).str());
218-
}
219-
220-
// FIXME: Xcode can't currently handle multiple targets in a single
221-
// dependency line.
222-
opts.forAllOutputPaths(input, [&](const StringRef targetName) {
223-
auto targetNameEscaped = frontend::utils::escapeForMake(targetName, buffer);
224-
out << targetNameEscaped << " :" << dependencyString << '\n';
225-
});
226-
227-
return false;
228-
}
229-
230110
static void emitMakeDependenciesIfNeeded(DiagnosticEngine &diags,
231111
DependencyTracker *depTracker,
232112
const FrontendOptions &opts) {
233113
opts.InputsAndOutputs.forEachInputProducingSupplementaryOutput(
234114
[&](const InputFile &f) -> bool {
235-
return emitMakeDependenciesIfNeeded(diags, depTracker, opts, f);
115+
return swift::emitMakeDependenciesIfNeeded(diags, depTracker, opts, f);
236116
});
237117
}
238118

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//===--- MakeStyleDependencies.cpp -- Emit make-style dependencies --------===//
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 "Dependencies.h"
14+
#include "swift/AST/DiagnosticEngine.h"
15+
#include "swift/AST/DiagnosticsFrontend.h"
16+
#include "swift/AST/ModuleLoader.h"
17+
#include "swift/Frontend/FrontendOptions.h"
18+
#include "swift/Frontend/InputFile.h"
19+
#include "swift/FrontendTool/FrontendTool.h"
20+
21+
#include "llvm/ADT/SmallString.h"
22+
#include "llvm/ADT/StringRef.h"
23+
24+
using namespace swift;
25+
26+
StringRef
27+
swift::frontend::utils::escapeForMake(StringRef raw,
28+
llvm::SmallVectorImpl<char> &buffer) {
29+
buffer.clear();
30+
31+
// The escaping rules for GNU make are complicated due to the various
32+
// subsitutions and use of the tab in the leading position for recipes.
33+
// Various symbols have significance in different contexts. It is not
34+
// possible to correctly quote all characters in Make (as of 3.7). Match
35+
// gcc and clang's behaviour for the escaping which covers only a subset of
36+
// characters.
37+
for (unsigned I = 0, E = raw.size(); I != E; ++I) {
38+
switch (raw[I]) {
39+
case '#': // Handle '#' the broken GCC way
40+
buffer.push_back('\\');
41+
break;
42+
43+
case ' ':
44+
for (unsigned J = I; J && raw[J - 1] == '\\'; --J)
45+
buffer.push_back('\\');
46+
buffer.push_back('\\');
47+
break;
48+
49+
case '$': // $ is escaped by $
50+
buffer.push_back('$');
51+
break;
52+
}
53+
buffer.push_back(raw[I]);
54+
}
55+
buffer.push_back('\0');
56+
57+
return buffer.data();
58+
}
59+
60+
/// This sorting function is used to stabilize the order in which dependencies
61+
/// are emitted into \c .d files that are consumed by external build systems.
62+
/// This serves to eliminate order as a source of non-determinism in these
63+
/// outputs.
64+
///
65+
/// The exact sorting predicate is not important. Currently, it is a
66+
/// lexicographic comparison that reverses the provided strings before applying
67+
/// the sorting predicate. This has the benefit of being somewhat
68+
/// invariant with respect to the installation location of various system
69+
/// components. e.g. on two systems, the same file identified by two different
70+
/// paths differing only in their relative install location such as
71+
///
72+
/// /Applications/MyXcode.app/Path/To/A/Framework/In/The/SDK/Header.h
73+
/// /Applications/Xcodes/AnotherXcode.app/Path/To/A/Framework/In/The/SDK/Header.h
74+
///
75+
/// should appear in roughly the same order relative to other paths. Ultimately,
76+
/// this makes it easier to test the contents of the emitted files with tools
77+
/// like FileCheck.
78+
static std::vector<std::string>
79+
reversePathSortedFilenames(const ArrayRef<std::string> elts) {
80+
std::vector<std::string> tmp(elts.begin(), elts.end());
81+
std::sort(tmp.begin(), tmp.end(),
82+
[](const std::string &a, const std::string &b) -> bool {
83+
return std::lexicographical_compare(a.rbegin(), a.rend(),
84+
b.rbegin(), b.rend());
85+
});
86+
return tmp;
87+
}
88+
89+
/// Emits a Make-style dependencies file.
90+
bool swift::emitMakeDependenciesIfNeeded(DiagnosticEngine &diags,
91+
DependencyTracker *depTracker,
92+
const FrontendOptions &opts,
93+
const InputFile &input) {
94+
const std::string &dependenciesFilePath = input.dependenciesFilePath();
95+
if (dependenciesFilePath.empty())
96+
return false;
97+
98+
std::error_code EC;
99+
llvm::raw_fd_ostream out(dependenciesFilePath, EC, llvm::sys::fs::F_None);
100+
101+
if (out.has_error() || EC) {
102+
diags.diagnose(SourceLoc(), diag::error_opening_output,
103+
dependenciesFilePath, EC.message());
104+
out.clear_error();
105+
return true;
106+
}
107+
108+
llvm::SmallString<256> buffer;
109+
110+
// collect everything in memory to avoid redundant work
111+
// when there are multiple targets
112+
std::string dependencyString;
113+
114+
// First include all other files in the module. Make-style dependencies
115+
// need to be conservative!
116+
auto inputPaths =
117+
reversePathSortedFilenames(opts.InputsAndOutputs.getInputFilenames());
118+
for (auto const &path : inputPaths) {
119+
dependencyString.push_back(' ');
120+
dependencyString.append(frontend::utils::escapeForMake(path, buffer).str());
121+
}
122+
// Then print dependencies we've picked up during compilation.
123+
auto dependencyPaths =
124+
reversePathSortedFilenames(depTracker->getDependencies());
125+
for (auto const &path : dependencyPaths) {
126+
dependencyString.push_back(' ');
127+
dependencyString.append(frontend::utils::escapeForMake(path, buffer).str());
128+
}
129+
auto incrementalDependencyPaths =
130+
reversePathSortedFilenames(depTracker->getIncrementalDependencies());
131+
for (auto const &path : incrementalDependencyPaths) {
132+
dependencyString.push_back(' ');
133+
dependencyString.append(frontend::utils::escapeForMake(path, buffer).str());
134+
}
135+
136+
// FIXME: Xcode can't currently handle multiple targets in a single
137+
// dependency line.
138+
opts.forAllOutputPaths(input, [&](const StringRef targetName) {
139+
auto targetNameEscaped = frontend::utils::escapeForMake(targetName, buffer);
140+
out << targetNameEscaped << " :" << dependencyString << '\n';
141+
});
142+
143+
return false;
144+
}

0 commit comments

Comments
 (0)