Skip to content
This repository was archived by the owner on Feb 4, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion clcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,21 @@ def getManifestHash(compilerBinary, commandLine, sourceFile):
# One of the few exceptions to this rule is the /MP switch, which only
# defines how many compiler processes are running simultaneusly.
commandLine = [arg for arg in commandLine if not arg.startswith("/MP")]
arguments, inputFiles = CommandLineAnalyzer.parseArgumentsAndInputFiles(commandLine)
collapseBasedirInCmdPath = lambda path: collapseBasedirToPlaceholder(os.path.normcase(os.path.abspath(path)))

commandLineArgs = []
projectSpecificArgs = ("AI", "I", "FU")
for k in sorted(arguments.keys()):
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you use sorted here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the sorted is very important to have repeatable manifest hashes. As dictionaries in python are unordered the only way to guarantee that we generate the same hash every invocation is to iterate over the keys in the same order. It is the same case that #235 solved.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so this basically improves the manifest cache hit rate, i.e. it's an improvement but not directly related to the BASEDIR feature? Ok.

Copy link
Copy Markdown
Contributor Author

@siu siu Nov 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is going to have that side effect but at same time it is needed for correctness since the implementation changed in this PR to use a dictionary.

if k in projectSpecificArgs:
commandLineArgs.extend(["/" + k + collapseBasedirInCmdPath(arg) for arg in arguments[k]])
else:
commandLineArgs.extend(["/" + k + arg for arg in arguments[k]])

commandLineArgs.extend(collapseBasedirInCmdPath(arg) for arg in inputFiles)

additionalData = "{}|{}|{}".format(
compilerHash, commandLine, ManifestRepository.MANIFEST_FILE_FORMAT_VERSION)
compilerHash, commandLineArgs, ManifestRepository.MANIFEST_FILE_FORMAT_VERSION)
return getFileHash(sourceFile, additionalData)

@staticmethod
Expand Down
123 changes: 94 additions & 29 deletions integrationtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

PYTHON_BINARY = sys.executable
CLCACHE_SCRIPT = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clcache.py")
ASSETS_DIR = os.path.join("tests", "integrationtests")
ASSETS_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "tests", "integrationtests")
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a suspicion, but I'm not quite sure - why did you change this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was changed only to simplify the code in TestBasedir. The cwd is changed with cd() and the relative path to the assets wouldn't work. If I rearrange the code there a bit this change is not needed.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, makes sense! 👍


# pytest-cov note: subprocesses are coverage tested by default with some limitations
# "For subprocess measurement environment variables must make it from the main process to the
Expand Down Expand Up @@ -1073,37 +1073,102 @@ def testHitsViaMpConcurrent(self):


class TestBasedir(unittest.TestCase):
def testBasedir(self):
with cd(os.path.join(ASSETS_DIR, "basedir")), tempfile.TemporaryDirectory() as tempDir:
# First, create two separate build directories with the same sources
for buildDir in ["builddir_a", "builddir_b"]:
shutil.rmtree(buildDir, ignore_errors=True)
os.mkdir(buildDir)
def setUp(self):
self.projectDir = os.path.join(ASSETS_DIR, "basedir")
self.tempDir = tempfile.TemporaryDirectory()
self.clcacheDir = os.path.join(self.tempDir.name, "clcache")
self.savedCwd = os.getcwd()

os.chdir(self.tempDir.name)

# First, create two separate build directories with the same sources
for buildDir in ["builddir_a", "builddir_b"]:
shutil.copytree(self.projectDir, buildDir)

self.cache = clcache.Cache(self.clcacheDir)

def tearDown(self):
os.chdir(self.savedCwd)
self.tempDir.cleanup()

def _runCompiler(self, cppFile, extraArgs=None):
cmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c"]
if extraArgs:
cmd.extend(extraArgs)
cmd = cmd + [cppFile]
env = dict(os.environ, CLCACHE_DIR=self.clcacheDir, CLCACHE_BASEDIR=os.getcwd())
self.assertEqual(subprocess.call(cmd, env=env), 0)

