Clang: Improve updating of PCHs

Instead of deleting the path and time stamp of the project PCH we always
set the time stamp but delete only the path if PCH creation failed. A
failed PCH will be no rebuilt if the files changed. We have to add that
feature later.

Change-Id: I1094271f9ead5d906e94b68ac91c0becd2371ca9
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2019-07-24 16:23:08 +02:00
parent 2ae4c35a65
commit cc78969bdb
10 changed files with 61 additions and 52 deletions

View File

@@ -32,6 +32,7 @@
#include "includesearchpath.h" #include "includesearchpath.h"
#include "projectpartartefact.h" #include "projectpartartefact.h"
#include "projectpartid.h" #include "projectpartid.h"
#include "sourceentry.h"
#include <utils/cpplanguage_details.h> #include <utils/cpplanguage_details.h>
#include <utils/smallstringio.h> #include <utils/smallstringio.h>
@@ -148,7 +149,7 @@ public:
first.language, first.language,
first.languageVersion, first.languageVersion,
first.languageExtension, first.languageExtension,
first.hasPrecompiledHeader) first.preCompiledHeaderWasGenerated)
< std::tie(second.projectPartId, < std::tie(second.projectPartId,
second.toolChainArguments, second.toolChainArguments,
second.compilerMacros, second.compilerMacros,
@@ -159,7 +160,7 @@ public:
second.language, second.language,
second.languageVersion, second.languageVersion,
second.languageExtension, second.languageExtension,
second.hasPrecompiledHeader); second.preCompiledHeaderWasGenerated);
} }
ProjectPartContainer clone() const ProjectPartContainer clone() const
@@ -171,7 +172,7 @@ public:
FilePathIds headerPathIds; FilePathIds headerPathIds;
FilePathIds sourcePathIds; FilePathIds sourcePathIds;
bool updateIsDeferred = false; bool updateIsDeferred = false;
bool hasPrecompiledHeader = true; bool preCompiledHeaderWasGenerated = true;
}; };
using ProjectPartContainerReference = std::reference_wrapper<ProjectPartContainer>; using ProjectPartContainerReference = std::reference_wrapper<ProjectPartContainer>;

View File

