premake-core icon indicating copy to clipboard operation
premake-core copied to clipboard

os.reparse(path) or/and path.getfinal(path, recursive) to solve symbolic files or directories

Open vlmillet opened this issue 4 years ago • 2 comments

ex:

os.reparse(path)

or/and

path.getfinal(path, /*recursive = */true)

What problem will this solve? It will ensure paths are unified when source files are located into symbolic directories (at least on Windows). In my case source files are different from generated PDB which makes Visual Studio to open multiple version of the same file as itself doesn't solve that symbolic link.

What might be a solution? On windows you can use internally something like this : (code not tested as is but inspired from the code I use myself in my library to solve reparsing)

    HANDLE hFile;
    std::string finalPath;
    char lnk[256];
    memset(lnk, 0, 256);

    for (auto part : path_parts) // every part separated by / or \\
    {
        finalPath = finalPath.empty() ? part : finalPath + '\\' + part;
        DWORD dwAttrib = GetFileAttributesA(finalPath.c_str());
        if ((dwAttrib == INVALID_FILE_ATTRIBUTES))
            return originalPath;
        if (((dwAttrib & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT))
        {
            // reparse point :
            hFile = CreateFileA(finalPath .c_str(),     // file to open
                                GENERIC_READ,    // open for reading
                                FILE_SHARE_READ, // share for reading
                                NULL,            // default security
                                OPEN_EXISTING,   // existing file only
                                (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ? FILE_FLAG_BACKUP_SEMANTICS
                                                                      : FILE_ATTRIBUTE_NORMAL, // normal file
                                NULL);

            if (hFile == INVALID_HANDLE_VALUE)
                return originalPath;
            GetFinalPathNameByHandleA(hFile, lnk, 256, VOLUME_NAME_DOS);
            ::CloseHandle(hFile);

            if (*lnk)
            {
                finalPath = std::string(lnk + 4); // reparse adds extra unwanted chars
            }
        }
    }

What other alternatives have you already considered? Tried everything existing (I expected realpath to do the job, but it does not)

Anything else we should know? That would save me some very hard headaches inside Visual Studio ! And it will make your API more complete on the path side !

(You can now support Premake on our OpenCollective. Your contributions help us spend more time responding to requests like these!)

vlmillet avatar Jun 15 '21 19:06 vlmillet

I think your first inclination was correct: this should be handled by os.realpath(), rather than adding an entirely new API. Any chance you can integrate this fix with the existing os.realpath() code?

starkos avatar Jun 21 '21 17:06 starkos

Here's an untested implementation I wrote up.

char* os_realpath_windows(char* result, const char* path)
{
	char pathCopy[PATH_MAX];
	strcpy(pathCopy, path);

	HANDLE hFile;
	memset(result, 0, PATH_MAX);

	char lnk[PATH_MAX];
	memset(lnk, 0, PATH_MAX);

	char* token = strtok(pathCopy, "/\\");
	while (token != NULL) {
		if (*result != '\0')
		{
			strcat(result, "\\");
			strcat(result, token);
		}

		DWORD dwAttrib = GetFileAttributesA(result);
        if ((dwAttrib == INVALID_FILE_ATTRIBUTES))
		{
			return NULL;
		}

		if (((dwAttrib & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT))
        {
            // reparse point :
            hFile = CreateFileA(result,     // file to open
                                GENERIC_READ,    // open for reading
                                FILE_SHARE_READ, // share for reading
                                NULL,            // default security
                                OPEN_EXISTING,   // existing file only
                                (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) ? FILE_FLAG_BACKUP_SEMANTICS
                                                                      : FILE_ATTRIBUTE_NORMAL, // normal file
                                NULL);

            if (hFile == INVALID_HANDLE_VALUE)
                return NULL;
            GetFinalPathNameByHandleA(hFile, lnk, PATH_MAX, VOLUME_NAME_DOS);
            CloseHandle(hFile);

            if (*lnk)
            {
				strcpy(result, lnk + 4);
            }
        }

		// get next token
		token = strtok(NULL, "/\\");
	}

	return result;
}

@vlmillet Would you be willing to see if that works for your case? Just replace _realpath in os_realpath.c with that function.

nickclark2016 avatar Jun 24 '21 03:06 nickclark2016