Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify the Integration of Arduino-cli into external tools : Interactive Mode Flag #2839

Open
3 tasks done
mink99 opened this issue Feb 19, 2025 · 1 comment
Open
3 tasks done
Labels
topic: CLI Related to the command line interface topic: code Related to content of the project itself type: enhancement Proposed improvement

Comments

@mink99
Copy link

mink99 commented Feb 19, 2025

Describe the request

I am working on an editor / ide for arduino development. I am using Arduino-CLI for the various actions like compile, upload, queries etc.
This is done by shelling out the Arduino-cli for each command with the according command line parameters and capturing the JSON output. The response then runs into a json-parser which feeds the provided results into the application.

As this is done on every request, there is a huge overhead both in code and resource usage.
To simplify this, i would recommend adding an "interactive" flag to the Arduino-CLI that the CLI will

  • Read the parameters for each call not from the command line but from stdin
  • not terminate after providing the response

This way the CLI could be started only once, when the editor is starting up, and the communication between the editor and the CLI would be a simple stdin - stdout communication, avoiding creating a new instance of the CLI for every request, and of course getting rid of them after.

The application is currently Windows only, so the following code examples are like that, but the concepts behind would be useful also for other platforms.
The "shelling out" is done like that:

int shExecA(const string &sRunPath, const string &cmd,const string &param,string &result,string &err)
{

    fs::path pRun = fs::current_path();

    if (sRunPath.length()> 0 )
    {
        pRun = fs::path(sRunPath);
        fs::current_path(pRun);
    }
    string pipeFileName = tmpnam(nullptr);
    string errFileName = tmpnam(nullptr);


    string commandLine = cmd + " " + param ;


    SECURITY_ATTRIBUTES sa = internal::createSecurityAttributes();

    HANDLE hout = internal::createFileW(pipeFileName, &sa);
    HANDLE herr = internal::createFileW(errFileName, &sa);

    PROCESS_INFORMATION pi = internal::createProcessInformation();
    STARTUPINFOA si = internal::createStartupInfo (NULL,hout,herr );

    BOOL ret = FALSE;


    ret = internal::createProcess (commandLine,&si,&pi);
    int applicationExitCode = 0;
    if ( ret )
    {
        WaitForSingleObject( pi.hProcess, INFINITE );
        applicationExitCode = internal::endProcess(&si,&pi);
        string resultLine;
        ifstream file(pipeFileName, ios::in );
        if (file)
        {
            while (!file.eof())
            {
                getline(file,resultLine);
                if (resultLine.length())
                {
                    result += "\n";
                    result += resultLine;
                }
            }
            file.close();
            fs::remove(pipeFileName);
        }
        else
        {
            cerr << "File (pipe)"<< pipeFileName << " could not be opened!\n"; // Report error
            cerr << "Error code: " << strerror(errno); // Get some info as to why
            return errno;
        }
        ifstream file2(errFileName, ios::in );
        if (file2)
        {
            while (!file2.eof())
            {
                getline(file2,resultLine);
                if (resultLine.length())
                {
                    err += "\n";
                    err += resultLine;
                }
            }
            file2.close();
            fs::remove(errFileName);
        }
        else
        {
            cerr << "File (err)"<< errFileName << "could not be opened!\n"; // Report error
            cerr << "Error code: " << strerror(errno); // Get some info as to why
            return errno;
        }
    }
    else
    {
        cerr << "ShExec fail on CreateProcessA :" << ret << " in " << sRunPath << endl;
        return ret;
    }
    if (applicationExitCode)
    {
        cerr << "ShExec process \"" << commandLine<< "\" fail with ExitCode :" << applicationExitCode << "\n" <<err << "\nPath: " << sRunPath << endl;
    }
    return applicationExitCode;
}

Of course this could be implemented on GRPC but there are other issues including the missing support for gcc and massive development effort for the integration.

Describe the current behavior

A new instance of the Arduino CLI has to be created for each request.

Arduino CLI version

all

Operating system

Windows

Operating system version

10 , 11

Additional context

No response

Issue checklist

  • I searched for previous requests in the issue tracker
  • I verified the feature was still missing when using the nightly build
  • My request contains all necessary details
@mink99 mink99 added the type: enhancement Proposed improvement label Feb 19, 2025
@per1234 per1234 added topic: code Related to content of the project itself topic: CLI Related to the command line interface labels Feb 19, 2025
@mink99
Copy link
Author

mink99 commented Feb 21, 2025

Just one addition: Many tools use a double \n as End of Response...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: CLI Related to the command line interface topic: code Related to content of the project itself type: enhancement Proposed improvement
Projects
None yet
Development

No branches or pull requests

2 participants