@@ -74,12 +74,12 @@ public:
.template values<FilePathId>(1024, projectPartId.projectPathId); .template values<FilePathId>(1024, projectPartId.projectPathId);
} }
bool hasPrecompiledHeader(ProjectPartId projectPartId) const bool preCompiledHeaderWasGenerated(ProjectPartId projectPartId) const
{ {
auto value = fetchProjectPrecompiledHeaderPathStatement.template value<Utils::SmallString>( auto value = fetchProjectPrecompiledHeaderBuildTimeStatement.template value<long long>(
projectPartId.projectPathId); projectPartId.projectPathId);
return value && value->hasContent(); return value && *value > 0;
} }
ProjectPartContainers fetchProjectParts(const ProjectPartIds &projectPartIds) const override ProjectPartContainers fetchProjectParts(const ProjectPartIds &projectPartIds) const override
@@ -96,7 +96,7 @@ public:
if (value) { if (value) {
value->headerPathIds = fetchHeaders(projectPartId); value->headerPathIds = fetchHeaders(projectPartId);
value->sourcePathIds = fetchSources(projectPartId); value->sourcePathIds = fetchSources(projectPartId);
value->hasPrecompiledHeader = hasPrecompiledHeader(projectPartId); value->preCompiledHeaderWasGenerated = preCompiledHeaderWasGenerated(projectPartId);
projectParts.push_back(*std::move(value)); projectParts.push_back(*std::move(value));
} }
} }
@@ -379,13 +379,9 @@ public:
mutable ReadStatement fetchProjectPartsSourcesByIdStatement{ mutable ReadStatement fetchProjectPartsSourcesByIdStatement{
"SELECT sourceId FROM projectPartsSources WHERE projectPartId = ? ORDER BY sourceId", "SELECT sourceId FROM projectPartsSources WHERE projectPartId = ? ORDER BY sourceId",
database}; database};
mutable ReadStatement fetchProjectPrecompiledHeaderPathStatement{ mutable ReadStatement fetchProjectPrecompiledHeaderBuildTimeStatement{
"SELECT projectPchPath FROM precompiledHeaders WHERE projectPartId = ?", database}; "SELECT projectPchBuildTime FROM precompiledHeaders WHERE projectPartId = ?", database};
WriteStatement resetDependentIndexingTimeStampsStatement{ WriteStatement resetDependentIndexingTimeStampsStatement{
"WITH RECURSIVE collectedDependencies(sourceId) AS (VALUES(?) UNION SELECT " "UPDATE fileStatuses SET indexingTimeStamp = NULL WHERE sourceId = ?", database};
"dependencySourceId FROM sourceDependencies, collectedDependencies WHERE "
"sourceDependencies.sourceId == collectedDependencies.sourceId) UPDATE fileStatuses SET "
"indexingTimeStamp = NULL WHERE sourceId IN (SELECT sourceId FROM collectedDependencies)",
database};
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -153,7 +153,8 @@ std::vector<PchTaskQueue::Task> PchTaskQueue::createProjectTasks(PchTasks &&pchT
pchCreator.generatePch(std::move(pchTask)); pchCreator.generatePch(std::move(pchTask));
const auto &projectPartPch = pchCreator.projectPartPch(); const auto &projectPartPch = pchCreator.projectPartPch();
if (projectPartPch.pchPath.empty()) { if (projectPartPch.pchPath.empty()) {
m_precompiledHeaderStorage.deleteProjectPrecompiledHeader(projectPartId); m_precompiledHeaderStorage.deleteProjectPrecompiledHeader(projectPartId,
projectPartPch.lastModified);
} else { } else {
m_precompiledHeaderStorage.insertProjectPrecompiledHeader( m_precompiledHeaderStorage.insertProjectPrecompiledHeader(
projectPartId, projectPartPch.pchPath, projectPartPch.lastModified); projectPartId, projectPartPch.pchPath, projectPartPch.lastModified);

View File

@@ -65,16 +65,17 @@ public:
} }
} }
void deleteProjectPrecompiledHeader(ProjectPartId projectPartId) override void deleteProjectPrecompiledHeader(ProjectPartId projectPartId, long long pchBuildTime) override
{ {
try { try {
Sqlite::ImmediateTransaction transaction{database}; Sqlite::ImmediateTransaction transaction{database};
deleteProjectPrecompiledHeaderStatement.write(projectPartId.projectPathId); deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement.write(projectPartId.projectPathId,
pchBuildTime);
transaction.commit(); transaction.commit();
} catch (const Sqlite::StatementIsBusy) { } catch (const Sqlite::StatementIsBusy) {
deleteProjectPrecompiledHeader(projectPartId); deleteProjectPrecompiledHeader(projectPartId, pchBuildTime);
} }
} }
@@ -198,6 +199,10 @@ public:
"UPDATE OR IGNORE precompiledHeaders SET projectPchPath=NULL,projectPchBuildTime=NULL " "UPDATE OR IGNORE precompiledHeaders SET projectPchPath=NULL,projectPchBuildTime=NULL "
"WHERE projectPartId = ?", "WHERE projectPartId = ?",
database}; database};
WriteStatement deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement{
"UPDATE OR IGNORE precompiledHeaders SET projectPchPath=NULL,projectPchBuildTime=?002 "
"WHERE projectPartId = ?001",
database};
WriteStatement deleteSystemPrecompiledHeaderStatement{ WriteStatement deleteSystemPrecompiledHeaderStatement{
"UPDATE OR IGNORE precompiledHeaders SET systemPchPath=NULL,systemPchBuildTime=NULL " "UPDATE OR IGNORE precompiledHeaders SET systemPchPath=NULL,systemPchBuildTime=NULL "
"WHERE projectPartId = ?", "WHERE projectPartId = ?",

View File

@@ -47,7 +47,7 @@ public:
Utils::SmallStringView pchPath, Utils::SmallStringView pchPath,
long long pchBuildTime) long long pchBuildTime)
= 0; = 0;
virtual void deleteProjectPrecompiledHeader(ProjectPartId projectPartId) = 0; virtual void deleteProjectPrecompiledHeader(ProjectPartId projectPartId, long long pchBuildTime) = 0;
virtual void deleteProjectPrecompiledHeaders(const ProjectPartIds &projectPartIds) = 0; virtual void deleteProjectPrecompiledHeaders(const ProjectPartIds &projectPartIds) = 0;
virtual void insertSystemPrecompiledHeaders(const ProjectPartIds &projectPartIds, virtual void insertSystemPrecompiledHeaders(const ProjectPartIds &projectPartIds,
Utils::SmallStringView pchPath, Utils::SmallStringView pchPath,

View File

@@ -36,7 +36,8 @@ public:
void(ClangBackEnd::ProjectPartId projectPartId, void(ClangBackEnd::ProjectPartId projectPartId,
Utils::SmallStringView pchPath, Utils::SmallStringView pchPath,
long long pchBuildTime)); long long pchBuildTime));
MOCK_METHOD1(deleteProjectPrecompiledHeader, void(ClangBackEnd::ProjectPartId projectPartId)); MOCK_METHOD2(deleteProjectPrecompiledHeader,
void(ClangBackEnd::ProjectPartId projectPartId, long long buildTime));
MOCK_METHOD1(deleteProjectPrecompiledHeaders, MOCK_METHOD1(deleteProjectPrecompiledHeaders,
void(const ClangBackEnd::ProjectPartIds &projectPartIds)); void(const ClangBackEnd::ProjectPartIds &projectPartIds));
MOCK_METHOD3(insertSystemPrecompiledHeaders, MOCK_METHOD3(insertSystemPrecompiledHeaders,

View File

@@ -343,7 +343,7 @@ TEST_F(PchTaskQueue, DeleteProjectPchEntryInDatabaseIfNoPchIsGenerated)
{ {
InSequence s; InSequence s;
MockPchCreator mockPchCreator; MockPchCreator mockPchCreator;
ClangBackEnd::ProjectPartPch projectPartPch{{}, "", 0}; ClangBackEnd::ProjectPartPch projectPartPch{{}, "", 34};
auto tasks = queue.createProjectTasks({projectTask1}); auto tasks = queue.createProjectTasks({projectTask1});
auto projectTask = projectTask1; auto projectTask = projectTask1;
projectTask.systemPchPath = "/path/to/pch"; projectTask.systemPchPath = "/path/to/pch";
@@ -353,7 +353,7 @@ TEST_F(PchTaskQueue, DeleteProjectPchEntryInDatabaseIfNoPchIsGenerated)
.WillOnce(Return(ClangBackEnd::FilePath{"/path/to/pch"})); .WillOnce(Return(ClangBackEnd::FilePath{"/path/to/pch"}));
EXPECT_CALL(mockPchCreator, generatePch(Eq(projectTask))); EXPECT_CALL(mockPchCreator, generatePch(Eq(projectTask)));
EXPECT_CALL(mockPchCreator, projectPartPch()).WillOnce(ReturnRef(projectPartPch)); EXPECT_CALL(mockPchCreator, projectPartPch()).WillOnce(ReturnRef(projectPartPch));
EXPECT_CALL(mockPrecompiledHeaderStorage, deleteProjectPrecompiledHeader(Eq(1))); EXPECT_CALL(mockPrecompiledHeaderStorage, deleteProjectPrecompiledHeader(Eq(1), Eq(34)));
tasks.front()(mockPchCreator); tasks.front()(mockPchCreator);
} }

View File

@@ -44,6 +44,8 @@ protected:
Storage storage{database}; Storage storage{database};
MockSqliteWriteStatement &insertProjectPrecompiledHeaderStatement = storage.insertProjectPrecompiledHeaderStatement; MockSqliteWriteStatement &insertProjectPrecompiledHeaderStatement = storage.insertProjectPrecompiledHeaderStatement;
MockSqliteWriteStatement &deleteProjectPrecompiledHeaderStatement = storage.deleteProjectPrecompiledHeaderStatement; MockSqliteWriteStatement &deleteProjectPrecompiledHeaderStatement = storage.deleteProjectPrecompiledHeaderStatement;
MockSqliteWriteStatement &deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement
= storage.deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement;
MockSqliteWriteStatement &insertSystemPrecompiledHeaderStatement = storage.insertSystemPrecompiledHeaderStatement; MockSqliteWriteStatement &insertSystemPrecompiledHeaderStatement = storage.insertSystemPrecompiledHeaderStatement;
MockSqliteWriteStatement &deleteSystemPrecompiledHeaderStatement = storage.deleteSystemPrecompiledHeaderStatement; MockSqliteWriteStatement &deleteSystemPrecompiledHeaderStatement = storage.deleteSystemPrecompiledHeaderStatement;
MockSqliteReadStatement &fetchSystemPrecompiledHeaderPathStatement = storage.fetchSystemPrecompiledHeaderPathStatement; MockSqliteReadStatement &fetchSystemPrecompiledHeaderPathStatement = storage.fetchSystemPrecompiledHeaderPathStatement;
@@ -95,10 +97,11 @@ TEST_F(PrecompiledHeaderStorage, DeleteProjectPrecompiledHeader)
InSequence s; InSequence s;
EXPECT_CALL(database, immediateBegin()); EXPECT_CALL(database, immediateBegin());
EXPECT_CALL(deleteProjectPrecompiledHeaderStatement, write(TypedEq<int>(1))); EXPECT_CALL(deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement,
write(TypedEq<int>(1), TypedEq<long long>(13)));
EXPECT_CALL(database, commit()); EXPECT_CALL(database, commit());
storage.deleteProjectPrecompiledHeader(1); storage.deleteProjectPrecompiledHeader(1, 13);
} }
TEST_F(PrecompiledHeaderStorage, DeleteProjectPrecompiledHeaderStatementIsBusy) TEST_F(PrecompiledHeaderStorage, DeleteProjectPrecompiledHeaderStatementIsBusy)
@@ -107,10 +110,11 @@ TEST_F(PrecompiledHeaderStorage, DeleteProjectPrecompiledHeaderStatementIsBusy)
EXPECT_CALL(database, immediateBegin()).WillOnce(Throw(Sqlite::StatementIsBusy("busy"))); EXPECT_CALL(database, immediateBegin()).WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
EXPECT_CALL(database, immediateBegin()); EXPECT_CALL(database, immediateBegin());
EXPECT_CALL(deleteProjectPrecompiledHeaderStatement, write(TypedEq<int>(1))); EXPECT_CALL(deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement,
write(TypedEq<int>(1), TypedEq<long long>(13)));
EXPECT_CALL(database, commit()); EXPECT_CALL(database, commit());
storage.deleteProjectPrecompiledHeader(1); storage.deleteProjectPrecompiledHeader(1, 13);
} }
TEST_F(PrecompiledHeaderStorage, DeleteProjectPrecompiledHeaders) TEST_F(PrecompiledHeaderStorage, DeleteProjectPrecompiledHeaders)

