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 "projectpartartefact.h"
#include "projectpartid.h"
#include "sourceentry.h"
#include <utils/cpplanguage_details.h>
#include <utils/smallstringio.h>
@@ -126,12 +127,12 @@ public:
friend bool operator==(const ProjectPartContainer &first, const ProjectPartContainer &second)
{
return first.projectPartId == second.projectPartId
&& first.toolChainArguments == second.toolChainArguments
&& first.compilerMacros == second.compilerMacros
&& first.systemIncludeSearchPaths == second.systemIncludeSearchPaths
&& first.projectIncludeSearchPaths == second.projectIncludeSearchPaths
&& first.headerPathIds == second.headerPathIds
&& first.sourcePathIds == second.sourcePathIds&& first.language == second.language
&& first.toolChainArguments == second.toolChainArguments
&& first.compilerMacros == second.compilerMacros
&& first.systemIncludeSearchPaths == second.systemIncludeSearchPaths
&& first.projectIncludeSearchPaths == second.projectIncludeSearchPaths
&& first.headerPathIds == second.headerPathIds
&& first.sourcePathIds == second.sourcePathIds && first.language == second.language
&& first.languageVersion == second.languageVersion
&& first.languageExtension == second.languageExtension;
}
@@ -148,7 +149,7 @@ public:
first.language,
first.languageVersion,
first.languageExtension,
first.hasPrecompiledHeader)
first.preCompiledHeaderWasGenerated)
< std::tie(second.projectPartId,
second.toolChainArguments,
second.compilerMacros,
@@ -159,7 +160,7 @@ public:
second.language,
second.languageVersion,
second.languageExtension,
second.hasPrecompiledHeader);
second.preCompiledHeaderWasGenerated);
}
ProjectPartContainer clone() const
@@ -171,7 +172,7 @@ public:
FilePathIds headerPathIds;
FilePathIds sourcePathIds;
bool updateIsDeferred = false;
bool hasPrecompiledHeader = true;
bool preCompiledHeaderWasGenerated = true;
};
using ProjectPartContainerReference = std::reference_wrapper<ProjectPartContainer>;

View File

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

View File

@@ -153,7 +153,8 @@ std::vector<PchTaskQueue::Task> PchTaskQueue::createProjectTasks(PchTasks &&pchT
pchCreator.generatePch(std::move(pchTask));
const auto &projectPartPch = pchCreator.projectPartPch();
if (projectPartPch.pchPath.empty()) {
m_precompiledHeaderStorage.deleteProjectPrecompiledHeader(projectPartId);
m_precompiledHeaderStorage.deleteProjectPrecompiledHeader(projectPartId,
projectPartPch.lastModified);
} else {
m_precompiledHeaderStorage.insertProjectPrecompiledHeader(
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 {
Sqlite::ImmediateTransaction transaction{database};
deleteProjectPrecompiledHeaderStatement.write(projectPartId.projectPathId);
deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement.write(projectPartId.projectPathId,
pchBuildTime);
transaction.commit();
} catch (const Sqlite::StatementIsBusy) {
deleteProjectPrecompiledHeader(projectPartId);
deleteProjectPrecompiledHeader(projectPartId, pchBuildTime);
}
}
@@ -198,6 +199,10 @@ public:
"UPDATE OR IGNORE precompiledHeaders SET projectPchPath=NULL,projectPchBuildTime=NULL "
"WHERE projectPartId = ?",
database};
WriteStatement deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement{
"UPDATE OR IGNORE precompiledHeaders SET projectPchPath=NULL,projectPchBuildTime=?002 "
"WHERE projectPartId = ?001",
database};
WriteStatement deleteSystemPrecompiledHeaderStatement{
"UPDATE OR IGNORE precompiledHeaders SET systemPchPath=NULL,systemPchBuildTime=NULL "
"WHERE projectPartId = ?",

View File

@@ -47,7 +47,7 @@ public:
Utils::SmallStringView pchPath,
long long pchBuildTime)
= 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 insertSystemPrecompiledHeaders(const ProjectPartIds &projectPartIds,
Utils::SmallStringView pchPath,

View File

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

View File

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

View File

@@ -44,6 +44,8 @@ protected:
Storage storage{database};
MockSqliteWriteStatement &insertProjectPrecompiledHeaderStatement = storage.insertProjectPrecompiledHeaderStatement;
MockSqliteWriteStatement &deleteProjectPrecompiledHeaderStatement = storage.deleteProjectPrecompiledHeaderStatement;
MockSqliteWriteStatement &deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement
= storage.deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement;
MockSqliteWriteStatement &insertSystemPrecompiledHeaderStatement = storage.insertSystemPrecompiledHeaderStatement;
MockSqliteWriteStatement &deleteSystemPrecompiledHeaderStatement = storage.deleteSystemPrecompiledHeaderStatement;
MockSqliteReadStatement &fetchSystemPrecompiledHeaderPathStatement = storage.fetchSystemPrecompiledHeaderPathStatement;
@@ -95,10 +97,11 @@ TEST_F(PrecompiledHeaderStorage, DeleteProjectPrecompiledHeader)
InSequence s;
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());
storage.deleteProjectPrecompiledHeader(1);
storage.deleteProjectPrecompiledHeader(1, 13);
}
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());
EXPECT_CALL(deleteProjectPrecompiledHeaderStatement, write(TypedEq<int>(1)));
EXPECT_CALL(deleteProjectPrecompiledHeaderPathAndSetBuildTimeStatement,
write(TypedEq<int>(1), TypedEq<long long>(13)));
EXPECT_CALL(database, commit());
storage.deleteProjectPrecompiledHeader(1);
storage.deleteProjectPrecompiledHeader(1, 13);
}
TEST_F(PrecompiledHeaderStorage, DeleteProjectPrecompiledHeaders)

View File

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

View File

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