premake/premake-4.x

vpaths matching doesn't use anchors and therefore ends up matching .clang-format with *.c or .hgignore with *.h

assarbad opened this issue · 0 comments

I noticed this because .hgignore always ended up in the ["Header Files/*"] = { "*.hpp", "*.h", }, vpaths-mapping, despite there being another mapping with which I attempted to map .hgignore without any wildcard characters. The same evidently happens/happened for .clang-format.

The path.wilcards() function returns a match string that isn't anchored to the beginning and end of the string respectively, so returning matches when it would not be expected.

I think the appropriate way would be (or rather have been) to have path.wildcards() return patterns anchored between ^ and $, i.e. the beginning and end of a string respectively. But in this particular case it's rather finicky to work around this from the outside.

The issue with that seems to be that -- for obvious reasons -- the name path.wildcards is already bound by the time I try to manipulate it.

Essentially I have replace this single line by something along the lines of (not knowing, however, how much else this may influence or how many existing premake4.lua files it'd break):

                        local wildcards = "^" .. path.wildcards(pattern) .. "$"
                        local i = abspath:find(wildcards)

... to get my proverbial foot in the door. Still, only thanks to the way Premake4 was designed, it's possible without having to patch the C part of it.

This does not happen when replacement (the key in the vpaths table) is an empty string, btw.

The workaround

Somewhere in your premake4.lua place this code:

do
    local orig_getvpath = premake.project.getvpath
    premake.project.getvpath = function(prj, abspath)
        for replacement, patterns in pairs(prj.vpaths or {}) do
            for _, pattern in ipairs(patterns) do
                if abspath == pattern then
                    local stem, _ = replacement:gsub("%*", "")
                    return path.join(stem, abspath)
                end
            end
        end
        return orig_getvpath(prj, abspath)
    end
end

What it does is to replace premake.project.getvpath() with our own code, retaining a "function pointer" to the original function.

Then it goes through all replacements and respective patterns, looking for exact matches between patterns and path -- which should reasonably only happen whenever the pattern (and path) contain no wildcard characters anyway -- and if that was found uses a stripped down version of the logic in the original getvpath() and returns its result at the first match. This is exactly the behavior I would have expected when there are no wildcard characters in the pattern, but which wasn't the case due to using string.find without anchors.


FYI: I don't expect anyone but myself to fix/address this. However, perhaps it helps someone else who still uses Premake4 or raises awareness on that issue for Premake5 (I don't know how related the code bases truly are).


Here's one failed attempt:

do
    if premake.project.getvpath then
        local orig_getvpath = premake.project.getvpath
        premake.project.getvpath = function(prj, abspath)
            local orig_wildcards = path.wildcards
            -- just for a single call, fix up the wildcards() function
            path.wildcards = function(pattern)
                return "^" .. orig_wildcards(pattern) .. ""
            end
            local retval = orig_getvpath(prj, abspath)
            path.wildcards = orig_wildcards -- reset back to the original value
            return retval
        end
    end
end

This method also fails (filteridgroup and getfilegroup respectively use the premake.project.eachfile function, which I hoped to influence this way):

do
    local orig_wildcards = path.wildcards
    local orig_filteridgroup = premake.vstudio.vc2010.filteridgroup
    local orig_getfilegroup = premake.vstudio.vc2010.getfilegroup
    local replacement_wildcards = function(pattern)
        return "^" .. orig_wildcards(pattern) .. ""
    end
    premake.vstudio.vc2010.filteridgroup = function(prj)
        path.wildcards = replacement_wildcards -- just for a single call, fix up the wildcards() function
        local retval = orig_filteridgroup(prj)
        path.wildcards = orig_wildcards -- reset back to the original value
        return retval
    end
    premake.vstudio.vc2010.getfilegroup = function(prj, group)
        path.wildcards = replacement_wildcards -- just for a single call, fix up the wildcards() function
        local retval = orig_getfilegroup(prj, group)
        path.wildcards = orig_wildcards -- reset back to the original value
        return retval
    end
end