Skip to content

Commit b4a96bb

Browse files
committed
[ModuleInterface] Write, read-back and test deps stored in .sid files.
1 parent fa95f7a commit b4a96bb

File tree

3 files changed

+99
-21
lines changed

3 files changed

+99
-21
lines changed

include/swift/Frontend/ParseableInterfaceSupport.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ class ParseableInterfaceModuleLoader : public SerializedModuleLoaderBase {
6464
std::string CacheDir;
6565

6666
void
67-
configureSubInvocationAndOutputPath(CompilerInvocation &SubInvocation,
68-
StringRef InPath,
69-
llvm::SmallString<128> &OutPath);
67+
configureSubInvocationAndOutputPaths(CompilerInvocation &SubInvocation,
68+
StringRef InPath,
69+
llvm::SmallString<128> &OutPath,
70+
llvm::SmallString<128> &DepPath);
7071

7172
std::error_code
7273
openModuleFiles(StringRef DirName, StringRef ModuleFilename,

lib/Frontend/ParseableInterfaceSupport.cpp

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "swift/AST/ASTContext.h"
1515
#include "swift/AST/Decl.h"
1616
#include "swift/AST/DiagnosticsFrontend.h"
17+
#include "swift/AST/FileSystem.h"
1718
#include "swift/AST/Module.h"
1819
#include "swift/Frontend/Frontend.h"
1920
#include "swift/Frontend/ParseableInterfaceSupport.h"
@@ -32,6 +33,7 @@ using namespace swift;
3233

3334
#define SWIFT_TOOLS_VERSION_KEY "swift-tools-version"
3435
#define SWIFT_MODULE_FLAGS_KEY "swift-module-flags"
36+
#define SWIFT_INTERFACE_DEPS_VERSION "swift-interface-deps-version-1"
3537

3638
static bool
3739
extractSwiftInterfaceVersionAndArgs(DiagnosticEngine &Diags,
@@ -100,10 +102,11 @@ std::string getCacheHash(ASTContext &Ctx,
100102
}
101103

102104
void
103-
ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPath(
105+
ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPaths(
104106
CompilerInvocation &SubInvocation,
105107
StringRef InPath,
106-
llvm::SmallString<128> &OutPath) {
108+
llvm::SmallString<128> &OutPath,
109+
llvm::SmallString<128> &DepPath) {
107110

108111
auto &SearchPathOpts = Ctx.SearchPathOpts;
109112
auto &LangOpts = Ctx.LangOpts;
@@ -125,8 +128,11 @@ ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPath(
125128
OutPath.append("-");
126129
OutPath.append(getCacheHash(Ctx, SubInvocation, InPath));
127130
OutPath.append(".");
128-
auto Ext = file_types::getExtension(file_types::TY_SwiftModuleFile);
129-
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);
130136

131137
auto &FEOpts = SubInvocation.getFrontendOptions();
132138
FEOpts.RequestedAction = FrontendOptions::ActionType::EmitModuleOnly;
@@ -137,24 +143,67 @@ ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPath(
137143
FEOpts.InputsAndOutputs.setMainAndSupplementaryOutputs({MainOut}, {SOPs});
138144
}
139145

140-
// 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.
141162
static bool
142163
swiftModuleIsUpToDate(clang::vfs::FileSystem &FS,
143-
StringRef InPath, StringRef OutPath) {
144-
if (FS.exists(OutPath)) {
145-
auto InStatus = FS.status(InPath);
146-
auto OutStatus = FS.status(OutPath);
147-
if (InStatus && OutStatus) {
148-
return InStatus.get().getLastModificationTime() <=
149-
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;
150198
}
151199
}
152-
return false;
200+
return true;
153201
}
154202

155203
static bool buildSwiftModuleFromSwiftInterface(
156204
clang::vfs::FileSystem &FS, DiagnosticEngine &Diags,
157-
CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath) {
205+
CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath,
206+
StringRef DepPath) {
158207
bool SubError = false;
159208
bool RunSuccess = llvm::CrashRecoveryContext().RunSafelyOnThread([&] {
160209

@@ -178,6 +227,7 @@ static bool buildSwiftModuleFromSwiftInterface(
178227
// module-serialization task we're trying to do here.
179228
LLVM_DEBUG(llvm::dbgs() << "Setting up instance\n");
180229
CompilerInstance SubInstance;
230+
SubInstance.createDependencyTracker(/*TrackSystemDeps=*/false);
181231
if (SubInstance.setup(SubInvocation)) {
182232
SubError = true;
183233
return;
@@ -212,6 +262,12 @@ static bool buildSwiftModuleFromSwiftInterface(
212262
});
213263
SILMod->serialize();
214264
SubError = Diags.hadAnyError();
265+
266+
if (!SubError) {
267+
SubError |= writeSwiftInterfaceDeps(
268+
Diags, SubInstance.getDependencyTracker()->getDependencies(),
269+
DepPath);
270+
}
215271
});
216272
return !RunSuccess || SubError;
217273
}
@@ -227,7 +283,7 @@ std::error_code ParseableInterfaceModuleLoader::openModuleFiles(
227283

228284
auto &FS = *Ctx.SourceMgr.getFileSystem();
229285
auto &Diags = Ctx.Diags;
230-
llvm::SmallString<128> InPath, OutPath;
286+
llvm::SmallString<128> InPath, OutPath, DepPath;
231287

232288
// First check to see if the .swiftinterface exists at all. Bail if not.
233289
InPath = DirName;
@@ -240,12 +296,12 @@ std::error_code ParseableInterfaceModuleLoader::openModuleFiles(
240296
// Set up a _potential_ sub-invocation to consume the .swiftinterface and emit
241297
// the .swiftmodule.
242298
CompilerInvocation SubInvocation;
243-
configureSubInvocationAndOutputPath(SubInvocation, InPath, OutPath);
299+
configureSubInvocationAndOutputPaths(SubInvocation, InPath, OutPath, DepPath);
244300

245301
// Evaluate if we need to run this sub-invocation, and if so run it.
246-
if (!swiftModuleIsUpToDate(FS, InPath, OutPath)) {
302+
if (!swiftModuleIsUpToDate(FS, InPath, OutPath, DepPath)) {
247303
if (buildSwiftModuleFromSwiftInterface(FS, Diags, SubInvocation, InPath,
248-
OutPath))
304+
OutPath, DepPath))
249305
return std::make_error_code(std::errc::invalid_argument);
250306
}
251307

test/ParseableInterface/client.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,32 @@
33
// RUN: %target-swift-frontend -enable-resilience -emit-parseable-module-interface-path %t/TestModule.swiftinterface -module-name TestModule %S/Inputs/other.swift -emit-module -o /dev/null
44
// RUN: test -f %t/TestModule.swiftinterface
55
// RUN: %target-swift-frontend -typecheck -enable-parseable-module-interface -module-cache-path %t/modulecache -I %t %s
6+
7+
// RUN: test ! %t/modulecache/TestModule-*.swiftmodule -ot %t/TestModule.swiftinterface
68
// RUN: llvm-bcanalyzer -dump %t/modulecache/TestModule-*.swiftmodule | %FileCheck %s -check-prefix=CHECK-SWIFTMODULE
9+
// RUN: %FileCheck %s -check-prefix=CHECK-SID <%t/modulecache/TestModule-*.sid
10+
11+
// Now add a "dependency" to the .sid file but set it to be quite old
12+
// RUN: echo %t/fake-dep >>%t/modulecache/TestModule-*.sid
13+
// RUN: touch -t 201401240005 %t/fake-dep
14+
// RUN: %FileCheck %s -check-prefix=CHECK-SID2 <%t/modulecache/TestModule-*.sid
15+
16+
// Check that the cache does not rebuild
17+
// RUN: %target-swift-frontend -typecheck -enable-parseable-module-interface -module-cache-path %t/modulecache -I %t %s
18+
// RUN: %FileCheck %s -check-prefix=CHECK-SID2 <%t/modulecache/TestModule-*.sid
19+
20+
// Now touch the dependency a ways into the future, and check that the cache _does_ rebuild
21+
// (Sorry, this test will need refreshing in the year 2035)
22+
// RUN: touch -t 203501240005 %t/fake-dep
23+
// RUN: %target-swift-frontend -typecheck -enable-parseable-module-interface -module-cache-path %t/modulecache -I %t %s
24+
// RUN: %FileCheck %s -check-prefix=CHECK-SID3 <%t/modulecache/TestModule-*.sid
725

826
// CHECK-SWIFTMODULE: {{MODULE_NAME.*blob data = 'TestModule'}}
927
// CHECK-SWIFTMODULE: FUNC_DECL
1028
// CHECK-SWIFTMODULE: RESILIENCE_STRATEGY
29+
// CHECK-SID: Swift.swiftmodule
30+
// CHECK-SID2: fake-dep
31+
// CHECK-SID3-NOT: fake-dep
1132

1233
import TestModule
1334
func foo() {

0 commit comments

Comments
 (0)