Skip to content

Add os.access function for file access checking#7274

Merged
waruqi merged 16 commits intoxmake-io:devfrom
luadebug:isexec
Jan 30, 2026
Merged

Add os.access function for file access checking#7274
waruqi merged 16 commits intoxmake-io:devfrom
luadebug:isexec

Conversation

@luadebug
Copy link
Member

@luadebug luadebug commented Jan 28, 2026

It looks like os.isexec could be improved?

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @luadebug, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the xmake build system by introducing a new os.access function, which provides a robust and cross-platform mechanism for checking file permissions. This new functionality is then utilized to streamline the os.isexec function, making its implementation more concise and efficient by centralizing file access checks.

Highlights

  • New os.access function: A new C-level function xm_os_access has been added and exposed to Lua as os.access. This function provides a standardized way to check file permissions (read, write, execute) for a given path.
  • Refactored os.isexec: The existing os.isexec Lua function has been updated to leverage the new os.access function. This simplifies its logic, making it more concise and removing platform-specific checks for executable file extensions (e.g., '.exe', '.cmd', '.bat' on Windows).

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new os.access function for checking file access permissions and refactors os.isexec to use it. This is a good improvement for checking execute permissions, especially on Unix-like systems. However, the change to os.isexec introduces a regression on Windows by removing the logic that checks for executable file extensions. My review includes a suggestion to fix this regression while retaining the benefits of the new os.access function.

return true
if os.host() == "windows" then
local ext = path.extension(filepath):lower()
if ext == ".exe" or ext == ".cmd" or ext == ".bat" or ext == ".ps1" then
Copy link
Member

@waruqi waruqi Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it again, and on Windows, simply checking if the file exists should be sufficient.

Using only the file extension isn't accurate; it might overlook some executable files, such as .sh scripts on msys2, and shell scripts might not even have a file extension.

Copy link
Member

@waruqi waruqi Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current rules for filtering file extensions may break some things when built on msys2.

e.g. 7z.sh on msys2

if os.host() == "windows" then
local ext = path.extension(filepath):lower()
if ext == ".exe" or ext == ".cmd" or ext == ".bat" or ext == ".ps1" then
if ext == ".exe" or ext == ".cmd" or ext == ".bat" or ext == ".ps1" or ext == ".sh" then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simply adding .sh is not enough; many files without extensions are shell script files.

Therefore, we can only use os.isfile

Image

Copy link
Member Author

@luadebug luadebug Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Therefore, we can only use os.isfile

If we could sort of use PIPE to capture output of streams would that help to filter out files without extensions shell script files? Such as core.base.pipe.lua thing. As PE file check would not work over shell scripts but might work over binary files without extension or with wrong extension, as for Windows OS example with PE file check we can figure out arch of executable and minimum Windows OS support (Windows XP) for example.

The current rules for filtering file extensions may break some things when built on msys2. e.g. 7z.sh on msys2

That is probably same issue with shell script files without extensions I guess...

Copy link
Member

@waruqi waruqi Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try this

function os.isexec(filepath)
    assert(filepath)

    if os.host() == "windows" then
        local exts = {".exe", ".cmd", ".bat", ".ps1", ".sh"}
        if os.isfile(filepath) then
            -- detect file extension first
            local extension = path.extension(filepath):lower()
            if extension then
                if table.contains(extension, exts) then
                    return true
                end
            end

            -- detect file header
            if winos.is_pefile(filepath) then
                return true
            end

            -- detect shebang scripts
            local file = io.open(filepath, "rb")
            if file then
                local header = file:read(2)
                file:close()
                if header == "#!" then
                    return true
                end
            end
        end
        for _, suffix in ipairs(exts) do
            if os.isfile(filepath .. suffix) then
                return true
            end
        end
    elseif os.isfile(filepath) then
        return os._access(filepath, "x")
    end
    return false
end

Then we can improve the previous parsepe native interface and change it to the winos.is_pefile interface implementation.

like this

def is_pe_file(file_path):
    try:
        with open(file_path, 'rb') as f:
            # Step 1: Check the DOS header "MZ"
            mz_signature = f.read(2)
            if mz_signature != b'MZ':
                return False

            # Step 2: Seek to the PE header
            f.seek(60)  # DOS header e_lfanew offset location
            pe_offset = int.from_bytes(f.read(4), 'little')

            # Step 3: Check the PE signature "PE\0\0"
            f.seek(pe_offset)
            pe_signature = f.read(4)
            if pe_signature == b'PE\0\0':
                return True
    except Exception as e:
        print(f"Error reading file: {e}")
    return False

# Example usage
print(is_pe_file("example.exe"))

@waruqi waruqi mentioned this pull request Jan 29, 2026
@waruqi
Copy link
Member

waruqi commented Jan 29, 2026

I need to refactor binutils and add the binutils.format interface to replace is_pefile.

please wait #7278

@waruqi waruqi merged commit 0d0e9a3 into xmake-io:dev Jan 30, 2026
27 of 35 checks passed
@waruqi waruqi added this to the v3.0.7 milestone Jan 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants