Skip to content
This repository was archived by the owner on Feb 4, 2020. It is now read-only.

Commit 3ef6643

Browse files
xoviatfrerich
authored andcommitted
Fix source file duplication in command line
Previously, if the /Tp or /Tc option were passed, clcache would duplicate the source file in the command line, making MSVC think that it was compiling two separate files (/Tcexample.c and example.c), when it was really compiling one. The problem with this was that MSVC only allows some options if it is compiling one file, which caused some invocations of clcache to fail.
1 parent b880939 commit 3ef6643

File tree

3 files changed

+57
-15
lines changed

3 files changed

+57
-15
lines changed

clcache.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,7 @@ def findCompilerBinary():
10041004

10051005

10061006
def printTraceStatement(msg):
1007+
# type: (str) -> None
10071008
if "CLCACHE_LOG" in os.environ:
10081009
scriptDir = os.path.realpath(os.path.dirname(sys.argv[0]))
10091010
with OUTPUT_LOCK:
@@ -1252,15 +1253,21 @@ def parseArgumentsAndInputFiles(cmdline):
12521253

12531254
@staticmethod
12541255
def analyze(cmdline):
1256+
# type: List[str] -> Tuple[List[Tuple[str, str]], List[str]]
12551257
options, inputFiles = CommandLineAnalyzer.parseArgumentsAndInputFiles(cmdline)
1258+
# Use an override pattern to shadow input files that have
1259+
# already been specified in the function above
1260+
inputFiles = {inputFile: '' for inputFile in inputFiles}
12561261
compl = False
12571262
if 'Tp' in options:
1258-
inputFiles += options['Tp']
1263+
inputFiles.update({inputFile: '/Tp' for inputFile in options['Tp']})
12591264
compl = True
12601265
if 'Tc' in options:
1261-
inputFiles += options['Tc']
1266+
inputFiles.update({inputFile: '/Tc' for inputFile in options['Tc']})
12621267
compl = True
12631268

1269+
# Now collect the inputFiles into the return format
1270+
inputFiles = list(inputFiles.items())
12641271
if not inputFiles:
12651272
raise NoSourceFileError()
12661273

@@ -1293,7 +1300,7 @@ def analyze(cmdline):
12931300
objectFiles = [tmp]
12941301
if objectFiles is None:
12951302
# Generate from .c/.cpp filenames
1296-
objectFiles = [os.path.join(prefix, basenameWithoutExtension(f)) + '.obj' for f in inputFiles]
1303+
objectFiles = [os.path.join(prefix, basenameWithoutExtension(f)) + '.obj' for f, _ in inputFiles]
12971304

12981305
printTraceStatement("Compiler source files: {}".format(inputFiles))
12991306
printTraceStatement("Compiler object file: {}".format(objectFiles))
@@ -1617,19 +1624,26 @@ def processCompileRequest(cache, compiler, args):
16171624
printOutAndErr(out, err)
16181625
return exitCode
16191626

1627+
def filterSourceFiles(cmdLine, sourceFiles):
1628+
# type: (List[str], List[Tuple[str, str]]) -> Iterator[str]
1629+
setOfSources = set(sourceFile for sourceFile, _ in sourceFiles)
1630+
skippedArgs = ('/Tc', '/Tp', '-Tp', '-Tc')
1631+
yield from (
1632+
arg for arg in cmdLine
1633+
if not (arg in setOfSources or arg.startswith(skippedArgs))
1634+
)
1635+
16201636
def scheduleJobs(cache, compiler, cmdLine, environment, sourceFiles, objectFiles):
1621-
baseCmdLine = []
1622-
setOfSources = set(sourceFiles)
1623-
for arg in cmdLine:
1624-
if not (arg in setOfSources or arg.startswith("/MP")):
1625-
baseCmdLine.append(arg)
1637+
# type: (Any, str, List[str], Any, List[Tuple[str, str]], List[str]) -> int
1638+
# Filter out all source files from the command line to form baseCmdLine
1639+
baseCmdLine = [arg for arg in filterSourceFiles(cmdLine, sourceFiles) if not arg.startswith('/MP')]
16261640

16271641
exitCode = 0
16281642
cleanupRequired = False
16291643
with concurrent.futures.ThreadPoolExecutor(max_workers=jobCount(cmdLine)) as executor:
16301644
jobs = []
1631-
for srcFile, objFile in zip(sourceFiles, objectFiles):
1632-
jobCmdLine = baseCmdLine + [srcFile]
1645+
for (srcFile, srcLanguage), objFile in zip(sourceFiles, objectFiles):
1646+
jobCmdLine = baseCmdLine + [srcLanguage + srcFile]
16331647
jobs.append(executor.submit(
16341648
processSingleSource,
16351649
compiler, jobCmdLine, srcFile, objFile, environment))

integrationtests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,13 +806,16 @@ def testHitsViaMpConcurrent(self):
806806
self.assertEqual(stats.numCacheEntries(), 2)
807807