View File

@@ -43,7 +43,7 @@ class ProjectPartsManager : public testing::Test
protected: protected:
ProjectPartsManager() ProjectPartsManager()
{ {
projectPartContainerWithoutPrecompiledHeader1.hasPrecompiledHeader = false; projectPartContainerWithoutPrecompiledHeader1.preCompiledHeaderWasGenerated = false;
} }
NiceMock<MockProjectPartsStorage> mockProjectPartsStorage; NiceMock<MockProjectPartsStorage> mockProjectPartsStorage;
NiceMock<MockPrecompiledHeaderStorage> mockPrecompiledHeaderStorage; NiceMock<MockPrecompiledHeaderStorage> mockPrecompiledHeaderStorage;

View File

@@ -104,7 +104,8 @@ protected:
MockSqliteWriteStatement &insertProjectPartsSourcesStatement = storage.insertProjectPartsSourcesStatement; MockSqliteWriteStatement &insertProjectPartsSourcesStatement = storage.insertProjectPartsSourcesStatement;
MockSqliteReadStatement &fetchProjectPartsHeadersByIdStatement = storage.fetchProjectPartsHeadersByIdStatement; MockSqliteReadStatement &fetchProjectPartsHeadersByIdStatement = storage.fetchProjectPartsHeadersByIdStatement;
MockSqliteReadStatement &fetchProjectPartsSourcesByIdStatement = storage.fetchProjectPartsSourcesByIdStatement; MockSqliteReadStatement &fetchProjectPartsSourcesByIdStatement = storage.fetchProjectPartsSourcesByIdStatement;
MockSqliteReadStatement &fetchProjectPrecompiledHeaderPathStatement = storage.fetchProjectPrecompiledHeaderPathStatement; MockSqliteReadStatement &fetchProjectPrecompiledHeaderPathStatement = storage.fetchProjectPrecompiledHeaderBuildTimeStatement;
MockSqliteReadStatement &fetchProjectPrecompiledHeaderBuildTimeStatement = storage.fetchProjectPrecompiledHeaderBuildTimeStatement;
MockSqliteWriteStatement &resetDependentIndexingTimeStampsStatement = storage.resetDependentIndexingTimeStampsStatement; MockSqliteWriteStatement &resetDependentIndexingTimeStampsStatement = storage.resetDependentIndexingTimeStampsStatement;
IncludeSearchPaths systemIncludeSearchPaths{{"/includes", 1, IncludeSearchPathType::BuiltIn}, IncludeSearchPaths systemIncludeSearchPaths{{"/includes", 1, IncludeSearchPathType::BuiltIn},
{"/other/includes", 2, IncludeSearchPathType::System}}; {"/other/includes", 2, IncludeSearchPathType::System}};
@@ -257,11 +258,11 @@ TEST_F(ProjectPartsStorage, FetchProjectPartsByIds)
EXPECT_CALL(fetchProjectPartByIdStatement, valueReturnProjectPartContainer(Eq(1))); EXPECT_CALL(fetchProjectPartByIdStatement, valueReturnProjectPartContainer(Eq(1)));
EXPECT_CALL(fetchProjectPartsHeadersByIdStatement, valuesReturnFilePathIds(1024, Eq(1))); EXPECT_CALL(fetchProjectPartsHeadersByIdStatement, valuesReturnFilePathIds(1024, Eq(1)));
EXPECT_CALL(fetchProjectPartsSourcesByIdStatement, valuesReturnFilePathIds(1024, Eq(1))); EXPECT_CALL(fetchProjectPartsSourcesByIdStatement, valuesReturnFilePathIds(1024, Eq(1)));
EXPECT_CALL(fetchProjectPrecompiledHeaderPathStatement, valueReturnSmallString(Eq(1))); EXPECT_CALL(fetchProjectPrecompiledHeaderBuildTimeStatement, valueReturnInt64(Eq(1)));
EXPECT_CALL(fetchProjectPartByIdStatement, valueReturnProjectPartContainer(Eq(2))); EXPECT_CALL(fetchProjectPartByIdStatement, valueReturnProjectPartContainer(Eq(2)));
EXPECT_CALL(fetchProjectPartsHeadersByIdStatement, valuesReturnFilePathIds(1024, Eq(2))); EXPECT_CALL(fetchProjectPartsHeadersByIdStatement, valuesReturnFilePathIds(1024, Eq(2)));
EXPECT_CALL(fetchProjectPartsSourcesByIdStatement, valuesReturnFilePathIds(1024, Eq(2))); EXPECT_CALL(fetchProjectPartsSourcesByIdStatement, valuesReturnFilePathIds(1024, Eq(2)));
EXPECT_CALL(fetchProjectPrecompiledHeaderPathStatement, valueReturnSmallString(Eq(2))); EXPECT_CALL(fetchProjectPrecompiledHeaderBuildTimeStatement, valueReturnInt64(Eq(2)));
EXPECT_CALL(mockDatabase, commit()); EXPECT_CALL(mockDatabase, commit());
storage.fetchProjectParts({1, 2}); storage.fetchProjectParts({1, 2});
@@ -284,34 +285,34 @@ TEST_F(ProjectPartsStorage, FetchProjectPartsByIdsIsBusy)
storage.fetchProjectParts({1, 2}); storage.fetchProjectParts({1, 2});
} }
TEST_F(ProjectPartsStorage, FetchProjectPartsByIdsHasPrecompiledNullOptional) TEST_F(ProjectPartsStorage, FetchProjectPartsByIdsPreCompiledHeaderWasGeneratedNullOptional)
{ {
ON_CALL(fetchProjectPrecompiledHeaderPathStatement, valueReturnSmallString(Eq(1))) ON_CALL(fetchProjectPrecompiledHeaderBuildTimeStatement, valueReturnInt64(Eq(1)))
.WillByDefault(Return(Utils::optional<Utils::SmallString>{})); .WillByDefault(Return(Utils::optional<long long>{}));
auto projectParts = storage.fetchProjectParts({1}); auto projectParts = storage.fetchProjectParts({1});
ASSERT_FALSE(projectParts.front().hasPrecompiledHeader); ASSERT_FALSE(projectParts.front().preCompiledHeaderWasGenerated);
} }
TEST_F(ProjectPartsStorage, FetchProjectPartsByIdsHasPrecompiledEmptyString) TEST_F(ProjectPartsStorage, FetchProjectPartsByIdsPreCompiledHeaderWasGeneratedZero)
{ {
ON_CALL(fetchProjectPrecompiledHeaderPathStatement, valueReturnSmallString(Eq(1))) ON_CALL(fetchProjectPrecompiledHeaderBuildTimeStatement, valueReturnInt64(Eq(1)))
.WillByDefault(Return(Utils::optional<Utils::SmallString>{""})); .WillByDefault(Return(Utils::optional<long long>{0}));
auto projectParts = storage.fetchProjectParts({1}); auto projectParts = storage.fetchProjectParts({1});
ASSERT_FALSE(projectParts.front().hasPrecompiledHeader); ASSERT_FALSE(projectParts.front().preCompiledHeaderWasGenerated);
} }
TEST_F(ProjectPartsStorage, FetchProjectPartsByIdsHasPrecompiledStringWithContent) TEST_F(ProjectPartsStorage, FetchProjectPartsByIdsPreCompiledHeaderWasGeneratedSomeNumber)
{ {
ON_CALL(fetchProjectPrecompiledHeaderPathStatement, valueReturnSmallString(Eq(1))) ON_CALL(fetchProjectPrecompiledHeaderBuildTimeStatement, valueReturnInt64(Eq(1)))
.WillByDefault(Return(Utils::optional<Utils::SmallString>{"/some/path"})); .WillByDefault(Return(Utils::optional<long long>{23}));
auto projectParts = storage.fetchProjectParts({1}); auto projectParts = storage.fetchProjectParts({1});
ASSERT_TRUE(projectParts.front().hasPrecompiledHeader); ASSERT_TRUE(projectParts.front().preCompiledHeaderWasGenerated);
} }
TEST_F(ProjectPartsStorage, FetchProjectPartsByIdsHasMissingId) TEST_F(ProjectPartsStorage, FetchProjectPartsByIdsHasMissingId)
@@ -550,12 +551,12 @@ TEST_F(ProjectPartsStorageSlow, ResetDependentIndexingTimeStamps)
storage.resetIndexingTimeStamps({projectPart1, projectPart2}); storage.resetIndexingTimeStamps({projectPart1, projectPart2});
ASSERT_THAT(buildDependenciesStorage.fetchIndexingTimeStamps(), ASSERT_THAT(buildDependenciesStorage.fetchIndexingTimeStamps(),
ElementsAre(SourceTimeStamp{1, 0}, ElementsAre(SourceTimeStamp{1, 34},
SourceTimeStamp{2, 0}, SourceTimeStamp{2, 34},
SourceTimeStamp{3, 0}, SourceTimeStamp{3, 0},
SourceTimeStamp{4, 0}, SourceTimeStamp{4, 0},
SourceTimeStamp{5, 0}, SourceTimeStamp{5, 34},
SourceTimeStamp{6, 0}, SourceTimeStamp{6, 34},
SourceTimeStamp{7, 0}, SourceTimeStamp{7, 0},
SourceTimeStamp{8, 0}, SourceTimeStamp{8, 0},
SourceTimeStamp{9, 34}, SourceTimeStamp{9, 34},