def expectHit(self, runCompiler):
# Build once in one directory
with cd("builddir_a"):
runCompiler[0]()
with self.cache.statistics as stats:
self.assertEqual(stats.numCacheMisses(), 1)
self.assertEqual(stats.numCacheHits(), 0)

shutil.copy("main.cpp", buildDir)
shutil.copy("constants.h", buildDir)
# Build again in a different directory, this should hit now because of CLCACHE_BASEDIR
with cd("builddir_b"):
runCompiler[1]()
with self.cache.statistics as stats:
self.assertEqual(stats.numCacheMisses(), 1)
self.assertEqual(stats.numCacheHits(), 1)

cache = clcache.Cache(tempDir)
def expectMiss(self, runCompiler):
# Build once in one directory
with cd("builddir_a"):
runCompiler[0]()
with self.cache.statistics as stats:
self.assertEqual(stats.numCacheMisses(), 1)
self.assertEqual(stats.numCacheHits(), 0)

# Build again in a different directory, this should hit now because of CLCACHE_BASEDIR
with cd("builddir_b"):
runCompiler[1]()
with self.cache.statistics as stats:
self.assertEqual(stats.numCacheMisses(), 2)
self.assertEqual(stats.numCacheHits(), 0)

def testBasedirRelativePaths(self):
def runCompiler():
self._runCompiler("main.cpp")
self.expectHit([runCompiler, runCompiler])

def testBasedirAbsolutePaths(self):
def runCompiler():
self._runCompiler(os.path.join(os.getcwd(), "main.cpp"))
self.expectHit([runCompiler, runCompiler])

def testBasedirIncludeArg(self):
def runCompiler():
self._runCompiler("main.cpp", ["/I{}".format(os.getcwd())])
self.expectHit([runCompiler, runCompiler])

def testBasedirIncludeSlashes(self):
runCompiler1 = lambda: self._runCompiler("main.cpp", ["/I{}/".format(os.getcwd())])
runCompiler2 = lambda: self._runCompiler("main.cpp", ["/I{}".format(os.getcwd())])
self.expectHit([runCompiler1, runCompiler2])

def testBasedirIncludeArgDifferentCapitalization(self):
def runCompiler():
self._runCompiler("main.cpp", ["/I{}".format(os.getcwd().upper())])
self.expectHit([runCompiler, runCompiler])

def testBasedirDefineArg(self):
def runCompiler():
self._runCompiler("main.cpp", ["/DRESOURCES_DIR={}".format(os.getcwd())])
self.expectMiss([runCompiler, runCompiler])

def testBasedirRelativeIncludeArg(self):
basedir = os.getcwd()

def runCompiler(cppFile="main.cpp"):
cmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c", "/I."]
cmd = cmd + [cppFile]
env = dict(os.environ, CLCACHE_DIR=self.clcacheDir, CLCACHE_BASEDIR=basedir)
self.assertEqual(subprocess.call(cmd, env=env), 0)

cmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c", "main.cpp"]

# Build once in one directory
with cd("builddir_a"):
env = dict(os.environ, CLCACHE_DIR=tempDir, CLCACHE_BASEDIR=os.getcwd())
self.assertEqual(subprocess.call(cmd, env=env), 0)
with cache.statistics as stats:
self.assertEqual(stats.numCacheMisses(), 1)
self.assertEqual(stats.numCacheHits(), 0)

shutil.rmtree("builddir_a", ignore_errors=True)

# Build again in a different directory, this should hit now because of CLCACHE_BASEDIR
with cd("builddir_b"):
env = dict(os.environ, CLCACHE_DIR=tempDir, CLCACHE_BASEDIR=os.getcwd())
self.assertEqual(subprocess.call(cmd, env=env), 0)
with cache.statistics as stats:
self.assertEqual(stats.numCacheMisses(), 1)
self.assertEqual(stats.numCacheHits(), 1)
self.expectMiss([runCompiler, runCompiler])


class TestCleanCache(unittest.TestCase):
Expand Down