Ejemplo n.º 1
0
    def testClearPostcondition(self):
        cache = clcache.Cache()

        # Compile a random file to populate cache
        cmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c", os.path.join(ASSETS_DIR, "fibonacci.cpp")]
        subprocess.check_call(cmd)

        # Now there should be something in the cache
        with cache.statistics as stats:
            self.assertTrue(stats.currentCacheSize() > 0)
            self.assertTrue(stats.numCacheEntries() > 0)

        # Now, clear the cache: the stats should remain unchanged except for
        # the cache size and number of cache entries.
        oldStats = copy.copy(cache.statistics)
        self._clearCache()
        with cache.statistics as stats:
            self.assertEqual(stats.currentCacheSize(), 0)
            self.assertEqual(stats.numCacheEntries(), 0)
            self.assertEqual(stats.numCallsWithoutSourceFile(), oldStats.numCallsWithoutSourceFile())
            self.assertEqual(stats.numCallsWithMultipleSourceFiles(), oldStats.numCallsWithMultipleSourceFiles())
            self.assertEqual(stats.numCallsWithPch(), oldStats.numCallsWithPch())
            self.assertEqual(stats.numCallsForLinking(), oldStats.numCallsForLinking())
            self.assertEqual(stats.numCallsForPreprocessing(), oldStats.numCallsForPreprocessing())
            self.assertEqual(stats.numCallsForExternalDebugInfo(), oldStats.numCallsForExternalDebugInfo())
            self.assertEqual(stats.numEvictedMisses(), oldStats.numEvictedMisses())
            self.assertEqual(stats.numHeaderChangedMisses(), oldStats.numHeaderChangedMisses())
            self.assertEqual(stats.numSourceChangedMisses(), oldStats.numSourceChangedMisses())
            self.assertEqual(stats.numCacheHits(), oldStats.numCacheHits())
            self.assertEqual(stats.numCacheMisses(), oldStats.numCacheMisses())
