14
14
#include " swift/AST/ASTContext.h"
15
15
#include " swift/AST/Decl.h"
16
16
#include " swift/AST/DiagnosticsFrontend.h"
17
+ #include " swift/AST/FileSystem.h"
17
18
#include " swift/AST/Module.h"
18
19
#include " swift/Frontend/Frontend.h"
19
20
#include " swift/Frontend/ParseableInterfaceSupport.h"
20
21
#include " swift/SILOptimizer/PassManager/Passes.h"
21
22
#include " swift/Serialization/SerializationOptions.h"
22
23
#include " clang/Basic/Module.h"
24
+ #include " llvm/ADT/Hashing.h"
23
25
#include " llvm/Support/Debug.h"
24
26
#include " llvm/Support/CommandLine.h"
25
27
#include " llvm/Support/CrashRecoveryContext.h"
@@ -31,6 +33,7 @@ using namespace swift;
31
33
32
34
#define SWIFT_TOOLS_VERSION_KEY " swift-tools-version"
33
35
#define SWIFT_MODULE_FLAGS_KEY " swift-module-flags"
36
+ #define SWIFT_INTERFACE_DEPS_VERSION " swift-interface-deps-version-1"
34
37
35
38
static bool
36
39
extractSwiftInterfaceVersionAndArgs (DiagnosticEngine &Diags,
@@ -66,11 +69,44 @@ extractSwiftInterfaceVersionAndArgs(DiagnosticEngine &Diags,
66
69
return false ;
67
70
}
68
71
72
+ // / Construct a cache key for the .swiftmodule being generated. There is a
73
+ // / balance to be struck here between things that go in the cache key and
74
+ // / things that go in the "up to date" check of the cache entry. We want to
75
+ // / avoid fighting over a single cache entry too much when (say) running
76
+ // / different compiler versions on the same machine or different inputs
77
+ // / that happen to have the same short module name, so we will disambiguate
78
+ // / those in the key. But we want to invalidate and rebuild a cache entry
79
+ // / -- rather than making a new one and potentially filling up the cache
80
+ // / with dead entries -- when other factors change, such as the contents of
81
+ // / the .swiftinterface input or its dependencies.
82
+ std::string getCacheHash (ASTContext &Ctx,
83
+ CompilerInvocation &SubInvocation,
84
+ StringRef InPath) {
85
+ // Start with the compiler version (which will be either tag names or revs).
86
+ std::string vers = swift::version::getSwiftFullVersion (
87
+ Ctx.LangOpts .EffectiveLanguageVersion );
88
+ llvm::hash_code H = llvm::hash_value (vers);
89
+
90
+ // Simplest representation of input "identity" (not content) is just a
91
+ // pathname, and probably all we can get from the VFS in this regard anyways.
92
+ H = llvm::hash_combine (H, InPath);
93
+
94
+ // ClangImporterOpts does include the target CPU, which is redundant: we
95
+ // already have separate .swiftinterface files per target due to expanding
96
+ // preprocessing directives, but further specializing the cache key to that
97
+ // target is harmless and will not make any extra cache entries, so allow it.
98
+ H = llvm::hash_combine (
99
+ H, SubInvocation.getClangImporterOptions ().getPCHHashComponents ());
100
+
101
+ return llvm::APInt (64 , H).toString (36 , /* Signed=*/ false );
102
+ }
103
+
69
104
void
70
- ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPath (
105
+ ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPaths (
71
106
CompilerInvocation &SubInvocation,
72
107
StringRef InPath,
73
- llvm::SmallString<128 > &OutPath) {
108
+ llvm::SmallString<128 > &OutPath,
109
+ llvm::SmallString<128 > &DepPath) {
74
110
75
111
auto &SearchPathOpts = Ctx.SearchPathOpts ;
76
112
auto &LangOpts = Ctx.LangOpts ;
@@ -84,16 +120,19 @@ ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPath(
84
120
SubInvocation.setRuntimeResourcePath (SearchPathOpts.RuntimeResourcePath );
85
121
SubInvocation.setTargetTriple (LangOpts.Target );
86
122
87
- // Calculate an output filename based on the SubInvocation hash, and
88
- // wire up the SubInvocation's InputsAndOutputs to contain both
89
- // input and output filenames.
123
+ // Calculate an output filename that includes a hash of relevant key data , and
124
+ // wire up the SubInvocation's InputsAndOutputs to contain both input and
125
+ // output filenames.
90
126
OutPath = CacheDir;
91
127
llvm::sys::path::append (OutPath, llvm::sys::path::stem (InPath));
92
128
OutPath.append (" -" );
93
- OutPath.append (SubInvocation. getPCHHash ( ));
129
+ OutPath.append (getCacheHash (Ctx, SubInvocation, InPath ));
94
130
OutPath.append (" ." );
95
- auto Ext = file_types::getExtension (file_types::TY_SwiftModuleFile);
96
- OutPath.append (Ext);
131
+ DepPath = OutPath;
132
+ auto OutExt = file_types::getExtension (file_types::TY_SwiftModuleFile);
133
+ OutPath.append (OutExt);
134
+ auto DepExt = file_types::getExtension (file_types::TY_SwiftParseableInterfaceDeps);
135
+ DepPath.append (DepExt);
97
136
98
137
auto &FEOpts = SubInvocation.getFrontendOptions ();
99
138
FEOpts.RequestedAction = FrontendOptions::ActionType::EmitModuleOnly;
@@ -104,24 +143,67 @@ ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPath(
104
143
FEOpts.InputsAndOutputs .setMainAndSupplementaryOutputs ({MainOut}, {SOPs});
105
144
}
106
145
107
- // FIXME: this needs to be a more extensive up-to-date check.
146
+ // Write the world's simplest dependencies file: a version identifier on
147
+ // a line followed by a list of files, one per line.
148
+ static bool writeSwiftInterfaceDeps (DiagnosticEngine &Diags,
149
+ ArrayRef<std::string> Deps,
150
+ StringRef DepPath) {
151
+ return withOutputFile (Diags, DepPath, [&](llvm::raw_pwrite_stream &out) {
152
+ out << SWIFT_INTERFACE_DEPS_VERSION << ' \n ' ;
153
+ for (auto const &D : Deps) {
154
+ out << D << ' \n ' ;
155
+ }
156
+ return false ;
157
+ });
158
+ }
159
+
160
+ // Check that the output .swiftmodule file is at least as new as all the
161
+ // dependencies it read when it was built last time.
108
162
static bool
109
163
swiftModuleIsUpToDate (clang::vfs::FileSystem &FS,
110
- StringRef InPath, StringRef OutPath) {
111
- if (FS.exists (OutPath)) {
112
- auto InStatus = FS.status (InPath);
113
- auto OutStatus = FS.status (OutPath);
114
- if (InStatus && OutStatus) {
115
- return InStatus.get ().getLastModificationTime () <=
116
- OutStatus.get ().getLastModificationTime ();
164
+ StringRef InPath, StringRef OutPath, StringRef DepPath) {
165
+
166
+ if (!FS.exists (OutPath) || !FS.exists (DepPath))
167
+ return false ;
168
+
169
+ auto OutStatus = FS.status (OutPath);
170
+ if (!OutStatus)
171
+ return false ;
172
+
173
+ auto DepBuf = FS.getBufferForFile (DepPath);
174
+ if (!DepBuf)
175
+ return false ;
176
+
177
+ // Split the deps file into a vector of lines.
178
+ StringRef Deps = DepBuf.get ()->getBuffer ();
179
+ SmallVector<StringRef, 16 > AllDeps;
180
+ Deps.split (AllDeps, ' \n ' , /* MaxSplit=*/ -1 , /* KeepEmpty=*/ false );
181
+
182
+ // First line in vector is a version-string; check it is the expected value.
183
+ if (AllDeps.size () < 1 ||
184
+ AllDeps[0 ] != SWIFT_INTERFACE_DEPS_VERSION) {
185
+ return false ;
186
+ }
187
+
188
+ // Overwrite the version-string entry in the vector with the .swiftinterface
189
+ // input file we're reading, then stat() every entry in the vector and check
190
+ // none are newer than the .swiftmodule (OutStatus).
191
+ AllDeps[0 ] = InPath;
192
+ for (auto In : AllDeps) {
193
+ auto InStatus = FS.status (In);
194
+ if (!InStatus ||
195
+ (InStatus.get ().getLastModificationTime () >
196
+ OutStatus.get ().getLastModificationTime ())) {
197
+ return false ;
117
198
}
118
199
}
119
- return false ;
200
+ return true ;
120
201
}
121
202
122
203
static bool buildSwiftModuleFromSwiftInterface (
123
204
clang::vfs::FileSystem &FS, DiagnosticEngine &Diags,
124
- CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath) {
205
+ CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath,
206
+ StringRef DepPath) {
125
207
bool SubError = false ;
126
208
bool RunSuccess = llvm::CrashRecoveryContext ().RunSafelyOnThread ([&] {
127
209
@@ -145,6 +227,7 @@ static bool buildSwiftModuleFromSwiftInterface(
145
227
// module-serialization task we're trying to do here.
146
228
LLVM_DEBUG (llvm::dbgs () << " Setting up instance\n " );
147
229
CompilerInstance SubInstance;
230
+ SubInstance.createDependencyTracker (/* TrackSystemDeps=*/ false );
148
231
if (SubInstance.setup (SubInvocation)) {
149
232
SubError = true ;
150
233
return ;
@@ -179,6 +262,12 @@ static bool buildSwiftModuleFromSwiftInterface(
179
262
});
180
263
SILMod->serialize ();
181
264
SubError = Diags.hadAnyError ();
265
+
266
+ if (!SubError) {
267
+ SubError |= writeSwiftInterfaceDeps (
268
+ Diags, SubInstance.getDependencyTracker ()->getDependencies (),
269
+ DepPath);
270
+ }
182
271
});
183
272
return !RunSuccess || SubError;
184
273
}
@@ -194,7 +283,7 @@ std::error_code ParseableInterfaceModuleLoader::openModuleFiles(
194
283
195
284
auto &FS = *Ctx.SourceMgr .getFileSystem ();
196
285
auto &Diags = Ctx.Diags ;
197
- llvm::SmallString<128 > InPath, OutPath;
286
+ llvm::SmallString<128 > InPath, OutPath, DepPath ;
198
287
199
288
// First check to see if the .swiftinterface exists at all. Bail if not.
200
289
InPath = DirName;
@@ -207,12 +296,12 @@ std::error_code ParseableInterfaceModuleLoader::openModuleFiles(
207
296
// Set up a _potential_ sub-invocation to consume the .swiftinterface and emit
208
297
// the .swiftmodule.
209
298
CompilerInvocation SubInvocation;
210
- configureSubInvocationAndOutputPath (SubInvocation, InPath, OutPath);
299
+ configureSubInvocationAndOutputPaths (SubInvocation, InPath, OutPath, DepPath );
211
300
212
301
// Evaluate if we need to run this sub-invocation, and if so run it.
213
- if (!swiftModuleIsUpToDate (FS, InPath, OutPath)) {
302
+ if (!swiftModuleIsUpToDate (FS, InPath, OutPath, DepPath )) {
214
303
if (buildSwiftModuleFromSwiftInterface (FS, Diags, SubInvocation, InPath,
215
- OutPath))
304
+ OutPath, DepPath ))
216
305
return std::make_error_code (std::errc::invalid_argument);
217
306
}
218
307
0 commit comments