From 5142230999c596fefe7b3654971a933904b99684 Mon Sep 17 00:00:00 2001 From: Ben Ratzlaff Date: Sun, 8 Sep 2019 23:49:32 -0700 Subject: [PATCH] Account for filename collisions on systems with case-insensitive file systems --- modules/gmake2/tests/test_gmake2_objects.lua | 65 +++++++++++++++++ modules/vstudio/tests/vc2010/test_files.lua | 76 ++++++++++++++++++++ src/base/oven.lua | 40 ++++++++--- 3 files changed, 170 insertions(+), 11 deletions(-) diff --git a/modules/gmake2/tests/test_gmake2_objects.lua b/modules/gmake2/tests/test_gmake2_objects.lua index 6918953593..317df6f244 100644 --- a/modules/gmake2/tests/test_gmake2_objects.lua +++ b/modules/gmake2/tests/test_gmake2_objects.lua @@ -156,6 +156,71 @@ OBJECTS += $(OBJDIR)/hello111.o else ifeq ($(config),release) OBJECTS += $(OBJDIR)/hello1.o +else + $(error "invalid configuration $(config)") +endif + + ]] + end + +-- +-- Test that changes in case are treated as if multiple files of the same name are being built +-- + + function suite.uniqueObjNames_ignoreCase1() + files { "a/hello.cpp", "b/Hello.cpp" } + prepare() + test.capture [[ +# File sets +# ############################################# + +OBJECTS := + +OBJECTS += $(OBJDIR)/Hello1.o +OBJECTS += $(OBJDIR)/hello.o + + ]] + end + + function suite.uniqueObjNames_ignoreCase2() + files { "a/hello.cpp", "b/hello.cpp", "c/Hello1.cpp" } + prepare() + test.capture [[ +# File sets +# ############################################# + +OBJECTS := + +OBJECTS += $(OBJDIR)/Hello11.o +OBJECTS += $(OBJDIR)/hello.o +OBJECTS += $(OBJDIR)/hello1.o + + ]] + end + + function suite.uniqueObjectNames_ignoreCase_Release() + files { "a/hello.cpp", "b/hello.cpp", "c/Hello1.cpp", "d/Hello11.cpp" } + filter "configurations:Debug" + excludes {"b/hello.cpp"} + filter "configurations:Release" + excludes {"d/Hello11.cpp"} + + prepare() + test.capture [[ +# File sets +# ############################################# + +OBJECTS := + +OBJECTS += $(OBJDIR)/Hello11.o +OBJECTS += $(OBJDIR)/hello.o + +ifeq ($(config),debug) +OBJECTS += $(OBJDIR)/Hello111.o + +else ifeq ($(config),release) +OBJECTS += $(OBJDIR)/hello1.o + else $(error "invalid configuration $(config)") endif diff --git a/modules/vstudio/tests/vc2010/test_files.lua b/modules/vstudio/tests/vc2010/test_files.lua index 0e7cc855e1..2f16624e3e 100644 --- a/modules/vstudio/tests/vc2010/test_files.lua +++ b/modules/vstudio/tests/vc2010/test_files.lua @@ -479,6 +479,82 @@ ]] end +-- +-- Test that changes in case are treated as if multiple files of the same name are being built +-- + + function suite.uniqueObjectNames_onSourceNameCollision_ignoreCase() + files { "hello.cpp", "greetings/Hello.cpp" } + prepare() + test.capture [[ + + + + $(IntDir)\hello1.obj + + + ]] + end + + + function suite.uniqueObjectNames_onBaseNameCollision_ignoreCase1() + files { "a/hello.cpp", "b/Hello.cpp", "c/hello1.cpp" } + prepare() + test.capture [[ + + + + $(IntDir)\Hello1.obj + + + $(IntDir)\hello11.obj + + + ]] + end + + + function suite.uniqueObjectNames_onBaseNameCollision_ignoreCase2() + files { "a/hello1.cpp", "b/Hello.cpp", "c/hello.cpp" } + prepare() + test.capture [[ + + + + + $(IntDir)\hello2.obj + + + ]] + end + + + function suite.uniqueObjectNames_onBaseNameCollision_Release_ignoreCase() + files { "a/Hello.cpp", "b/hello.cpp", "c/hello1.cpp", "d/hello11.cpp" } + filter "configurations:Debug" + excludes {"b/hello.cpp"} + filter "configurations:Release" + excludes {"d/hello11.cpp"} + + prepare() + test.capture [[ + + + + true + $(IntDir)\hello1.obj + + + $(IntDir)\hello11.obj + + + true + + + ]] + end + + -- -- Check handling of per-file forced includes. diff --git a/src/base/oven.lua b/src/base/oven.lua index 559b4b519b..75c2b43b35 100644 --- a/src/base/oven.lua +++ b/src/base/oven.lua @@ -695,23 +695,40 @@ -- conflicting object file names - hello1.o function oven.uniqueSequence(f, cfg, seq, bases) - while true do + local good_sequence = true + repeat + -- be optimistic + good_sequence = true f.sequence = seq[cfg] or 0 seq[cfg] = f.sequence + 1 - if seq[cfg] == 1 then + if f.sequence == 0 then + -- first time seeing this objname break end - if not bases[f.objname] then - bases[f.objname] = {} + -- getting here has changed our sequence number, but this new "basename" + -- may still collide with files that actually end with this "sequence number" + -- so we have to check the bases table now + + -- objname changes with the sequence number on every loop + local lowerobj = f.objname:lower() + if not bases[lowerobj] then + -- this is the first appearance of a file that produces this objname + -- intialize the table for any future basename that matches our objname + bases[lowerobj] = {} end - if not bases[f.objname][cfg] then - bases[f.objname][cfg] = 1 - break + if not bases[lowerobj][cfg] then + -- not a collision + -- start a sequence for a future basename that matches our objname for this cfg + bases[lowerobj][cfg] = 1 + good_sequence = true + else + -- is truly a collision, try the next sequence number + good_sequence = false end - end + until good_sequence end @@ -733,11 +750,12 @@ -- collisions that have occurred for each project configuration. Use -- this collision count to generate the unique object file names. - if not bases[file.basename] then - bases[file.basename] = {} + local lowerbase = file.basename:lower() + if not bases[lowerbase] then + bases[lowerbase] = {} end - local sequences = bases[file.basename] + local sequences = bases[lowerbase] for cfg in p.project.eachconfig(prj) do local fcfg = p.fileconfig.getconfig(file, cfg)