Ejemplo n.º 2
0
    def testHitsViaMpConcurrent(self):
        with cd(os.path.join(ASSETS_DIR, "parallel")), tempfile.TemporaryDirectory() as tempDir:
            cache = clcache.Cache(tempDir)

            customEnv = self._createEnv(tempDir)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 0)
                self.assertEqual(stats.numCacheEntries(), 0)

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

            # Compile two random files
            subprocess.check_call(cmd + ["fibonacci01.cpp"], env=customEnv)
            subprocess.check_call(cmd + ["fibonacci02.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 2)
                self.assertEqual(stats.numCacheEntries(), 2)

            # Compile same two files concurrently, this should hit twice.
            subprocess.check_call(cmd + ["/MP2", "fibonacci01.cpp", "fibonacci02.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 2)
                self.assertEqual(stats.numCacheMisses(), 2)
                self.assertEqual(stats.numCacheEntries(), 2)
Ejemplo n.º 3
0
    def testHitViaMpSequential(self):
        with cd(os.path.join(ASSETS_DIR, "parallel")), tempfile.TemporaryDirectory() as tempDir:
            cache = clcache.Cache(tempDir)

            customEnv = self._createEnv(tempDir)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 0)
                self.assertEqual(stats.numCacheEntries(), 0)

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

            # Compile random file, filling cache
            subprocess.check_call(cmd + ["fibonacci01.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 1)
                self.assertEqual(stats.numCacheEntries(), 1)

            # Compile same files with specifying /MP, this should hit
            subprocess.check_call(cmd + ["/MP", "fibonacci01.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 1)
                self.assertEqual(stats.numCacheMisses(), 1)
                self.assertEqual(stats.numCacheEntries(), 1)
Ejemplo n.º 4
0
    def testAlternatingIncludeOrder(self):
        with cd(os.path.join(ASSETS_DIR, "hits-and-misses")), tempfile.TemporaryDirectory() as tempDir:
            cache = clcache.Cache(tempDir)
            customEnv = dict(os.environ, CLCACHE_DIR=tempDir)
            baseCmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c"]

            with open('A.h', 'w') as header:
                header.write('#define A 1\n')
            with open('B.h', 'w') as header:
                header.write('#define B 1\n')

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 0)
                self.assertEqual(stats.numCacheEntries(), 0)

            # VERSION 1
            with open('stable-source-with-alternating-header.h', 'w') as f:
                f.write('#include "A.h"\n')
                f.write('#include "B.h"\n')
            subprocess.check_call(baseCmd + ["stable-source-with-alternating-header.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 1)
                self.assertEqual(stats.numCacheEntries(), 1)

            # VERSION 2
            with open('stable-source-with-alternating-header.h', 'w') as f:
                f.write('#include "B.h"\n')
                f.write('#include "A.h"\n')
            subprocess.check_call(baseCmd + ["stable-source-with-alternating-header.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 2)
                self.assertEqual(stats.numCacheEntries(), 2)

            # VERSION 1 again
            with open('stable-source-with-alternating-header.h', 'w') as f:
                f.write('#include "A.h"\n')
                f.write('#include "B.h"\n')
            subprocess.check_call(baseCmd + ["stable-source-with-alternating-header.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 1)
                self.assertEqual(stats.numCacheMisses(), 2)
                self.assertEqual(stats.numCacheEntries(), 2)

            # VERSION 2 again
            with open('stable-source-with-alternating-header.h', 'w') as f:
                f.write('#include "B.h"\n')
                f.write('#include "A.h"\n')
            subprocess.check_call(baseCmd + ["stable-source-with-alternating-header.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 2)
                self.assertEqual(stats.numCacheMisses(), 2)
                self.assertEqual(stats.numCacheEntries(), 2)
Ejemplo n.º 5
0
    def testPreprocessorFailure(self):
        cache = clcache.Cache()
        oldStats = copy.copy(cache.statistics)

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

        self.assertNotEqual(subprocess.call(cmd, env=self.env), 0)
        self.assertEqual(cache.statistics, oldStats)
Ejemplo n.º 6
0
    def testOutput(self):
        with cd(os.path.join(ASSETS_DIR, "parallel")), tempfile.TemporaryDirectory() as tempDir:
            sources = glob.glob("*.cpp")
            clcache.Cache(tempDir)
            customEnv = self._createEnv(tempDir)
            cmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c"]
            mpFlag = "/MP" + str(len(sources))
            out = subprocess.check_output(cmd + [mpFlag] + sources, env=customEnv).decode("ascii")

            for s in sources:
                self.assertEqual(out.count(s), 1)
Ejemplo n.º 7
0
    def testParallel(self):
        with cd(os.path.join(ASSETS_DIR, "parallel")):
            self._zeroStats()

            # Compile first time
            self._buildAll()

            cache = clcache.Cache()
            with cache.statistics as stats:
                hits = stats.numCacheHits()
                misses = stats.numCacheMisses()
            self.assertEqual(hits + misses, 10)

            # Compile second time
            self._buildAll()

            cache = clcache.Cache()
            with cache.statistics as stats:
                hits = stats.numCacheHits()
                misses = stats.numCacheMisses()
            self.assertEqual(hits + misses, 20)
Ejemplo n.º 8
0
    def testHitsSimple(self):
        with cd(os.path.join(ASSETS_DIR, "hits-and-misses")):
            cmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c", 'hit.cpp']
            subprocess.check_call(cmd) # Ensure it has been compiled before

            cache = clcache.Cache()
            with cache.statistics as stats:
                oldHits = stats.numCacheHits()
            subprocess.check_call(cmd) # This must hit now
            with cache.statistics as stats:
                newHits = stats.numCacheHits()
            self.assertEqual(newHits, oldHits + 1)
Ejemplo n.º 9
0
    def testEvictedManifest(self):
        with cd(os.path.join(ASSETS_DIR, "hits-and-misses")), tempfile.TemporaryDirectory() as tempDir:
            customEnv = dict(os.environ, CLCACHE_DIR=tempDir)
            cmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c", 'hit.cpp']

            # Compile once to insert the object in the cache
            subprocess.check_call(cmd, env=customEnv)

            # Remove manifest
            cache = clcache.Cache(tempDir)
            clcache.clearCache(cache)

            self.assertEqual(subprocess.call(cmd, env=customEnv), 0)
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
0
    def testHit(self):
        with cd(os.path.join(ASSETS_DIR, "hits-and-misses")):
            cmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c", "hit.cpp"]

            self.assertEqual(subprocess.call(cmd, env=self.env), 0)

            cache = clcache.Cache()
            with cache.statistics as stats:
                oldHits = stats.numCacheHits()

            self.assertEqual(subprocess.call(cmd, env=self.env), 0) # This should hit now
            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), oldHits + 1)
Ejemplo n.º 12
0
    def testClearIdempotency(self):
        cache = clcache.Cache()

        self._clearCache()
        with cache.statistics as stats:
            self.assertEqual(stats.currentCacheSize(), 0)
            self.assertEqual(stats.numCacheEntries(), 0)

        # Clearing should be idempotent
        self._clearCache()
        with cache.statistics as stats:
            self.assertEqual(stats.currentCacheSize(), 0)
            self.assertEqual(stats.numCacheEntries(), 0)
Ejemplo n.º 13
0
    def testRemovedTransitiveHeader(self):
        with cd(os.path.join(ASSETS_DIR, "hits-and-misses")), tempfile.TemporaryDirectory() as tempDir:
            cache = clcache.Cache(tempDir)
            customEnv = dict(os.environ, CLCACHE_DIR=tempDir)
            baseCmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c"]

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 0)
                self.assertEqual(stats.numCacheEntries(), 0)

            # VERSION 1
            with open('alternating-header.h', 'w') as f:
                f.write("#define VERSION 1\n")
            subprocess.check_call(baseCmd + ["stable-source-transitive-header.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 1)
                self.assertEqual(stats.numCacheEntries(), 1)

            # Remove header, trigger the compiler which should fail
            os.remove('alternating-header.h')
            with self.assertRaises(subprocess.CalledProcessError):
                subprocess.check_call(baseCmd + ["stable-source-transitive-header.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 2)
                self.assertEqual(stats.numCacheEntries(), 1)

            # VERSION 1 again
            with open('alternating-header.h', 'w') as f:
                f.write("#define VERSION 1\n")
            subprocess.check_call(baseCmd + ["stable-source-transitive-header.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 1)
                self.assertEqual(stats.numCacheMisses(), 2)
                self.assertEqual(stats.numCacheEntries(), 1)

            # Remove header again, trigger the compiler which should fail
            os.remove('alternating-header.h')
            with self.assertRaises(subprocess.CalledProcessError):
                subprocess.check_call(baseCmd + ["stable-source-transitive-header.cpp"], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 1)
                self.assertEqual(stats.numCacheMisses(), 3)
                self.assertEqual(stats.numCacheEntries(), 1)
Ejemplo n.º 14
0
    def testObsoleteHeaderDisappears(self):
        # A includes B
        with cd(os.path.join(ASSETS_DIR, "header-miss-obsolete")), tempfile.TemporaryDirectory() as tempDir:
            customEnv = dict(os.environ, CLCACHE_DIR=tempDir)
            compileCmd = CLCACHE_CMD + ["/I.", "/nologo", "/EHsc", "/c", "main.cpp"]
            cache = clcache.Cache(tempDir)

            with open("A.h", "w") as header:
                header.write('#define INFO 1337\n')
                header.write('#include "B.h"\n')
            with open("B.h", "w") as header:
                header.write('#define SOMETHING 1\n')

            subprocess.check_call(compileCmd, env=customEnv)

            with cache.statistics as stats:
                headerChangedMisses1 = stats.numHeaderChangedMisses()
                hits1 = stats.numCacheHits()
                misses1 = stats.numCacheMisses()

            # Make include B.h obsolete
            with open("A.h", "w") as header:
                header.write('#define INFO 1337\n')
                header.write('\n')
            os.remove("B.h")

            subprocess.check_call(compileCmd, env=customEnv)

            with cache.statistics as stats:
                headerChangedMisses2 = stats.numHeaderChangedMisses()
                hits2 = stats.numCacheHits()
                misses2 = stats.numCacheMisses()

            self.assertEqual(headerChangedMisses2, headerChangedMisses1+1)
            self.assertEqual(misses2, misses1+1)
            self.assertEqual(hits2, hits1)

            # Ensure the new manifest was stored
            subprocess.check_call(compileCmd, env=customEnv)

            with cache.statistics as stats:
                headerChangedMisses3 = stats.numHeaderChangedMisses()
                hits3 = stats.numCacheHits()
                misses3 = stats.numCacheMisses()

            self.assertEqual(headerChangedMisses3, headerChangedMisses2)
            self.assertEqual(misses3, misses2)
            self.assertEqual(hits3, hits2+1)
Ejemplo n.º 15
0
    def testHitsSimple(self):
        invocations = [
            ["/nologo", "/E"],
            ["/nologo", "/EP", "/c"],
            ["/nologo", "/P", "/c"],
            ["/nologo", "/E", "/EP"],
        ]

        cache = clcache.Cache()
        with cache.statistics as stats:
            oldPreprocessorCalls = stats.numCallsForPreprocessing()

        for i, invocation in enumerate(invocations, 1):
            cmd = CLCACHE_CMD + invocation + [os.path.join(ASSETS_DIR, "minimal.cpp")]
            subprocess.check_call(cmd)
            with cache.statistics as stats:
                newPreprocessorCalls = stats.numCallsForPreprocessing()
            self.assertEqual(newPreprocessorCalls, oldPreprocessorCalls + i, str(cmd))
Ejemplo n.º 16
0
    def testFive(self):
        with cd(os.path.join(ASSETS_DIR, "mutiple-sources")), tempfile.TemporaryDirectory() as tempDir:
            cache = clcache.Cache(tempDir)
            customEnv = dict(os.environ, CLCACHE_DIR=tempDir)
            baseCmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c"]

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 0)
                self.assertEqual(stats.numCacheEntries(), 0)

            subprocess.check_call(baseCmd + [
                "fibonacci01.cpp",
                "fibonacci02.cpp",
                "fibonacci03.cpp",
                "fibonacci04.cpp",
                "fibonacci05.cpp",
            ], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 5)
                self.assertEqual(stats.numCacheEntries(), 5)

            subprocess.check_call(baseCmd + [
                "fibonacci01.cpp",
                "fibonacci02.cpp",
                "fibonacci03.cpp",
                "fibonacci04.cpp",
                "fibonacci05.cpp",
            ], env=customEnv)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 5)
                self.assertEqual(stats.numCacheMisses(), 5)
                self.assertEqual(stats.numCacheEntries(), 5)
Ejemplo n.º 17
0
    def testConcurrentHitsScaling(self):
        with tempfile.TemporaryDirectory() as tempDir:
            customEnv = dict(os.environ, CLCACHE_DIR=tempDir)

            cache = clcache.Cache(tempDir)

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(), 0)
                self.assertEqual(stats.numCacheEntries(), 0)

            # Populate cache
            cmd = CLCACHE_CMD + ['/nologo', '/EHsc', '/c'
                                 ] + TestConcurrency.sources
            coldCacheSequential = takeTime(
                lambda: subprocess.check_call(cmd, env=customEnv))

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(), 0)
                self.assertEqual(stats.numCacheMisses(),
                                 len(TestConcurrency.sources))
                self.assertEqual(stats.numCacheEntries(),
                                 len(TestConcurrency.sources))

            # Compile one-by-one, measuring the time.
            cmd = CLCACHE_CMD + ['/nologo', '/EHsc', '/c'
                                 ] + TestConcurrency.sources
            hotCacheSequential = takeTime(
                lambda: subprocess.check_call(cmd, env=customEnv))

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(),
                                 len(TestConcurrency.sources))
                self.assertEqual(stats.numCacheMisses(),
                                 len(TestConcurrency.sources))
                self.assertEqual(stats.numCacheEntries(),
                                 len(TestConcurrency.sources))

            # Recompile with many concurrent processes, measuring time
            cmd = CLCACHE_CMD + [
                '/nologo', '/EHsc', '/c', '/MP{}'.format(cpu_count())
            ] + TestConcurrency.sources
            hotCacheConcurrent = takeTime(
                lambda: subprocess.check_call(cmd, env=customEnv))

            with cache.statistics as stats:
                self.assertEqual(stats.numCacheHits(),
                                 len(TestConcurrency.sources) * 2)
                self.assertEqual(stats.numCacheMisses(),
                                 len(TestConcurrency.sources))
                self.assertEqual(stats.numCacheEntries(),
                                 len(TestConcurrency.sources))

            print(
                "Compiling {} source files sequentially, cold cache: {} seconds"
                .format(len(TestConcurrency.sources), coldCacheSequential))
            print(
                "Compiling {} source files sequentially, hot cache: {} seconds"
                .format(len(TestConcurrency.sources), hotCacheSequential))
            print(
                "Compiling {} source files concurrently via /MP{}, hot cache: {} seconds"
                .format(len(TestConcurrency.sources), cpu_count(),
                        hotCacheConcurrent))