808808
def testOutput(self):
809+
# type: () -> None
809810
with cd(os.path.join(ASSETS_DIR, "parallel")), tempfile.TemporaryDirectory() as tempDir:
810811
sources = glob.glob("*.cpp")
811812
clcache.Cache(tempDir)
812813
customEnv = self._createEnv(tempDir)
813814
cmd = CLCACHE_CMD + ["/nologo", "/EHsc", "/c"]
814815
mpFlag = "/MP" + str(len(sources))
815816
out = subprocess.check_output(cmd + [mpFlag] + sources, env=customEnv).decode("ascii")
817+
# print the output so that it shows up in py.test
818+
print(out)
816819

817820
for s in sources:
818821
self.assertEqual(out.count(s), 1)

unittests.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,30 @@ def testNestedResponseFiles(self):
488488
)
489489

490490

491+
class TestFilterSourceFiles(unittest.TestCase):
492+
def _assertFiltered(self, cmdLine, files, filteredCmdLine):
493+
# type: (List[str], List[Tuple[str, str]]) -> List[str]
494+
files = clcache.filterSourceFiles(cmdLine, files)
495+
self.assertEqual(list(files), filteredCmdLine)
496+
497+
def testFilterSourceFiles(self):
498+
self._assertFiltered(
499+
['/c', '/EP', '/FoSome.obj', 'main.cpp'], [('main.cpp', '')],
500+
['/c', '/EP', '/FoSome.obj'])
501+
self._assertFiltered(
502+
['/c', '/EP', '/FoSome.obj', 'main.cpp'], [('main.cpp', '/Tp')],
503+
['/c', '/EP', '/FoSome.obj'])
504+
self._assertFiltered(
505+
['/c', '/EP', '/FoSome.obj', 'main.cpp'], [('main.cpp', '/Tc')],
506+
['/c', '/EP', '/FoSome.obj'])
507+
self._assertFiltered(
508+
['/c', '/EP', '/FoSome.obj', '/Tcmain.cpp'], [('main.cpp', '')],
509+
['/c', '/EP', '/FoSome.obj'])
510+
self._assertFiltered(
511+
['/c', '/EP', '/FoSome.obj', '/Tcmain.cpp'], [('main.cpp', '-Tc')],
512+
['/c', '/EP', '/FoSome.obj'])
513+
514+
491515
class TestAnalyzeCommandLine(unittest.TestCase):
492516
def _testSourceFilesOk(self, cmdLine):
493517
try:
@@ -504,13 +528,14 @@ def _testFailure(self, cmdLine, expectedExceptionClass):
504528
self.assertRaises(expectedExceptionClass, lambda: CommandLineAnalyzer.analyze(cmdLine))
505529

506530
def _testFull(self, cmdLine, expectedSourceFiles, expectedOutputFile):
531+
# type: (List[str], List[Tuple[str, str]], List[str]) -> None
507532
sourceFiles, outputFile = CommandLineAnalyzer.analyze(cmdLine)
508533
self.assertEqual(sourceFiles, expectedSourceFiles)
509534
self.assertEqual(outputFile, expectedOutputFile)
510535

511536
def _testFo(self, foArgument, expectedObjectFilepath):
512537
self._testFull(['/c', foArgument, 'main.cpp'],
513-
["main.cpp"], [expectedObjectFilepath])
538+
[("main.cpp", '')], [expectedObjectFilepath])
514539

515540
def _testFi(self, fiArgument):
516541
self._testPreprocessingOutfile(['/c', '/P', fiArgument, 'main.cpp'])
@@ -528,7 +553,7 @@ def testEmpty(self):
528553
self._testFailure([], NoSourceFileError)
529554

530555
def testSimple(self):
531-
self._testFull(["/c", "main.cpp"], ["main.cpp"], ["main.obj"])
556+
self._testFull(["/c", "main.cpp"], [("main.cpp", "")], ["main.obj"])
532557

533558
def testNoSource(self):
534559
# No source file has priority over other errors, for consistency
@@ -547,7 +572,7 @@ def testNoSource(self):
547572
def testOutputFileFromSourcefile(self):
548573
# For object file
549574
self._testFull(['/c', 'main.cpp'],
550-
['main.cpp'], ['main.obj'])
575+
[('main.cpp', '')], ['main.obj'])
551576
# For preprocessor file
552577
self._testFailure(['/c', '/P', 'main.cpp'], CalledForPreprocessingError)
553578

@@ -634,9 +659,9 @@ def testPreprocessingFi(self):
634659
def testTpTcSimple(self):
635660
# clcache can handle /Tc or /Tp as long as there is only one of them
636661
self._testFull(['/c', '/TcMyCcProgram.c'],
637-
['MyCcProgram.c'], ['MyCcProgram.obj'])
662+
[('MyCcProgram.c', '/Tc')], ['MyCcProgram.obj'])
638663
self._testFull(['/c', '/TpMyCxxProgram.cpp'],
639-
['MyCxxProgram.cpp'], ['MyCxxProgram.obj'])
664+
[('MyCxxProgram.cpp', '/Tp')], ['MyCxxProgram.obj'])
640665

641666
def testLink(self):
642667
self._testFailure(["main.cpp"], CalledForLinkError)

0 commit comments

Comments
 (0)